1*6236dae4SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*6236dae4SAndroid Build Coastguard Worker# -*- coding: utf-8 -*- 3*6236dae4SAndroid Build Coastguard Worker#*************************************************************************** 4*6236dae4SAndroid Build Coastguard Worker# _ _ ____ _ 5*6236dae4SAndroid Build Coastguard Worker# Project ___| | | | _ \| | 6*6236dae4SAndroid Build Coastguard Worker# / __| | | | |_) | | 7*6236dae4SAndroid Build Coastguard Worker# | (__| |_| | _ <| |___ 8*6236dae4SAndroid Build Coastguard Worker# \___|\___/|_| \_\_____| 9*6236dae4SAndroid Build Coastguard Worker# 10*6236dae4SAndroid Build Coastguard Worker# Copyright (C) Daniel Stenberg, <[email protected]>, et al. 11*6236dae4SAndroid Build Coastguard Worker# 12*6236dae4SAndroid Build Coastguard Worker# This software is licensed as described in the file COPYING, which 13*6236dae4SAndroid Build Coastguard Worker# you should have received as part of this distribution. The terms 14*6236dae4SAndroid Build Coastguard Worker# are also available at https://curl.se/docs/copyright.html. 15*6236dae4SAndroid Build Coastguard Worker# 16*6236dae4SAndroid Build Coastguard Worker# You may opt to use, copy, modify, merge, publish, distribute and/or sell 17*6236dae4SAndroid Build Coastguard Worker# copies of the Software, and permit persons to whom the Software is 18*6236dae4SAndroid Build Coastguard Worker# furnished to do so, under the terms of the COPYING file. 19*6236dae4SAndroid Build Coastguard Worker# 20*6236dae4SAndroid Build Coastguard Worker# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 21*6236dae4SAndroid Build Coastguard Worker# KIND, either express or implied. 22*6236dae4SAndroid Build Coastguard Worker# 23*6236dae4SAndroid Build Coastguard Worker# SPDX-License-Identifier: curl 24*6236dae4SAndroid Build Coastguard Worker# 25*6236dae4SAndroid Build Coastguard Worker########################################################################### 26*6236dae4SAndroid Build Coastguard Worker# 27*6236dae4SAndroid Build Coastguard Workerimport difflib 28*6236dae4SAndroid Build Coastguard Workerimport filecmp 29*6236dae4SAndroid Build Coastguard Workerimport logging 30*6236dae4SAndroid Build Coastguard Workerimport math 31*6236dae4SAndroid Build Coastguard Workerimport os 32*6236dae4SAndroid Build Coastguard Workerimport re 33*6236dae4SAndroid Build Coastguard Workerfrom datetime import timedelta 34*6236dae4SAndroid Build Coastguard Workerimport pytest 35*6236dae4SAndroid Build Coastguard Worker 36*6236dae4SAndroid Build Coastguard Workerfrom testenv import Env, CurlClient, LocalClient 37*6236dae4SAndroid Build Coastguard Worker 38*6236dae4SAndroid Build Coastguard Worker 39*6236dae4SAndroid Build Coastguard Workerlog = logging.getLogger(__name__) 40*6236dae4SAndroid Build Coastguard Worker 41*6236dae4SAndroid Build Coastguard Worker 42*6236dae4SAndroid Build Coastguard Workerclass TestDownload: 43*6236dae4SAndroid Build Coastguard Worker 44*6236dae4SAndroid Build Coastguard Worker @pytest.fixture(autouse=True, scope='class') 45*6236dae4SAndroid Build Coastguard Worker def _class_scope(self, env, httpd, nghttpx): 46*6236dae4SAndroid Build Coastguard Worker if env.have_h3(): 47*6236dae4SAndroid Build Coastguard Worker nghttpx.start_if_needed() 48*6236dae4SAndroid Build Coastguard Worker httpd.clear_extra_configs() 49*6236dae4SAndroid Build Coastguard Worker httpd.reload() 50*6236dae4SAndroid Build Coastguard Worker 51*6236dae4SAndroid Build Coastguard Worker @pytest.fixture(autouse=True, scope='class') 52*6236dae4SAndroid Build Coastguard Worker def _class_scope(self, env, httpd): 53*6236dae4SAndroid Build Coastguard Worker indir = httpd.docs_dir 54*6236dae4SAndroid Build Coastguard Worker env.make_data_file(indir=indir, fname="data-10k", fsize=10*1024) 55*6236dae4SAndroid Build Coastguard Worker env.make_data_file(indir=indir, fname="data-100k", fsize=100*1024) 56*6236dae4SAndroid Build Coastguard Worker env.make_data_file(indir=indir, fname="data-1m", fsize=1024*1024) 57*6236dae4SAndroid Build Coastguard Worker env.make_data_file(indir=indir, fname="data-10m", fsize=10*1024*1024) 58*6236dae4SAndroid Build Coastguard Worker env.make_data_file(indir=indir, fname="data-50m", fsize=50*1024*1024) 59*6236dae4SAndroid Build Coastguard Worker 60*6236dae4SAndroid Build Coastguard Worker # download 1 file 61*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 62*6236dae4SAndroid Build Coastguard Worker def test_02_01_download_1(self, env: Env, httpd, nghttpx, repeat, proto): 63*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 64*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 65*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 66*6236dae4SAndroid Build Coastguard Worker url = f'https://{env.authority_for(env.domain1, proto)}/data.json' 67*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[url], alpn_proto=proto) 68*6236dae4SAndroid Build Coastguard Worker r.check_response(http_status=200) 69*6236dae4SAndroid Build Coastguard Worker 70*6236dae4SAndroid Build Coastguard Worker # download 2 files 71*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 72*6236dae4SAndroid Build Coastguard Worker def test_02_02_download_2(self, env: Env, httpd, nghttpx, repeat, proto): 73*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 74*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 75*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 76*6236dae4SAndroid Build Coastguard Worker url = f'https://{env.authority_for(env.domain1, proto)}/data.json?[0-1]' 77*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[url], alpn_proto=proto) 78*6236dae4SAndroid Build Coastguard Worker r.check_response(http_status=200, count=2) 79*6236dae4SAndroid Build Coastguard Worker 80*6236dae4SAndroid Build Coastguard Worker # download 100 files sequentially 81*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 82*6236dae4SAndroid Build Coastguard Worker def test_02_03_download_sequential(self, env: Env, 83*6236dae4SAndroid Build Coastguard Worker httpd, nghttpx, repeat, proto): 84*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 85*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 86*6236dae4SAndroid Build Coastguard Worker count = 10 87*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 88*6236dae4SAndroid Build Coastguard Worker urln = f'https://{env.authority_for(env.domain1, proto)}/data.json?[0-{count-1}]' 89*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], alpn_proto=proto) 90*6236dae4SAndroid Build Coastguard Worker r.check_response(http_status=200, count=count, connect_count=1) 91*6236dae4SAndroid Build Coastguard Worker 92*6236dae4SAndroid Build Coastguard Worker # download 100 files parallel 93*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 94*6236dae4SAndroid Build Coastguard Worker def test_02_04_download_parallel(self, env: Env, 95*6236dae4SAndroid Build Coastguard Worker httpd, nghttpx, repeat, proto): 96*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 97*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 98*6236dae4SAndroid Build Coastguard Worker count = 10 99*6236dae4SAndroid Build Coastguard Worker max_parallel = 5 100*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 101*6236dae4SAndroid Build Coastguard Worker urln = f'https://{env.authority_for(env.domain1, proto)}/data.json?[0-{count-1}]' 102*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], alpn_proto=proto, extra_args=[ 103*6236dae4SAndroid Build Coastguard Worker '--parallel', '--parallel-max', f'{max_parallel}' 104*6236dae4SAndroid Build Coastguard Worker ]) 105*6236dae4SAndroid Build Coastguard Worker r.check_response(http_status=200, count=count) 106*6236dae4SAndroid Build Coastguard Worker if proto == 'http/1.1': 107*6236dae4SAndroid Build Coastguard Worker # http/1.1 parallel transfers will open multiple connections 108*6236dae4SAndroid Build Coastguard Worker assert r.total_connects > 1, r.dump_logs() 109*6236dae4SAndroid Build Coastguard Worker else: 110*6236dae4SAndroid Build Coastguard Worker # http2 parallel transfers will use one connection (common limit is 100) 111*6236dae4SAndroid Build Coastguard Worker assert r.total_connects == 1, r.dump_logs() 112*6236dae4SAndroid Build Coastguard Worker 113*6236dae4SAndroid Build Coastguard Worker # download 500 files sequential 114*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 115*6236dae4SAndroid Build Coastguard Worker def test_02_05_download_many_sequential(self, env: Env, 116*6236dae4SAndroid Build Coastguard Worker httpd, nghttpx, repeat, proto): 117*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 118*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 119*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and env.curl_uses_lib('msh3'): 120*6236dae4SAndroid Build Coastguard Worker pytest.skip("msh3 shaky here") 121*6236dae4SAndroid Build Coastguard Worker count = 200 122*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 123*6236dae4SAndroid Build Coastguard Worker urln = f'https://{env.authority_for(env.domain1, proto)}/data.json?[0-{count-1}]' 124*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], alpn_proto=proto) 125*6236dae4SAndroid Build Coastguard Worker r.check_response(http_status=200, count=count) 126*6236dae4SAndroid Build Coastguard Worker if proto == 'http/1.1': 127*6236dae4SAndroid Build Coastguard Worker # http/1.1 parallel transfers will open multiple connections 128*6236dae4SAndroid Build Coastguard Worker assert r.total_connects > 1, r.dump_logs() 129*6236dae4SAndroid Build Coastguard Worker else: 130*6236dae4SAndroid Build Coastguard Worker # http2 parallel transfers will use one connection (common limit is 100) 131*6236dae4SAndroid Build Coastguard Worker assert r.total_connects == 1, r.dump_logs() 132*6236dae4SAndroid Build Coastguard Worker 133*6236dae4SAndroid Build Coastguard Worker # download 500 files parallel 134*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['h2', 'h3']) 135*6236dae4SAndroid Build Coastguard Worker def test_02_06_download_many_parallel(self, env: Env, 136*6236dae4SAndroid Build Coastguard Worker httpd, nghttpx, repeat, proto): 137*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 138*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 139*6236dae4SAndroid Build Coastguard Worker count = 200 140*6236dae4SAndroid Build Coastguard Worker max_parallel = 50 141*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 142*6236dae4SAndroid Build Coastguard Worker urln = f'https://{env.authority_for(env.domain1, proto)}/data.json?[000-{count-1}]' 143*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], alpn_proto=proto, extra_args=[ 144*6236dae4SAndroid Build Coastguard Worker '--parallel', '--parallel-max', f'{max_parallel}' 145*6236dae4SAndroid Build Coastguard Worker ]) 146*6236dae4SAndroid Build Coastguard Worker r.check_response(http_status=200, count=count, connect_count=1) 147*6236dae4SAndroid Build Coastguard Worker 148*6236dae4SAndroid Build Coastguard Worker # download files parallel, check connection reuse/multiplex 149*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['h2', 'h3']) 150*6236dae4SAndroid Build Coastguard Worker def test_02_07_download_reuse(self, env: Env, 151*6236dae4SAndroid Build Coastguard Worker httpd, nghttpx, repeat, proto): 152*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 153*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 154*6236dae4SAndroid Build Coastguard Worker count = 200 155*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 156*6236dae4SAndroid Build Coastguard Worker urln = f'https://{env.authority_for(env.domain1, proto)}/data.json?[0-{count-1}]' 157*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], alpn_proto=proto, 158*6236dae4SAndroid Build Coastguard Worker with_stats=True, extra_args=[ 159*6236dae4SAndroid Build Coastguard Worker '--parallel', '--parallel-max', '200' 160*6236dae4SAndroid Build Coastguard Worker ]) 161*6236dae4SAndroid Build Coastguard Worker r.check_response(http_status=200, count=count) 162*6236dae4SAndroid Build Coastguard Worker # should have used at most 2 connections only (test servers allow 100 req/conn) 163*6236dae4SAndroid Build Coastguard Worker # it may be just 1 on slow systems where request are answered faster than 164*6236dae4SAndroid Build Coastguard Worker # curl can exhaust the capacity or if curl runs with address-sanitizer speed 165*6236dae4SAndroid Build Coastguard Worker assert r.total_connects <= 2, "h2 should use fewer connections here" 166*6236dae4SAndroid Build Coastguard Worker 167*6236dae4SAndroid Build Coastguard Worker # download files parallel with http/1.1, check connection not reused 168*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1']) 169*6236dae4SAndroid Build Coastguard Worker def test_02_07b_download_reuse(self, env: Env, 170*6236dae4SAndroid Build Coastguard Worker httpd, nghttpx, repeat, proto): 171*6236dae4SAndroid Build Coastguard Worker count = 6 172*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 173*6236dae4SAndroid Build Coastguard Worker urln = f'https://{env.authority_for(env.domain1, proto)}/data.json?[0-{count-1}]' 174*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], alpn_proto=proto, 175*6236dae4SAndroid Build Coastguard Worker with_stats=True, extra_args=[ 176*6236dae4SAndroid Build Coastguard Worker '--parallel' 177*6236dae4SAndroid Build Coastguard Worker ]) 178*6236dae4SAndroid Build Coastguard Worker r.check_response(count=count, http_status=200) 179*6236dae4SAndroid Build Coastguard Worker # http/1.1 should have used count connections 180*6236dae4SAndroid Build Coastguard Worker assert r.total_connects == count, "http/1.1 should use this many connections" 181*6236dae4SAndroid Build Coastguard Worker 182*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 183*6236dae4SAndroid Build Coastguard Worker def test_02_08_1MB_serial(self, env: Env, 184*6236dae4SAndroid Build Coastguard Worker httpd, nghttpx, repeat, proto): 185*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 186*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 187*6236dae4SAndroid Build Coastguard Worker count = 5 188*6236dae4SAndroid Build Coastguard Worker urln = f'https://{env.authority_for(env.domain1, proto)}/data-1m?[0-{count-1}]' 189*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 190*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], alpn_proto=proto) 191*6236dae4SAndroid Build Coastguard Worker r.check_response(count=count, http_status=200) 192*6236dae4SAndroid Build Coastguard Worker 193*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['h2', 'h3']) 194*6236dae4SAndroid Build Coastguard Worker def test_02_09_1MB_parallel(self, env: Env, 195*6236dae4SAndroid Build Coastguard Worker httpd, nghttpx, repeat, proto): 196*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 197*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 198*6236dae4SAndroid Build Coastguard Worker count = 5 199*6236dae4SAndroid Build Coastguard Worker urln = f'https://{env.authority_for(env.domain1, proto)}/data-1m?[0-{count-1}]' 200*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 201*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], alpn_proto=proto, extra_args=[ 202*6236dae4SAndroid Build Coastguard Worker '--parallel' 203*6236dae4SAndroid Build Coastguard Worker ]) 204*6236dae4SAndroid Build Coastguard Worker r.check_response(count=count, http_status=200) 205*6236dae4SAndroid Build Coastguard Worker 206*6236dae4SAndroid Build Coastguard Worker @pytest.mark.skipif(condition=Env().slow_network, reason="not suitable for slow network tests") 207*6236dae4SAndroid Build Coastguard Worker @pytest.mark.skipif(condition=Env().ci_run, reason="not suitable for CI runs") 208*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 209*6236dae4SAndroid Build Coastguard Worker def test_02_10_10MB_serial(self, env: Env, 210*6236dae4SAndroid Build Coastguard Worker httpd, nghttpx, repeat, proto): 211*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 212*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 213*6236dae4SAndroid Build Coastguard Worker count = 3 214*6236dae4SAndroid Build Coastguard Worker urln = f'https://{env.authority_for(env.domain1, proto)}/data-10m?[0-{count-1}]' 215*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 216*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], alpn_proto=proto) 217*6236dae4SAndroid Build Coastguard Worker r.check_response(count=count, http_status=200) 218*6236dae4SAndroid Build Coastguard Worker 219*6236dae4SAndroid Build Coastguard Worker @pytest.mark.skipif(condition=Env().slow_network, reason="not suitable for slow network tests") 220*6236dae4SAndroid Build Coastguard Worker @pytest.mark.skipif(condition=Env().ci_run, reason="not suitable for CI runs") 221*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['h2', 'h3']) 222*6236dae4SAndroid Build Coastguard Worker def test_02_11_10MB_parallel(self, env: Env, 223*6236dae4SAndroid Build Coastguard Worker httpd, nghttpx, repeat, proto): 224*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 225*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 226*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and env.curl_uses_lib('msh3'): 227*6236dae4SAndroid Build Coastguard Worker pytest.skip("msh3 stalls here") 228*6236dae4SAndroid Build Coastguard Worker count = 3 229*6236dae4SAndroid Build Coastguard Worker urln = f'https://{env.authority_for(env.domain1, proto)}/data-10m?[0-{count-1}]' 230*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 231*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], alpn_proto=proto, extra_args=[ 232*6236dae4SAndroid Build Coastguard Worker '--parallel' 233*6236dae4SAndroid Build Coastguard Worker ]) 234*6236dae4SAndroid Build Coastguard Worker r.check_response(count=count, http_status=200) 235*6236dae4SAndroid Build Coastguard Worker 236*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['h2', 'h3']) 237*6236dae4SAndroid Build Coastguard Worker def test_02_12_head_serial_https(self, env: Env, 238*6236dae4SAndroid Build Coastguard Worker httpd, nghttpx, repeat, proto): 239*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 240*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 241*6236dae4SAndroid Build Coastguard Worker count = 5 242*6236dae4SAndroid Build Coastguard Worker urln = f'https://{env.authority_for(env.domain1, proto)}/data-10m?[0-{count-1}]' 243*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 244*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], alpn_proto=proto, extra_args=[ 245*6236dae4SAndroid Build Coastguard Worker '--head' 246*6236dae4SAndroid Build Coastguard Worker ]) 247*6236dae4SAndroid Build Coastguard Worker r.check_response(count=count, http_status=200) 248*6236dae4SAndroid Build Coastguard Worker 249*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['h2']) 250*6236dae4SAndroid Build Coastguard Worker def test_02_13_head_serial_h2c(self, env: Env, 251*6236dae4SAndroid Build Coastguard Worker httpd, nghttpx, repeat, proto): 252*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 253*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 254*6236dae4SAndroid Build Coastguard Worker count = 5 255*6236dae4SAndroid Build Coastguard Worker urln = f'http://{env.domain1}:{env.http_port}/data-10m?[0-{count-1}]' 256*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 257*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], alpn_proto=proto, extra_args=[ 258*6236dae4SAndroid Build Coastguard Worker '--head', '--http2-prior-knowledge', '--fail-early' 259*6236dae4SAndroid Build Coastguard Worker ]) 260*6236dae4SAndroid Build Coastguard Worker r.check_response(count=count, http_status=200) 261*6236dae4SAndroid Build Coastguard Worker 262*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['h2', 'h3']) 263*6236dae4SAndroid Build Coastguard Worker def test_02_14_not_found(self, env: Env, httpd, nghttpx, repeat, proto): 264*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 265*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 266*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and env.curl_uses_lib('msh3'): 267*6236dae4SAndroid Build Coastguard Worker pytest.skip("msh3 stalls here") 268*6236dae4SAndroid Build Coastguard Worker count = 5 269*6236dae4SAndroid Build Coastguard Worker urln = f'https://{env.authority_for(env.domain1, proto)}/not-found?[0-{count-1}]' 270*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 271*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], alpn_proto=proto, extra_args=[ 272*6236dae4SAndroid Build Coastguard Worker '--parallel' 273*6236dae4SAndroid Build Coastguard Worker ]) 274*6236dae4SAndroid Build Coastguard Worker r.check_stats(count=count, http_status=404, exitcode=0, 275*6236dae4SAndroid Build Coastguard Worker remote_port=env.port_for(alpn_proto=proto), 276*6236dae4SAndroid Build Coastguard Worker remote_ip='127.0.0.1') 277*6236dae4SAndroid Build Coastguard Worker 278*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['h2', 'h3']) 279*6236dae4SAndroid Build Coastguard Worker def test_02_15_fail_not_found(self, env: Env, httpd, nghttpx, repeat, proto): 280*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 281*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 282*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and env.curl_uses_lib('msh3'): 283*6236dae4SAndroid Build Coastguard Worker pytest.skip("msh3 stalls here") 284*6236dae4SAndroid Build Coastguard Worker count = 5 285*6236dae4SAndroid Build Coastguard Worker urln = f'https://{env.authority_for(env.domain1, proto)}/not-found?[0-{count-1}]' 286*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 287*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], alpn_proto=proto, extra_args=[ 288*6236dae4SAndroid Build Coastguard Worker '--fail' 289*6236dae4SAndroid Build Coastguard Worker ]) 290*6236dae4SAndroid Build Coastguard Worker r.check_stats(count=count, http_status=404, exitcode=22, 291*6236dae4SAndroid Build Coastguard Worker remote_port=env.port_for(alpn_proto=proto), 292*6236dae4SAndroid Build Coastguard Worker remote_ip='127.0.0.1') 293*6236dae4SAndroid Build Coastguard Worker 294*6236dae4SAndroid Build Coastguard Worker @pytest.mark.skipif(condition=Env().slow_network, reason="not suitable for slow network tests") 295*6236dae4SAndroid Build Coastguard Worker def test_02_20_h2_small_frames(self, env: Env, httpd, repeat): 296*6236dae4SAndroid Build Coastguard Worker # Test case to reproduce content corruption as observed in 297*6236dae4SAndroid Build Coastguard Worker # https://github.com/curl/curl/issues/10525 298*6236dae4SAndroid Build Coastguard Worker # To reliably reproduce, we need an Apache httpd that supports 299*6236dae4SAndroid Build Coastguard Worker # setting smaller frame sizes. This is not released yet, we 300*6236dae4SAndroid Build Coastguard Worker # test if it works and back out if not. 301*6236dae4SAndroid Build Coastguard Worker httpd.set_extra_config(env.domain1, lines=[ 302*6236dae4SAndroid Build Coastguard Worker 'H2MaxDataFrameLen 1024', 303*6236dae4SAndroid Build Coastguard Worker ]) 304*6236dae4SAndroid Build Coastguard Worker assert httpd.stop() 305*6236dae4SAndroid Build Coastguard Worker if not httpd.start(): 306*6236dae4SAndroid Build Coastguard Worker # no, not supported, bail out 307*6236dae4SAndroid Build Coastguard Worker httpd.set_extra_config(env.domain1, lines=None) 308*6236dae4SAndroid Build Coastguard Worker assert httpd.start() 309*6236dae4SAndroid Build Coastguard Worker pytest.skip('H2MaxDataFrameLen not supported') 310*6236dae4SAndroid Build Coastguard Worker # ok, make 100 downloads with 2 parallel running and they 311*6236dae4SAndroid Build Coastguard Worker # are expected to stumble into the issue when using `lib/http2.c` 312*6236dae4SAndroid Build Coastguard Worker # from curl 7.88.0 313*6236dae4SAndroid Build Coastguard Worker count = 5 314*6236dae4SAndroid Build Coastguard Worker urln = f'https://{env.authority_for(env.domain1, "h2")}/data-1m?[0-{count-1}]' 315*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 316*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], alpn_proto="h2", extra_args=[ 317*6236dae4SAndroid Build Coastguard Worker '--parallel', '--parallel-max', '2' 318*6236dae4SAndroid Build Coastguard Worker ]) 319*6236dae4SAndroid Build Coastguard Worker r.check_response(count=count, http_status=200) 320*6236dae4SAndroid Build Coastguard Worker srcfile = os.path.join(httpd.docs_dir, 'data-1m') 321*6236dae4SAndroid Build Coastguard Worker self.check_downloads(curl, srcfile, count) 322*6236dae4SAndroid Build Coastguard Worker # restore httpd defaults 323*6236dae4SAndroid Build Coastguard Worker httpd.set_extra_config(env.domain1, lines=None) 324*6236dae4SAndroid Build Coastguard Worker assert httpd.stop() 325*6236dae4SAndroid Build Coastguard Worker assert httpd.start() 326*6236dae4SAndroid Build Coastguard Worker 327*6236dae4SAndroid Build Coastguard Worker # download via lib client, 1 at a time, pause/resume at different offsets 328*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("pause_offset", [0, 10*1024, 100*1023, 640000]) 329*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 330*6236dae4SAndroid Build Coastguard Worker def test_02_21_lib_serial(self, env: Env, httpd, nghttpx, proto, pause_offset, repeat): 331*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 332*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 333*6236dae4SAndroid Build Coastguard Worker count = 2 334*6236dae4SAndroid Build Coastguard Worker docname = 'data-10m' 335*6236dae4SAndroid Build Coastguard Worker url = f'https://localhost:{env.https_port}/{docname}' 336*6236dae4SAndroid Build Coastguard Worker client = LocalClient(name='hx-download', env=env) 337*6236dae4SAndroid Build Coastguard Worker if not client.exists(): 338*6236dae4SAndroid Build Coastguard Worker pytest.skip(f'example client not built: {client.name}') 339*6236dae4SAndroid Build Coastguard Worker r = client.run(args=[ 340*6236dae4SAndroid Build Coastguard Worker '-n', f'{count}', '-P', f'{pause_offset}', '-V', proto, url 341*6236dae4SAndroid Build Coastguard Worker ]) 342*6236dae4SAndroid Build Coastguard Worker r.check_exit_code(0) 343*6236dae4SAndroid Build Coastguard Worker srcfile = os.path.join(httpd.docs_dir, docname) 344*6236dae4SAndroid Build Coastguard Worker self.check_downloads(client, srcfile, count) 345*6236dae4SAndroid Build Coastguard Worker 346*6236dae4SAndroid Build Coastguard Worker # download via lib client, several at a time, pause/resume 347*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("pause_offset", [100*1023]) 348*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 349*6236dae4SAndroid Build Coastguard Worker def test_02_22_lib_parallel_resume(self, env: Env, httpd, nghttpx, proto, pause_offset, repeat): 350*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 351*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 352*6236dae4SAndroid Build Coastguard Worker count = 2 353*6236dae4SAndroid Build Coastguard Worker max_parallel = 5 354*6236dae4SAndroid Build Coastguard Worker docname = 'data-10m' 355*6236dae4SAndroid Build Coastguard Worker url = f'https://localhost:{env.https_port}/{docname}' 356*6236dae4SAndroid Build Coastguard Worker client = LocalClient(name='hx-download', env=env) 357*6236dae4SAndroid Build Coastguard Worker if not client.exists(): 358*6236dae4SAndroid Build Coastguard Worker pytest.skip(f'example client not built: {client.name}') 359*6236dae4SAndroid Build Coastguard Worker r = client.run(args=[ 360*6236dae4SAndroid Build Coastguard Worker '-n', f'{count}', '-m', f'{max_parallel}', 361*6236dae4SAndroid Build Coastguard Worker '-P', f'{pause_offset}', '-V', proto, url 362*6236dae4SAndroid Build Coastguard Worker ]) 363*6236dae4SAndroid Build Coastguard Worker r.check_exit_code(0) 364*6236dae4SAndroid Build Coastguard Worker srcfile = os.path.join(httpd.docs_dir, docname) 365*6236dae4SAndroid Build Coastguard Worker self.check_downloads(client, srcfile, count) 366*6236dae4SAndroid Build Coastguard Worker 367*6236dae4SAndroid Build Coastguard Worker # download, several at a time, pause and abort paused 368*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 369*6236dae4SAndroid Build Coastguard Worker def test_02_23a_lib_abort_paused(self, env: Env, httpd, nghttpx, proto, repeat): 370*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 371*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 372*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and env.curl_uses_ossl_quic(): 373*6236dae4SAndroid Build Coastguard Worker pytest.skip('OpenSSL QUIC fails here') 374*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and env.ci_run and env.curl_uses_lib('quiche'): 375*6236dae4SAndroid Build Coastguard Worker pytest.skip("fails in CI, but works locally for unknown reasons") 376*6236dae4SAndroid Build Coastguard Worker count = 10 377*6236dae4SAndroid Build Coastguard Worker max_parallel = 5 378*6236dae4SAndroid Build Coastguard Worker if proto in ['h2', 'h3']: 379*6236dae4SAndroid Build Coastguard Worker pause_offset = 64 * 1024 380*6236dae4SAndroid Build Coastguard Worker else: 381*6236dae4SAndroid Build Coastguard Worker pause_offset = 12 * 1024 382*6236dae4SAndroid Build Coastguard Worker docname = 'data-1m' 383*6236dae4SAndroid Build Coastguard Worker url = f'https://localhost:{env.https_port}/{docname}' 384*6236dae4SAndroid Build Coastguard Worker client = LocalClient(name='hx-download', env=env) 385*6236dae4SAndroid Build Coastguard Worker if not client.exists(): 386*6236dae4SAndroid Build Coastguard Worker pytest.skip(f'example client not built: {client.name}') 387*6236dae4SAndroid Build Coastguard Worker r = client.run(args=[ 388*6236dae4SAndroid Build Coastguard Worker '-n', f'{count}', '-m', f'{max_parallel}', '-a', 389*6236dae4SAndroid Build Coastguard Worker '-P', f'{pause_offset}', '-V', proto, url 390*6236dae4SAndroid Build Coastguard Worker ]) 391*6236dae4SAndroid Build Coastguard Worker r.check_exit_code(0) 392*6236dae4SAndroid Build Coastguard Worker srcfile = os.path.join(httpd.docs_dir, docname) 393*6236dae4SAndroid Build Coastguard Worker # downloads should be there, but not necessarily complete 394*6236dae4SAndroid Build Coastguard Worker self.check_downloads(client, srcfile, count, complete=False) 395*6236dae4SAndroid Build Coastguard Worker 396*6236dae4SAndroid Build Coastguard Worker # download, several at a time, abort after n bytes 397*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 398*6236dae4SAndroid Build Coastguard Worker def test_02_23b_lib_abort_offset(self, env: Env, httpd, nghttpx, proto, repeat): 399*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 400*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 401*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and env.curl_uses_ossl_quic(): 402*6236dae4SAndroid Build Coastguard Worker pytest.skip('OpenSSL QUIC fails here') 403*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and env.ci_run and env.curl_uses_lib('quiche'): 404*6236dae4SAndroid Build Coastguard Worker pytest.skip("fails in CI, but works locally for unknown reasons") 405*6236dae4SAndroid Build Coastguard Worker count = 10 406*6236dae4SAndroid Build Coastguard Worker max_parallel = 5 407*6236dae4SAndroid Build Coastguard Worker if proto in ['h2', 'h3']: 408*6236dae4SAndroid Build Coastguard Worker abort_offset = 64 * 1024 409*6236dae4SAndroid Build Coastguard Worker else: 410*6236dae4SAndroid Build Coastguard Worker abort_offset = 12 * 1024 411*6236dae4SAndroid Build Coastguard Worker docname = 'data-1m' 412*6236dae4SAndroid Build Coastguard Worker url = f'https://localhost:{env.https_port}/{docname}' 413*6236dae4SAndroid Build Coastguard Worker client = LocalClient(name='hx-download', env=env) 414*6236dae4SAndroid Build Coastguard Worker if not client.exists(): 415*6236dae4SAndroid Build Coastguard Worker pytest.skip(f'example client not built: {client.name}') 416*6236dae4SAndroid Build Coastguard Worker r = client.run(args=[ 417*6236dae4SAndroid Build Coastguard Worker '-n', f'{count}', '-m', f'{max_parallel}', '-a', 418*6236dae4SAndroid Build Coastguard Worker '-A', f'{abort_offset}', '-V', proto, url 419*6236dae4SAndroid Build Coastguard Worker ]) 420*6236dae4SAndroid Build Coastguard Worker r.check_exit_code(0) 421*6236dae4SAndroid Build Coastguard Worker srcfile = os.path.join(httpd.docs_dir, docname) 422*6236dae4SAndroid Build Coastguard Worker # downloads should be there, but not necessarily complete 423*6236dae4SAndroid Build Coastguard Worker self.check_downloads(client, srcfile, count, complete=False) 424*6236dae4SAndroid Build Coastguard Worker 425*6236dae4SAndroid Build Coastguard Worker # download, several at a time, abort after n bytes 426*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 427*6236dae4SAndroid Build Coastguard Worker def test_02_23c_lib_fail_offset(self, env: Env, httpd, nghttpx, proto, repeat): 428*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 429*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 430*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and env.curl_uses_ossl_quic(): 431*6236dae4SAndroid Build Coastguard Worker pytest.skip('OpenSSL QUIC fails here') 432*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and env.ci_run and env.curl_uses_lib('quiche'): 433*6236dae4SAndroid Build Coastguard Worker pytest.skip("fails in CI, but works locally for unknown reasons") 434*6236dae4SAndroid Build Coastguard Worker count = 10 435*6236dae4SAndroid Build Coastguard Worker max_parallel = 5 436*6236dae4SAndroid Build Coastguard Worker if proto in ['h2', 'h3']: 437*6236dae4SAndroid Build Coastguard Worker fail_offset = 64 * 1024 438*6236dae4SAndroid Build Coastguard Worker else: 439*6236dae4SAndroid Build Coastguard Worker fail_offset = 12 * 1024 440*6236dae4SAndroid Build Coastguard Worker docname = 'data-1m' 441*6236dae4SAndroid Build Coastguard Worker url = f'https://localhost:{env.https_port}/{docname}' 442*6236dae4SAndroid Build Coastguard Worker client = LocalClient(name='hx-download', env=env) 443*6236dae4SAndroid Build Coastguard Worker if not client.exists(): 444*6236dae4SAndroid Build Coastguard Worker pytest.skip(f'example client not built: {client.name}') 445*6236dae4SAndroid Build Coastguard Worker r = client.run(args=[ 446*6236dae4SAndroid Build Coastguard Worker '-n', f'{count}', '-m', f'{max_parallel}', '-a', 447*6236dae4SAndroid Build Coastguard Worker '-F', f'{fail_offset}', '-V', proto, url 448*6236dae4SAndroid Build Coastguard Worker ]) 449*6236dae4SAndroid Build Coastguard Worker r.check_exit_code(0) 450*6236dae4SAndroid Build Coastguard Worker srcfile = os.path.join(httpd.docs_dir, docname) 451*6236dae4SAndroid Build Coastguard Worker # downloads should be there, but not necessarily complete 452*6236dae4SAndroid Build Coastguard Worker self.check_downloads(client, srcfile, count, complete=False) 453*6236dae4SAndroid Build Coastguard Worker 454*6236dae4SAndroid Build Coastguard Worker # speed limited download 455*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 456*6236dae4SAndroid Build Coastguard Worker def test_02_24_speed_limit(self, env: Env, httpd, nghttpx, proto, repeat): 457*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 458*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 459*6236dae4SAndroid Build Coastguard Worker count = 1 460*6236dae4SAndroid Build Coastguard Worker url = f'https://{env.authority_for(env.domain1, proto)}/data-1m' 461*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 462*6236dae4SAndroid Build Coastguard Worker speed_limit = 384 * 1024 463*6236dae4SAndroid Build Coastguard Worker min_duration = math.floor((1024 * 1024)/speed_limit) 464*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[url], alpn_proto=proto, extra_args=[ 465*6236dae4SAndroid Build Coastguard Worker '--limit-rate', f'{speed_limit}' 466*6236dae4SAndroid Build Coastguard Worker ]) 467*6236dae4SAndroid Build Coastguard Worker r.check_response(count=count, http_status=200) 468*6236dae4SAndroid Build Coastguard Worker assert r.duration > timedelta(seconds=min_duration), \ 469*6236dae4SAndroid Build Coastguard Worker f'rate limited transfer should take more than {min_duration}s, '\ 470*6236dae4SAndroid Build Coastguard Worker f'not {r.duration}' 471*6236dae4SAndroid Build Coastguard Worker 472*6236dae4SAndroid Build Coastguard Worker # make extreme parallel h2 upgrades, check invalid conn reuse 473*6236dae4SAndroid Build Coastguard Worker # before protocol switch has happened 474*6236dae4SAndroid Build Coastguard Worker def test_02_25_h2_upgrade_x(self, env: Env, httpd, repeat): 475*6236dae4SAndroid Build Coastguard Worker url = f'http://localhost:{env.http_port}/data-100k' 476*6236dae4SAndroid Build Coastguard Worker client = LocalClient(name='h2-upgrade-extreme', env=env, timeout=15) 477*6236dae4SAndroid Build Coastguard Worker if not client.exists(): 478*6236dae4SAndroid Build Coastguard Worker pytest.skip(f'example client not built: {client.name}') 479*6236dae4SAndroid Build Coastguard Worker r = client.run(args=[url]) 480*6236dae4SAndroid Build Coastguard Worker assert r.exit_code == 0, f'{client.dump_logs()}' 481*6236dae4SAndroid Build Coastguard Worker 482*6236dae4SAndroid Build Coastguard Worker # Special client that tests TLS session reuse in parallel transfers 483*6236dae4SAndroid Build Coastguard Worker # TODO: just uses a single connection for h2/h3. Not sure how to prevent that 484*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 485*6236dae4SAndroid Build Coastguard Worker def test_02_26_session_shared_reuse(self, env: Env, proto, httpd, nghttpx, repeat): 486*6236dae4SAndroid Build Coastguard Worker url = f'https://{env.authority_for(env.domain1, proto)}/data-100k' 487*6236dae4SAndroid Build Coastguard Worker client = LocalClient(name='tls-session-reuse', env=env) 488*6236dae4SAndroid Build Coastguard Worker if not client.exists(): 489*6236dae4SAndroid Build Coastguard Worker pytest.skip(f'example client not built: {client.name}') 490*6236dae4SAndroid Build Coastguard Worker r = client.run(args=[proto, url]) 491*6236dae4SAndroid Build Coastguard Worker r.check_exit_code(0) 492*6236dae4SAndroid Build Coastguard Worker 493*6236dae4SAndroid Build Coastguard Worker # test on paused transfers, based on issue #11982 494*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 495*6236dae4SAndroid Build Coastguard Worker def test_02_27a_paused_no_cl(self, env: Env, httpd, nghttpx, proto, repeat): 496*6236dae4SAndroid Build Coastguard Worker url = f'https://{env.authority_for(env.domain1, proto)}' \ 497*6236dae4SAndroid Build Coastguard Worker '/curltest/tweak/?&chunks=6&chunk_size=8000' 498*6236dae4SAndroid Build Coastguard Worker client = LocalClient(env=env, name='h2-pausing') 499*6236dae4SAndroid Build Coastguard Worker r = client.run(args=['-V', proto, url]) 500*6236dae4SAndroid Build Coastguard Worker r.check_exit_code(0) 501*6236dae4SAndroid Build Coastguard Worker 502*6236dae4SAndroid Build Coastguard Worker # test on paused transfers, based on issue #11982 503*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 504*6236dae4SAndroid Build Coastguard Worker def test_02_27b_paused_no_cl(self, env: Env, httpd, nghttpx, proto, repeat): 505*6236dae4SAndroid Build Coastguard Worker url = f'https://{env.authority_for(env.domain1, proto)}' \ 506*6236dae4SAndroid Build Coastguard Worker '/curltest/tweak/?error=502' 507*6236dae4SAndroid Build Coastguard Worker client = LocalClient(env=env, name='h2-pausing') 508*6236dae4SAndroid Build Coastguard Worker r = client.run(args=['-V', proto, url]) 509*6236dae4SAndroid Build Coastguard Worker r.check_exit_code(0) 510*6236dae4SAndroid Build Coastguard Worker 511*6236dae4SAndroid Build Coastguard Worker # test on paused transfers, based on issue #11982 512*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 513*6236dae4SAndroid Build Coastguard Worker def test_02_27c_paused_no_cl(self, env: Env, httpd, nghttpx, proto, repeat): 514*6236dae4SAndroid Build Coastguard Worker url = f'https://{env.authority_for(env.domain1, proto)}' \ 515*6236dae4SAndroid Build Coastguard Worker '/curltest/tweak/?status=200&chunks=1&chunk_size=100' 516*6236dae4SAndroid Build Coastguard Worker client = LocalClient(env=env, name='h2-pausing') 517*6236dae4SAndroid Build Coastguard Worker r = client.run(args=['-V', proto, url]) 518*6236dae4SAndroid Build Coastguard Worker r.check_exit_code(0) 519*6236dae4SAndroid Build Coastguard Worker 520*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 521*6236dae4SAndroid Build Coastguard Worker def test_02_28_get_compressed(self, env: Env, httpd, nghttpx, repeat, proto): 522*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 523*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 524*6236dae4SAndroid Build Coastguard Worker count = 1 525*6236dae4SAndroid Build Coastguard Worker urln = f'https://{env.authority_for(env.domain1brotli, proto)}/data-100k?[0-{count-1}]' 526*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 527*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], alpn_proto=proto, extra_args=[ 528*6236dae4SAndroid Build Coastguard Worker '--compressed' 529*6236dae4SAndroid Build Coastguard Worker ]) 530*6236dae4SAndroid Build Coastguard Worker r.check_exit_code(code=0) 531*6236dae4SAndroid Build Coastguard Worker r.check_response(count=count, http_status=200) 532*6236dae4SAndroid Build Coastguard Worker 533*6236dae4SAndroid Build Coastguard Worker def check_downloads(self, client, srcfile: str, count: int, 534*6236dae4SAndroid Build Coastguard Worker complete: bool = True): 535*6236dae4SAndroid Build Coastguard Worker for i in range(count): 536*6236dae4SAndroid Build Coastguard Worker dfile = client.download_file(i) 537*6236dae4SAndroid Build Coastguard Worker assert os.path.exists(dfile) 538*6236dae4SAndroid Build Coastguard Worker if complete and not filecmp.cmp(srcfile, dfile, shallow=False): 539*6236dae4SAndroid Build Coastguard Worker diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), 540*6236dae4SAndroid Build Coastguard Worker b=open(dfile).readlines(), 541*6236dae4SAndroid Build Coastguard Worker fromfile=srcfile, 542*6236dae4SAndroid Build Coastguard Worker tofile=dfile, 543*6236dae4SAndroid Build Coastguard Worker n=1)) 544*6236dae4SAndroid Build Coastguard Worker assert False, f'download {dfile} differs:\n{diff}' 545*6236dae4SAndroid Build Coastguard Worker 546*6236dae4SAndroid Build Coastguard Worker # download via lib client, 1 at a time, pause/resume at different offsets 547*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("pause_offset", [0, 10*1024, 100*1023, 640000]) 548*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 549*6236dae4SAndroid Build Coastguard Worker def test_02_29_h2_lib_serial(self, env: Env, httpd, nghttpx, proto, pause_offset, repeat): 550*6236dae4SAndroid Build Coastguard Worker count = 2 551*6236dae4SAndroid Build Coastguard Worker docname = 'data-10m' 552*6236dae4SAndroid Build Coastguard Worker url = f'https://localhost:{env.https_port}/{docname}' 553*6236dae4SAndroid Build Coastguard Worker client = LocalClient(name='hx-download', env=env) 554*6236dae4SAndroid Build Coastguard Worker if not client.exists(): 555*6236dae4SAndroid Build Coastguard Worker pytest.skip(f'example client not built: {client.name}') 556*6236dae4SAndroid Build Coastguard Worker r = client.run(args=[ 557*6236dae4SAndroid Build Coastguard Worker '-n', f'{count}', '-P', f'{pause_offset}', '-V', proto, url 558*6236dae4SAndroid Build Coastguard Worker ]) 559*6236dae4SAndroid Build Coastguard Worker r.check_exit_code(0) 560*6236dae4SAndroid Build Coastguard Worker srcfile = os.path.join(httpd.docs_dir, docname) 561*6236dae4SAndroid Build Coastguard Worker self.check_downloads(client, srcfile, count) 562*6236dae4SAndroid Build Coastguard Worker 563*6236dae4SAndroid Build Coastguard Worker # download parallel with prior knowledge 564*6236dae4SAndroid Build Coastguard Worker def test_02_30_parallel_prior_knowledge(self, env: Env, httpd): 565*6236dae4SAndroid Build Coastguard Worker count = 3 566*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 567*6236dae4SAndroid Build Coastguard Worker urln = f'http://{env.domain1}:{env.http_port}/data.json?[0-{count-1}]' 568*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], extra_args=[ 569*6236dae4SAndroid Build Coastguard Worker '--parallel', '--http2-prior-knowledge' 570*6236dae4SAndroid Build Coastguard Worker ]) 571*6236dae4SAndroid Build Coastguard Worker r.check_response(http_status=200, count=count) 572*6236dae4SAndroid Build Coastguard Worker assert r.total_connects == 1, r.dump_logs() 573*6236dae4SAndroid Build Coastguard Worker 574*6236dae4SAndroid Build Coastguard Worker # download parallel with h2 "Upgrade:" 575*6236dae4SAndroid Build Coastguard Worker def test_02_31_parallel_upgrade(self, env: Env, httpd, nghttpx): 576*6236dae4SAndroid Build Coastguard Worker count = 3 577*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 578*6236dae4SAndroid Build Coastguard Worker urln = f'http://{env.domain1}:{env.http_port}/data.json?[0-{count-1}]' 579*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[urln], extra_args=[ 580*6236dae4SAndroid Build Coastguard Worker '--parallel', '--http2' 581*6236dae4SAndroid Build Coastguard Worker ]) 582*6236dae4SAndroid Build Coastguard Worker r.check_response(http_status=200, count=count) 583*6236dae4SAndroid Build Coastguard Worker # we see 3 connections, because Apache only every serves a single 584*6236dae4SAndroid Build Coastguard Worker # request via Upgrade: and then closed the connection. 585*6236dae4SAndroid Build Coastguard Worker assert r.total_connects == 3, r.dump_logs() 586*6236dae4SAndroid Build Coastguard Worker 587*6236dae4SAndroid Build Coastguard Worker # nghttpx is the only server we have that supports TLS early data 588*6236dae4SAndroid Build Coastguard Worker @pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx") 589*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 590*6236dae4SAndroid Build Coastguard Worker def test_02_32_earlydata(self, env: Env, httpd, nghttpx, proto): 591*6236dae4SAndroid Build Coastguard Worker if not env.curl_uses_lib('gnutls'): 592*6236dae4SAndroid Build Coastguard Worker pytest.skip('TLS earlydata only implemented in GnuTLS') 593*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 594*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 595*6236dae4SAndroid Build Coastguard Worker count = 2 596*6236dae4SAndroid Build Coastguard Worker docname = 'data-10k' 597*6236dae4SAndroid Build Coastguard Worker # we want this test to always connect to nghttpx, since it is 598*6236dae4SAndroid Build Coastguard Worker # the only server we have that supports TLS earlydata 599*6236dae4SAndroid Build Coastguard Worker port = env.port_for(proto) 600*6236dae4SAndroid Build Coastguard Worker if proto != 'h3': 601*6236dae4SAndroid Build Coastguard Worker port = env.nghttpx_https_port 602*6236dae4SAndroid Build Coastguard Worker url = f'https://{env.domain1}:{port}/{docname}' 603*6236dae4SAndroid Build Coastguard Worker client = LocalClient(name='hx-download', env=env) 604*6236dae4SAndroid Build Coastguard Worker if not client.exists(): 605*6236dae4SAndroid Build Coastguard Worker pytest.skip(f'example client not built: {client.name}') 606*6236dae4SAndroid Build Coastguard Worker r = client.run(args=[ 607*6236dae4SAndroid Build Coastguard Worker '-n', f'{count}', 608*6236dae4SAndroid Build Coastguard Worker '-e', # use TLS earlydata 609*6236dae4SAndroid Build Coastguard Worker '-f', # forbid reuse of connections 610*6236dae4SAndroid Build Coastguard Worker '-r', f'{env.domain1}:{port}:127.0.0.1', 611*6236dae4SAndroid Build Coastguard Worker '-V', proto, url 612*6236dae4SAndroid Build Coastguard Worker ]) 613*6236dae4SAndroid Build Coastguard Worker r.check_exit_code(0) 614*6236dae4SAndroid Build Coastguard Worker srcfile = os.path.join(httpd.docs_dir, docname) 615*6236dae4SAndroid Build Coastguard Worker self.check_downloads(client, srcfile, count) 616*6236dae4SAndroid Build Coastguard Worker # check that TLS earlydata worked as expected 617*6236dae4SAndroid Build Coastguard Worker earlydata = {} 618*6236dae4SAndroid Build Coastguard Worker reused_session = False 619*6236dae4SAndroid Build Coastguard Worker for line in r.trace_lines: 620*6236dae4SAndroid Build Coastguard Worker m = re.match(r'^\[t-(\d+)] EarlyData: (\d+)', line) 621*6236dae4SAndroid Build Coastguard Worker if m: 622*6236dae4SAndroid Build Coastguard Worker earlydata[int(m.group(1))] = int(m.group(2)) 623*6236dae4SAndroid Build Coastguard Worker continue 624*6236dae4SAndroid Build Coastguard Worker m = re.match(r'\[1-1] \* SSL reusing session.*', line) 625*6236dae4SAndroid Build Coastguard Worker if m: 626*6236dae4SAndroid Build Coastguard Worker reused_session = True 627*6236dae4SAndroid Build Coastguard Worker assert reused_session, 'session was not reused for 2nd transfer' 628*6236dae4SAndroid Build Coastguard Worker assert earlydata[0] == 0, f'{earlydata}' 629*6236dae4SAndroid Build Coastguard Worker if proto == 'http/1.1': 630*6236dae4SAndroid Build Coastguard Worker assert earlydata[1] == 69, f'{earlydata}' 631*6236dae4SAndroid Build Coastguard Worker elif proto == 'h2': 632*6236dae4SAndroid Build Coastguard Worker assert earlydata[1] == 107, f'{earlydata}' 633*6236dae4SAndroid Build Coastguard Worker elif proto == 'h3': 634*6236dae4SAndroid Build Coastguard Worker # not implemented 635*6236dae4SAndroid Build Coastguard Worker assert earlydata[1] == 0, f'{earlydata}' 636