xref: /aosp_15_r20/external/curl/tests/http/test_07_upload.py (revision 6236dae45794135f37c4eb022389c904c8b0090d)
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 os
31*6236dae4SAndroid Build Coastguard Workerimport re
32*6236dae4SAndroid Build Coastguard Workerimport pytest
33*6236dae4SAndroid Build Coastguard Workerfrom typing import List
34*6236dae4SAndroid Build Coastguard Worker
35*6236dae4SAndroid Build Coastguard Workerfrom testenv import Env, CurlClient, LocalClient
36*6236dae4SAndroid Build Coastguard Worker
37*6236dae4SAndroid Build Coastguard Worker
38*6236dae4SAndroid Build Coastguard Workerlog = logging.getLogger(__name__)
39*6236dae4SAndroid Build Coastguard Worker
40*6236dae4SAndroid Build Coastguard Worker
41*6236dae4SAndroid Build Coastguard Workerclass TestUpload:
42*6236dae4SAndroid Build Coastguard Worker
43*6236dae4SAndroid Build Coastguard Worker    @pytest.fixture(autouse=True, scope='class')
44*6236dae4SAndroid Build Coastguard Worker    def _class_scope(self, env, httpd, nghttpx):
45*6236dae4SAndroid Build Coastguard Worker        if env.have_h3():
46*6236dae4SAndroid Build Coastguard Worker            nghttpx.start_if_needed()
47*6236dae4SAndroid Build Coastguard Worker        env.make_data_file(indir=env.gen_dir, fname="data-10k", fsize=10*1024)
48*6236dae4SAndroid Build Coastguard Worker        env.make_data_file(indir=env.gen_dir, fname="data-63k", fsize=63*1024)
49*6236dae4SAndroid Build Coastguard Worker        env.make_data_file(indir=env.gen_dir, fname="data-64k", fsize=64*1024)
50*6236dae4SAndroid Build Coastguard Worker        env.make_data_file(indir=env.gen_dir, fname="data-100k", fsize=100*1024)
51*6236dae4SAndroid Build Coastguard Worker        env.make_data_file(indir=env.gen_dir, fname="data-1m+", fsize=(1024*1024)+1)
52*6236dae4SAndroid Build Coastguard Worker        env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024)
53*6236dae4SAndroid Build Coastguard Worker        httpd.clear_extra_configs()
54*6236dae4SAndroid Build Coastguard Worker        httpd.reload()
55*6236dae4SAndroid Build Coastguard Worker
56*6236dae4SAndroid Build Coastguard Worker    # upload small data, check that this is what was echoed
57*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
58*6236dae4SAndroid Build Coastguard Worker    def test_07_01_upload_1_small(self, env: Env, httpd, nghttpx, repeat, proto):
59*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
60*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
61*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
62*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 fails here")
63*6236dae4SAndroid Build Coastguard Worker        data = '0123456789'
64*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
65*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]'
66*6236dae4SAndroid Build Coastguard Worker        r = curl.http_upload(urls=[url], data=data, alpn_proto=proto)
67*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=1, http_status=200, exitcode=0)
68*6236dae4SAndroid Build Coastguard Worker        respdata = open(curl.response_file(0)).readlines()
69*6236dae4SAndroid Build Coastguard Worker        assert respdata == [data]
70*6236dae4SAndroid Build Coastguard Worker
71*6236dae4SAndroid Build Coastguard Worker    # upload large data, check that this is what was echoed
72*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
73*6236dae4SAndroid Build Coastguard Worker    def test_07_02_upload_1_large(self, env: Env, httpd, nghttpx, repeat, proto):
74*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
75*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
76*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
77*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 fails here")
78*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-100k')
79*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
80*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]'
81*6236dae4SAndroid Build Coastguard Worker        r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto)
82*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=1, http_status=200, exitcode=0)
83*6236dae4SAndroid Build Coastguard Worker        indata = open(fdata).readlines()
84*6236dae4SAndroid Build Coastguard Worker        respdata = open(curl.response_file(0)).readlines()
85*6236dae4SAndroid Build Coastguard Worker        assert respdata == indata
86*6236dae4SAndroid Build Coastguard Worker
87*6236dae4SAndroid Build Coastguard Worker    # upload data sequentially, check that they were echoed
88*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
89*6236dae4SAndroid Build Coastguard Worker    def test_07_10_upload_sequential(self, env: Env, httpd, nghttpx, repeat, proto):
90*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
91*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
92*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
93*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 stalls here")
94*6236dae4SAndroid Build Coastguard Worker        count = 20
95*6236dae4SAndroid Build Coastguard Worker        data = '0123456789'
96*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
97*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]'
98*6236dae4SAndroid Build Coastguard Worker        r = curl.http_upload(urls=[url], data=data, alpn_proto=proto)
99*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=count, http_status=200, exitcode=0)
100*6236dae4SAndroid Build Coastguard Worker        for i in range(count):
101*6236dae4SAndroid Build Coastguard Worker            respdata = open(curl.response_file(i)).readlines()
102*6236dae4SAndroid Build Coastguard Worker            assert respdata == [data]
103*6236dae4SAndroid Build Coastguard Worker
104*6236dae4SAndroid Build Coastguard Worker    # upload data parallel, check that they were echoed
105*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['h2', 'h3'])
106*6236dae4SAndroid Build Coastguard Worker    def test_07_11_upload_parallel(self, env: Env, httpd, nghttpx, repeat, proto):
107*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
108*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
109*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
110*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 stalls here")
111*6236dae4SAndroid Build Coastguard Worker        # limit since we use a separate connection in h1
112*6236dae4SAndroid Build Coastguard Worker        count = 20
113*6236dae4SAndroid Build Coastguard Worker        data = '0123456789'
114*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
115*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]'
116*6236dae4SAndroid Build Coastguard Worker        r = curl.http_upload(urls=[url], data=data, alpn_proto=proto,
117*6236dae4SAndroid Build Coastguard Worker                             extra_args=['--parallel'])
118*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=count, http_status=200, exitcode=0)
119*6236dae4SAndroid Build Coastguard Worker        for i in range(count):
120*6236dae4SAndroid Build Coastguard Worker            respdata = open(curl.response_file(i)).readlines()
121*6236dae4SAndroid Build Coastguard Worker            assert respdata == [data]
122*6236dae4SAndroid Build Coastguard Worker
123*6236dae4SAndroid Build Coastguard Worker    # upload large data sequentially, check that this is what was echoed
124*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
125*6236dae4SAndroid Build Coastguard Worker    def test_07_12_upload_seq_large(self, env: Env, httpd, nghttpx, repeat, proto):
126*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
127*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
128*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
129*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 stalls here")
130*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-100k')
131*6236dae4SAndroid Build Coastguard Worker        count = 10
132*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
133*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]'
134*6236dae4SAndroid Build Coastguard Worker        r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto)
135*6236dae4SAndroid Build Coastguard Worker        r.check_response(count=count, http_status=200)
136*6236dae4SAndroid Build Coastguard Worker        indata = open(fdata).readlines()
137*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=count, http_status=200, exitcode=0)
138*6236dae4SAndroid Build Coastguard Worker        for i in range(count):
139*6236dae4SAndroid Build Coastguard Worker            respdata = open(curl.response_file(i)).readlines()
140*6236dae4SAndroid Build Coastguard Worker            assert respdata == indata
141*6236dae4SAndroid Build Coastguard Worker
142*6236dae4SAndroid Build Coastguard Worker    # upload very large data sequentially, check that this is what was echoed
143*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
144*6236dae4SAndroid Build Coastguard Worker    def test_07_13_upload_seq_large(self, env: Env, httpd, nghttpx, repeat, proto):
145*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
146*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
147*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
148*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 stalls here")
149*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-10m')
150*6236dae4SAndroid Build Coastguard Worker        count = 2
151*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
152*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]'
153*6236dae4SAndroid Build Coastguard Worker        r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto)
154*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=count, http_status=200, exitcode=0)
155*6236dae4SAndroid Build Coastguard Worker        indata = open(fdata).readlines()
156*6236dae4SAndroid Build Coastguard Worker        for i in range(count):
157*6236dae4SAndroid Build Coastguard Worker            respdata = open(curl.response_file(i)).readlines()
158*6236dae4SAndroid Build Coastguard Worker            assert respdata == indata
159*6236dae4SAndroid Build Coastguard Worker
160*6236dae4SAndroid Build Coastguard Worker    # upload from stdin, issue #14870
161*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
162*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("indata", [
163*6236dae4SAndroid Build Coastguard Worker        '', '1', '123\n456andsomething\n\n'
164*6236dae4SAndroid Build Coastguard Worker    ])
165*6236dae4SAndroid Build Coastguard Worker    def test_07_14_upload_stdin(self, env: Env, httpd, nghttpx, proto, indata):
166*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
167*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
168*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
169*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 stalls here")
170*6236dae4SAndroid Build Coastguard Worker        count = 1
171*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
172*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put?id=[0-{count-1}]'
173*6236dae4SAndroid Build Coastguard Worker        r = curl.http_put(urls=[url], data=indata, alpn_proto=proto)
174*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=count, http_status=200, exitcode=0)
175*6236dae4SAndroid Build Coastguard Worker        for i in range(count):
176*6236dae4SAndroid Build Coastguard Worker            respdata = open(curl.response_file(i)).readlines()
177*6236dae4SAndroid Build Coastguard Worker            assert respdata == [f'{len(indata)}']
178*6236dae4SAndroid Build Coastguard Worker
179*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
180*6236dae4SAndroid Build Coastguard Worker    def test_07_15_hx_put(self, env: Env, httpd, nghttpx, proto):
181*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
182*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
183*6236dae4SAndroid Build Coastguard Worker        count = 2
184*6236dae4SAndroid Build Coastguard Worker        upload_size = 128*1024
185*6236dae4SAndroid Build Coastguard Worker        url = f'https://localhost:{env.https_port}/curltest/put?id=[0-{count-1}]'
186*6236dae4SAndroid Build Coastguard Worker        client = LocalClient(name='hx-upload', env=env)
187*6236dae4SAndroid Build Coastguard Worker        if not client.exists():
188*6236dae4SAndroid Build Coastguard Worker            pytest.skip(f'example client not built: {client.name}')
189*6236dae4SAndroid Build Coastguard Worker        r = client.run(args=[
190*6236dae4SAndroid Build Coastguard Worker             '-n', f'{count}', '-S', f'{upload_size}', '-V', proto, url
191*6236dae4SAndroid Build Coastguard Worker        ])
192*6236dae4SAndroid Build Coastguard Worker        r.check_exit_code(0)
193*6236dae4SAndroid Build Coastguard Worker        self.check_downloads(client, [f"{upload_size}"], count)
194*6236dae4SAndroid Build Coastguard Worker
195*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
196*6236dae4SAndroid Build Coastguard Worker    def test_07_16_hx_put_reuse(self, env: Env, httpd, nghttpx, proto):
197*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
198*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
199*6236dae4SAndroid Build Coastguard Worker        count = 2
200*6236dae4SAndroid Build Coastguard Worker        upload_size = 128*1024
201*6236dae4SAndroid Build Coastguard Worker        url = f'https://localhost:{env.https_port}/curltest/put?id=[0-{count-1}]'
202*6236dae4SAndroid Build Coastguard Worker        client = LocalClient(name='hx-upload', env=env)
203*6236dae4SAndroid Build Coastguard Worker        if not client.exists():
204*6236dae4SAndroid Build Coastguard Worker            pytest.skip(f'example client not built: {client.name}')
205*6236dae4SAndroid Build Coastguard Worker        r = client.run(args=[
206*6236dae4SAndroid Build Coastguard Worker             '-n', f'{count}', '-S', f'{upload_size}', '-R', '-V', proto, url
207*6236dae4SAndroid Build Coastguard Worker        ])
208*6236dae4SAndroid Build Coastguard Worker        r.check_exit_code(0)
209*6236dae4SAndroid Build Coastguard Worker        self.check_downloads(client, [f"{upload_size}"], count)
210*6236dae4SAndroid Build Coastguard Worker
211*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
212*6236dae4SAndroid Build Coastguard Worker    def test_07_17_hx_post_reuse(self, env: Env, httpd, nghttpx, proto):
213*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
214*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
215*6236dae4SAndroid Build Coastguard Worker        count = 2
216*6236dae4SAndroid Build Coastguard Worker        upload_size = 128*1024
217*6236dae4SAndroid Build Coastguard Worker        url = f'https://localhost:{env.https_port}/curltest/echo?id=[0-{count-1}]'
218*6236dae4SAndroid Build Coastguard Worker        client = LocalClient(name='hx-upload', env=env)
219*6236dae4SAndroid Build Coastguard Worker        if not client.exists():
220*6236dae4SAndroid Build Coastguard Worker            pytest.skip(f'example client not built: {client.name}')
221*6236dae4SAndroid Build Coastguard Worker        r = client.run(args=[
222*6236dae4SAndroid Build Coastguard Worker             '-n', f'{count}', '-M', 'POST', '-S', f'{upload_size}', '-R', '-V', proto, url
223*6236dae4SAndroid Build Coastguard Worker        ])
224*6236dae4SAndroid Build Coastguard Worker        r.check_exit_code(0)
225*6236dae4SAndroid Build Coastguard Worker        self.check_downloads(client, ["x" * upload_size], count)
226*6236dae4SAndroid Build Coastguard Worker
227*6236dae4SAndroid Build Coastguard Worker    # upload data parallel, check that they were echoed
228*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['h2', 'h3'])
229*6236dae4SAndroid Build Coastguard Worker    def test_07_20_upload_parallel(self, env: Env, httpd, nghttpx, repeat, proto):
230*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
231*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
232*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
233*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 stalls here")
234*6236dae4SAndroid Build Coastguard Worker        # limit since we use a separate connection in h1
235*6236dae4SAndroid Build Coastguard Worker        count = 10
236*6236dae4SAndroid Build Coastguard Worker        data = '0123456789'
237*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
238*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]'
239*6236dae4SAndroid Build Coastguard Worker        r = curl.http_upload(urls=[url], data=data, alpn_proto=proto,
240*6236dae4SAndroid Build Coastguard Worker                             extra_args=['--parallel'])
241*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=count, http_status=200, exitcode=0)
242*6236dae4SAndroid Build Coastguard Worker        for i in range(count):
243*6236dae4SAndroid Build Coastguard Worker            respdata = open(curl.response_file(i)).readlines()
244*6236dae4SAndroid Build Coastguard Worker            assert respdata == [data]
245*6236dae4SAndroid Build Coastguard Worker
246*6236dae4SAndroid Build Coastguard Worker    # upload large data parallel, check that this is what was echoed
247*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['h2', 'h3'])
248*6236dae4SAndroid Build Coastguard Worker    def test_07_21_upload_parallel_large(self, env: Env, httpd, nghttpx, repeat, proto):
249*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
250*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
251*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
252*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 stalls here")
253*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-100k')
254*6236dae4SAndroid Build Coastguard Worker        # limit since we use a separate connection in h1
255*6236dae4SAndroid Build Coastguard Worker        count = 10
256*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
257*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]'
258*6236dae4SAndroid Build Coastguard Worker        r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto,
259*6236dae4SAndroid Build Coastguard Worker                             extra_args=['--parallel'])
260*6236dae4SAndroid Build Coastguard Worker        r.check_response(count=count, http_status=200)
261*6236dae4SAndroid Build Coastguard Worker        self.check_download(count, fdata, curl)
262*6236dae4SAndroid Build Coastguard Worker
263*6236dae4SAndroid Build Coastguard Worker    # upload large data parallel to a URL that denies uploads
264*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['h2', 'h3'])
265*6236dae4SAndroid Build Coastguard Worker    def test_07_22_upload_parallel_fail(self, env: Env, httpd, nghttpx, repeat, proto):
266*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
267*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
268*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
269*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 stalls here")
270*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-10m')
271*6236dae4SAndroid Build Coastguard Worker        count = 20
272*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
273*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}'\
274*6236dae4SAndroid Build Coastguard Worker            f'/curltest/tweak?status=400&delay=5ms&chunks=1&body_error=reset&id=[0-{count-1}]'
275*6236dae4SAndroid Build Coastguard Worker        r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto,
276*6236dae4SAndroid Build Coastguard Worker                             extra_args=['--parallel'])
277*6236dae4SAndroid Build Coastguard Worker        exp_exit = 92 if proto == 'h2' else 95
278*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=count, exitcode=exp_exit)
279*6236dae4SAndroid Build Coastguard Worker
280*6236dae4SAndroid Build Coastguard Worker    # PUT 100k
281*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
282*6236dae4SAndroid Build Coastguard Worker    def test_07_30_put_100k(self, env: Env, httpd, nghttpx, repeat, proto):
283*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
284*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
285*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
286*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 fails here")
287*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-100k')
288*6236dae4SAndroid Build Coastguard Worker        count = 1
289*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
290*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put?id=[0-{count-1}]'
291*6236dae4SAndroid Build Coastguard Worker        r = curl.http_put(urls=[url], fdata=fdata, alpn_proto=proto,
292*6236dae4SAndroid Build Coastguard Worker                             extra_args=['--parallel'])
293*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=count, http_status=200, exitcode=0)
294*6236dae4SAndroid Build Coastguard Worker        exp_data = [f'{os.path.getsize(fdata)}']
295*6236dae4SAndroid Build Coastguard Worker        r.check_response(count=count, http_status=200)
296*6236dae4SAndroid Build Coastguard Worker        for i in range(count):
297*6236dae4SAndroid Build Coastguard Worker            respdata = open(curl.response_file(i)).readlines()
298*6236dae4SAndroid Build Coastguard Worker            assert respdata == exp_data
299*6236dae4SAndroid Build Coastguard Worker
300*6236dae4SAndroid Build Coastguard Worker    # PUT 10m
301*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
302*6236dae4SAndroid Build Coastguard Worker    def test_07_31_put_10m(self, env: Env, httpd, nghttpx, repeat, proto):
303*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
304*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
305*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
306*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 fails here")
307*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-10m')
308*6236dae4SAndroid Build Coastguard Worker        count = 1
309*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
310*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put?id=[0-{count-1}]&chunk_delay=2ms'
311*6236dae4SAndroid Build Coastguard Worker        r = curl.http_put(urls=[url], fdata=fdata, alpn_proto=proto,
312*6236dae4SAndroid Build Coastguard Worker                             extra_args=['--parallel'])
313*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=count, http_status=200, exitcode=0)
314*6236dae4SAndroid Build Coastguard Worker        exp_data = [f'{os.path.getsize(fdata)}']
315*6236dae4SAndroid Build Coastguard Worker        r.check_response(count=count, http_status=200)
316*6236dae4SAndroid Build Coastguard Worker        for i in range(count):
317*6236dae4SAndroid Build Coastguard Worker            respdata = open(curl.response_file(i)).readlines()
318*6236dae4SAndroid Build Coastguard Worker            assert respdata == exp_data
319*6236dae4SAndroid Build Coastguard Worker
320*6236dae4SAndroid Build Coastguard Worker    # issue #10591
321*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
322*6236dae4SAndroid Build Coastguard Worker    def test_07_32_issue_10591(self, env: Env, httpd, nghttpx, repeat, proto):
323*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
324*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
325*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
326*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 fails here")
327*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-10m')
328*6236dae4SAndroid Build Coastguard Worker        count = 1
329*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
330*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put?id=[0-{count-1}]'
331*6236dae4SAndroid Build Coastguard Worker        r = curl.http_put(urls=[url], fdata=fdata, alpn_proto=proto)
332*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=count, http_status=200, exitcode=0)
333*6236dae4SAndroid Build Coastguard Worker
334*6236dae4SAndroid Build Coastguard Worker    # issue #11157, upload that is 404'ed by server, needs to terminate
335*6236dae4SAndroid Build Coastguard Worker    # correctly and not time out on sending
336*6236dae4SAndroid Build Coastguard Worker    def test_07_33_issue_11157a(self, env: Env, httpd, nghttpx, repeat):
337*6236dae4SAndroid Build Coastguard Worker        proto = 'h2'
338*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-10m')
339*6236dae4SAndroid Build Coastguard Worker        # send a POST to our PUT handler which will send immediately a 404 back
340*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put'
341*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
342*6236dae4SAndroid Build Coastguard Worker        r = curl.run_direct(with_stats=True, args=[
343*6236dae4SAndroid Build Coastguard Worker            '--resolve', f'{env.authority_for(env.domain1, proto)}:127.0.0.1',
344*6236dae4SAndroid Build Coastguard Worker            '--cacert', env.ca.cert_file,
345*6236dae4SAndroid Build Coastguard Worker            '--request', 'POST',
346*6236dae4SAndroid Build Coastguard Worker            '--max-time', '5', '-v',
347*6236dae4SAndroid Build Coastguard Worker            '--url', url,
348*6236dae4SAndroid Build Coastguard Worker            '--form', 'idList=12345678',
349*6236dae4SAndroid Build Coastguard Worker            '--form', 'pos=top',
350*6236dae4SAndroid Build Coastguard Worker            '--form', 'name=mr_test',
351*6236dae4SAndroid Build Coastguard Worker            '--form', f'fileSource=@{fdata};type=application/pdf',
352*6236dae4SAndroid Build Coastguard Worker        ])
353*6236dae4SAndroid Build Coastguard Worker        assert r.exit_code == 0, f'{r}'
354*6236dae4SAndroid Build Coastguard Worker        r.check_stats(1, 404)
355*6236dae4SAndroid Build Coastguard Worker
356*6236dae4SAndroid Build Coastguard Worker    # issue #11157, send upload that is slowly read in
357*6236dae4SAndroid Build Coastguard Worker    def test_07_33_issue_11157b(self, env: Env, httpd, nghttpx, repeat):
358*6236dae4SAndroid Build Coastguard Worker        proto = 'h2'
359*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-10m')
360*6236dae4SAndroid Build Coastguard Worker        # tell our test PUT handler to read the upload more slowly, so
361*6236dae4SAndroid Build Coastguard Worker        # that the send buffering and transfer loop needs to wait
362*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put?chunk_delay=2ms'
363*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
364*6236dae4SAndroid Build Coastguard Worker        r = curl.run_direct(with_stats=True, args=[
365*6236dae4SAndroid Build Coastguard Worker            '--verbose', '--trace-config', 'ids,time',
366*6236dae4SAndroid Build Coastguard Worker            '--resolve', f'{env.authority_for(env.domain1, proto)}:127.0.0.1',
367*6236dae4SAndroid Build Coastguard Worker            '--cacert', env.ca.cert_file,
368*6236dae4SAndroid Build Coastguard Worker            '--request', 'PUT',
369*6236dae4SAndroid Build Coastguard Worker            '--max-time', '10', '-v',
370*6236dae4SAndroid Build Coastguard Worker            '--url', url,
371*6236dae4SAndroid Build Coastguard Worker            '--form', 'idList=12345678',
372*6236dae4SAndroid Build Coastguard Worker            '--form', 'pos=top',
373*6236dae4SAndroid Build Coastguard Worker            '--form', 'name=mr_test',
374*6236dae4SAndroid Build Coastguard Worker            '--form', f'fileSource=@{fdata};type=application/pdf',
375*6236dae4SAndroid Build Coastguard Worker        ])
376*6236dae4SAndroid Build Coastguard Worker        assert r.exit_code == 0, r.dump_logs()
377*6236dae4SAndroid Build Coastguard Worker        r.check_stats(1, 200)
378*6236dae4SAndroid Build Coastguard Worker
379*6236dae4SAndroid Build Coastguard Worker    def test_07_34_issue_11194(self, env: Env, httpd, nghttpx, repeat):
380*6236dae4SAndroid Build Coastguard Worker        proto = 'h2'
381*6236dae4SAndroid Build Coastguard Worker        # tell our test PUT handler to read the upload more slowly, so
382*6236dae4SAndroid Build Coastguard Worker        # that the send buffering and transfer loop needs to wait
383*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-100k')
384*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put'
385*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
386*6236dae4SAndroid Build Coastguard Worker        r = curl.run_direct(with_stats=True, args=[
387*6236dae4SAndroid Build Coastguard Worker            '--verbose', '--trace-config', 'ids,time',
388*6236dae4SAndroid Build Coastguard Worker            '--resolve', f'{env.authority_for(env.domain1, proto)}:127.0.0.1',
389*6236dae4SAndroid Build Coastguard Worker            '--cacert', env.ca.cert_file,
390*6236dae4SAndroid Build Coastguard Worker            '--request', 'PUT',
391*6236dae4SAndroid Build Coastguard Worker            '--digest', '--user', 'test:test',
392*6236dae4SAndroid Build Coastguard Worker            '--data-binary', f'@{fdata}',
393*6236dae4SAndroid Build Coastguard Worker            '--url', url,
394*6236dae4SAndroid Build Coastguard Worker        ])
395*6236dae4SAndroid Build Coastguard Worker        assert r.exit_code == 0, r.dump_logs()
396*6236dae4SAndroid Build Coastguard Worker        r.check_stats(1, 200)
397*6236dae4SAndroid Build Coastguard Worker
398*6236dae4SAndroid Build Coastguard Worker    # upload large data on a h1 to h2 upgrade
399*6236dae4SAndroid Build Coastguard Worker    def test_07_35_h1_h2_upgrade_upload(self, env: Env, httpd, nghttpx, repeat):
400*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-100k')
401*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
402*6236dae4SAndroid Build Coastguard Worker        url = f'http://{env.domain1}:{env.http_port}/curltest/echo?id=[0-0]'
403*6236dae4SAndroid Build Coastguard Worker        r = curl.http_upload(urls=[url], data=f'@{fdata}', extra_args=[
404*6236dae4SAndroid Build Coastguard Worker            '--http2'
405*6236dae4SAndroid Build Coastguard Worker        ])
406*6236dae4SAndroid Build Coastguard Worker        r.check_response(count=1, http_status=200)
407*6236dae4SAndroid Build Coastguard Worker        # apache does not Upgrade on request with a body
408*6236dae4SAndroid Build Coastguard Worker        assert r.stats[0]['http_version'] == '1.1', f'{r}'
409*6236dae4SAndroid Build Coastguard Worker        indata = open(fdata).readlines()
410*6236dae4SAndroid Build Coastguard Worker        respdata = open(curl.response_file(0)).readlines()
411*6236dae4SAndroid Build Coastguard Worker        assert respdata == indata
412*6236dae4SAndroid Build Coastguard Worker
413*6236dae4SAndroid Build Coastguard Worker    # upload to a 301,302,303 response
414*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("redir", ['301', '302', '303'])
415*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
416*6236dae4SAndroid Build Coastguard Worker    def test_07_36_upload_30x(self, env: Env, httpd, nghttpx, repeat, redir, proto):
417*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
418*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
419*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
420*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 fails here")
421*6236dae4SAndroid Build Coastguard Worker        data = '0123456789' * 10
422*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
423*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo{redir}?id=[0-0]'
424*6236dae4SAndroid Build Coastguard Worker        r = curl.http_upload(urls=[url], data=data, alpn_proto=proto, extra_args=[
425*6236dae4SAndroid Build Coastguard Worker            '-L', '--trace-config', 'http/2,http/3'
426*6236dae4SAndroid Build Coastguard Worker        ])
427*6236dae4SAndroid Build Coastguard Worker        r.check_response(count=1, http_status=200)
428*6236dae4SAndroid Build Coastguard Worker        respdata = open(curl.response_file(0)).readlines()
429*6236dae4SAndroid Build Coastguard Worker        assert respdata == []  # was transformed to a GET
430*6236dae4SAndroid Build Coastguard Worker
431*6236dae4SAndroid Build Coastguard Worker    # upload to a 307 response
432*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
433*6236dae4SAndroid Build Coastguard Worker    def test_07_37_upload_307(self, env: Env, httpd, nghttpx, repeat, proto):
434*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
435*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
436*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
437*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 fails here")
438*6236dae4SAndroid Build Coastguard Worker        data = '0123456789' * 10
439*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
440*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo307?id=[0-0]'
441*6236dae4SAndroid Build Coastguard Worker        r = curl.http_upload(urls=[url], data=data, alpn_proto=proto, extra_args=[
442*6236dae4SAndroid Build Coastguard Worker            '-L', '--trace-config', 'http/2,http/3'
443*6236dae4SAndroid Build Coastguard Worker        ])
444*6236dae4SAndroid Build Coastguard Worker        r.check_response(count=1, http_status=200)
445*6236dae4SAndroid Build Coastguard Worker        respdata = open(curl.response_file(0)).readlines()
446*6236dae4SAndroid Build Coastguard Worker        assert respdata == [data]  # was POST again
447*6236dae4SAndroid Build Coastguard Worker
448*6236dae4SAndroid Build Coastguard Worker    # POST form data, yet another code path in transfer
449*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
450*6236dae4SAndroid Build Coastguard Worker    def test_07_38_form_small(self, env: Env, httpd, nghttpx, repeat, proto):
451*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
452*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
453*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
454*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 fails here")
455*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
456*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]'
457*6236dae4SAndroid Build Coastguard Worker        r = curl.http_form(urls=[url], alpn_proto=proto, form={
458*6236dae4SAndroid Build Coastguard Worker            'name1': 'value1',
459*6236dae4SAndroid Build Coastguard Worker        })
460*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=1, http_status=200, exitcode=0)
461*6236dae4SAndroid Build Coastguard Worker
462*6236dae4SAndroid Build Coastguard Worker    # POST data urlencoded, small enough to be sent with request headers
463*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
464*6236dae4SAndroid Build Coastguard Worker    def test_07_39_post_urlenc_small(self, env: Env, httpd, nghttpx, repeat, proto):
465*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
466*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
467*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
468*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 fails here")
469*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-63k')
470*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
471*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]'
472*6236dae4SAndroid Build Coastguard Worker        r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto, extra_args=[
473*6236dae4SAndroid Build Coastguard Worker            '--trace-config', 'http/2,http/3'
474*6236dae4SAndroid Build Coastguard Worker        ])
475*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=1, http_status=200, exitcode=0)
476*6236dae4SAndroid Build Coastguard Worker        indata = open(fdata).readlines()
477*6236dae4SAndroid Build Coastguard Worker        respdata = open(curl.response_file(0)).readlines()
478*6236dae4SAndroid Build Coastguard Worker        assert respdata == indata
479*6236dae4SAndroid Build Coastguard Worker
480*6236dae4SAndroid Build Coastguard Worker    # POST data urlencoded, large enough to be sent separate from request headers
481*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
482*6236dae4SAndroid Build Coastguard Worker    def test_07_40_post_urlenc_large(self, env: Env, httpd, nghttpx, repeat, proto):
483*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
484*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
485*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
486*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 fails here")
487*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-64k')
488*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
489*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]'
490*6236dae4SAndroid Build Coastguard Worker        r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto, extra_args=[
491*6236dae4SAndroid Build Coastguard Worker            '--trace-config', 'http/2,http/3'
492*6236dae4SAndroid Build Coastguard Worker        ])
493*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=1, http_status=200, exitcode=0)
494*6236dae4SAndroid Build Coastguard Worker        indata = open(fdata).readlines()
495*6236dae4SAndroid Build Coastguard Worker        respdata = open(curl.response_file(0)).readlines()
496*6236dae4SAndroid Build Coastguard Worker        assert respdata == indata
497*6236dae4SAndroid Build Coastguard Worker
498*6236dae4SAndroid Build Coastguard Worker    # POST data urlencoded, small enough to be sent with request headers
499*6236dae4SAndroid Build Coastguard Worker    # and request headers are so large that the first send is larger
500*6236dae4SAndroid Build Coastguard Worker    # than our default upload buffer length (64KB).
501*6236dae4SAndroid Build Coastguard Worker    # Unfixed, this will fail when run with CURL_DBG_SOCK_WBLOCK=80 most
502*6236dae4SAndroid Build Coastguard Worker    # of the time
503*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
504*6236dae4SAndroid Build Coastguard Worker    def test_07_41_post_urlenc_small(self, env: Env, httpd, nghttpx, repeat, proto):
505*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
506*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
507*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
508*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 fails here")
509*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('quiche'):
510*6236dae4SAndroid Build Coastguard Worker            pytest.skip("quiche has CWND issues with large requests")
511*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-63k')
512*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
513*6236dae4SAndroid Build Coastguard Worker        extra_args = ['--trace-config', 'http/2,http/3']
514*6236dae4SAndroid Build Coastguard Worker        # add enough headers so that the first send chunk is > 64KB
515*6236dae4SAndroid Build Coastguard Worker        for i in range(63):
516*6236dae4SAndroid Build Coastguard Worker            extra_args.extend(['-H', f'x{i:02d}: {"y"*1019}'])
517*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]'
518*6236dae4SAndroid Build Coastguard Worker        r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto, extra_args=extra_args)
519*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=1, http_status=200, exitcode=0)
520*6236dae4SAndroid Build Coastguard Worker        indata = open(fdata).readlines()
521*6236dae4SAndroid Build Coastguard Worker        respdata = open(curl.response_file(0)).readlines()
522*6236dae4SAndroid Build Coastguard Worker        assert respdata == indata
523*6236dae4SAndroid Build Coastguard Worker
524*6236dae4SAndroid Build Coastguard Worker    def check_download(self, count, srcfile, curl):
525*6236dae4SAndroid Build Coastguard Worker        for i in range(count):
526*6236dae4SAndroid Build Coastguard Worker            dfile = curl.download_file(i)
527*6236dae4SAndroid Build Coastguard Worker            assert os.path.exists(dfile)
528*6236dae4SAndroid Build Coastguard Worker            if not filecmp.cmp(srcfile, dfile, shallow=False):
529*6236dae4SAndroid Build Coastguard Worker                diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(),
530*6236dae4SAndroid Build Coastguard Worker                                                    b=open(dfile).readlines(),
531*6236dae4SAndroid Build Coastguard Worker                                                    fromfile=srcfile,
532*6236dae4SAndroid Build Coastguard Worker                                                    tofile=dfile,
533*6236dae4SAndroid Build Coastguard Worker                                                    n=1))
534*6236dae4SAndroid Build Coastguard Worker                assert False, f'download {dfile} differs:\n{diff}'
535*6236dae4SAndroid Build Coastguard Worker
536*6236dae4SAndroid Build Coastguard Worker    # upload data, pause, let connection die with an incomplete response
537*6236dae4SAndroid Build Coastguard Worker    # issues #11769 #13260
538*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
539*6236dae4SAndroid Build Coastguard Worker    def test_07_42a_upload_disconnect(self, env: Env, httpd, nghttpx, repeat, proto):
540*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
541*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
542*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
543*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 fails here")
544*6236dae4SAndroid Build Coastguard Worker        client = LocalClient(name='upload-pausing', env=env, timeout=60)
545*6236dae4SAndroid Build Coastguard Worker        if not client.exists():
546*6236dae4SAndroid Build Coastguard Worker            pytest.skip(f'example client not built: {client.name}')
547*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]&die_after=0'
548*6236dae4SAndroid Build Coastguard Worker        r = client.run(['-V', proto, url])
549*6236dae4SAndroid Build Coastguard Worker        if r.exit_code == 18: # PARTIAL_FILE is always ok
550*6236dae4SAndroid Build Coastguard Worker            pass
551*6236dae4SAndroid Build Coastguard Worker        elif proto == 'h2':
552*6236dae4SAndroid Build Coastguard Worker            r.check_exit_code(92)  # CURLE_HTTP2_STREAM also ok
553*6236dae4SAndroid Build Coastguard Worker        elif proto == 'h3':
554*6236dae4SAndroid Build Coastguard Worker            r.check_exit_code(95)  # CURLE_HTTP3 also ok
555*6236dae4SAndroid Build Coastguard Worker        else:
556*6236dae4SAndroid Build Coastguard Worker            r.check_exit_code(18)  # will fail as it should
557*6236dae4SAndroid Build Coastguard Worker
558*6236dae4SAndroid Build Coastguard Worker    # upload data, pause, let connection die without any response at all
559*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
560*6236dae4SAndroid Build Coastguard Worker    def test_07_42b_upload_disconnect(self, env: Env, httpd, nghttpx, repeat, proto):
561*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
562*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
563*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
564*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 fails here")
565*6236dae4SAndroid Build Coastguard Worker        client = LocalClient(name='upload-pausing', env=env, timeout=60)
566*6236dae4SAndroid Build Coastguard Worker        if not client.exists():
567*6236dae4SAndroid Build Coastguard Worker            pytest.skip(f'example client not built: {client.name}')
568*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]&just_die=1'
569*6236dae4SAndroid Build Coastguard Worker        r = client.run(['-V', proto, url])
570*6236dae4SAndroid Build Coastguard Worker        exp_code = 52  # GOT_NOTHING
571*6236dae4SAndroid Build Coastguard Worker        if proto == 'h2' or proto == 'h3':
572*6236dae4SAndroid Build Coastguard Worker            exp_code = 0  # we get a 500 from the server
573*6236dae4SAndroid Build Coastguard Worker        r.check_exit_code(exp_code)  # GOT_NOTHING
574*6236dae4SAndroid Build Coastguard Worker
575*6236dae4SAndroid Build Coastguard Worker    # upload data, pause, let connection die after 100 continue
576*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
577*6236dae4SAndroid Build Coastguard Worker    def test_07_42c_upload_disconnect(self, env: Env, httpd, nghttpx, repeat, proto):
578*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
579*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
580*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
581*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 fails here")
582*6236dae4SAndroid Build Coastguard Worker        client = LocalClient(name='upload-pausing', env=env, timeout=60)
583*6236dae4SAndroid Build Coastguard Worker        if not client.exists():
584*6236dae4SAndroid Build Coastguard Worker            pytest.skip(f'example client not built: {client.name}')
585*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]&die_after_100=1'
586*6236dae4SAndroid Build Coastguard Worker        r = client.run(['-V', proto, url])
587*6236dae4SAndroid Build Coastguard Worker        exp_code = 52  # GOT_NOTHING
588*6236dae4SAndroid Build Coastguard Worker        if proto == 'h2' or proto == 'h3':
589*6236dae4SAndroid Build Coastguard Worker            exp_code = 0  # we get a 500 from the server
590*6236dae4SAndroid Build Coastguard Worker        r.check_exit_code(exp_code)  # GOT_NOTHING
591*6236dae4SAndroid Build Coastguard Worker
592*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
593*6236dae4SAndroid Build Coastguard Worker    def test_07_43_upload_denied(self, env: Env, httpd, nghttpx, repeat, proto):
594*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
595*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
596*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and env.curl_uses_lib('msh3'):
597*6236dae4SAndroid Build Coastguard Worker            pytest.skip("msh3 fails here")
598*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-10m')
599*6236dae4SAndroid Build Coastguard Worker        count = 1
600*6236dae4SAndroid Build Coastguard Worker        max_upload = 128 * 1024
601*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
602*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put?'\
603*6236dae4SAndroid Build Coastguard Worker            f'id=[0-{count-1}]&max_upload={max_upload}'
604*6236dae4SAndroid Build Coastguard Worker        r = curl.http_put(urls=[url], fdata=fdata, alpn_proto=proto,
605*6236dae4SAndroid Build Coastguard Worker                             extra_args=['--trace-config', 'all'])
606*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=count, http_status=413, exitcode=0)
607*6236dae4SAndroid Build Coastguard Worker
608*6236dae4SAndroid Build Coastguard Worker    # speed limited on put handler
609*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
610*6236dae4SAndroid Build Coastguard Worker    def test_07_50_put_speed_limit(self, env: Env, httpd, nghttpx, proto, repeat):
611*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
612*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
613*6236dae4SAndroid Build Coastguard Worker        count = 1
614*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-100k')
615*6236dae4SAndroid Build Coastguard Worker        up_len = 100 * 1024
616*6236dae4SAndroid Build Coastguard Worker        speed_limit = 50 * 1024
617*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
618*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put?id=[0-0]'
619*6236dae4SAndroid Build Coastguard Worker        r = curl.http_put(urls=[url], fdata=fdata, alpn_proto=proto,
620*6236dae4SAndroid Build Coastguard Worker                          with_headers=True, extra_args=[
621*6236dae4SAndroid Build Coastguard Worker            '--limit-rate', f'{speed_limit}'
622*6236dae4SAndroid Build Coastguard Worker        ])
623*6236dae4SAndroid Build Coastguard Worker        r.check_response(count=count, http_status=200)
624*6236dae4SAndroid Build Coastguard Worker        assert r.responses[0]['header']['received-length'] == f'{up_len}', f'{r.responses[0]}'
625*6236dae4SAndroid Build Coastguard Worker        up_speed = r.stats[0]['speed_upload']
626*6236dae4SAndroid Build Coastguard Worker        assert (speed_limit * 0.5) <= up_speed <= (speed_limit * 1.5), f'{r.stats[0]}'
627*6236dae4SAndroid Build Coastguard Worker
628*6236dae4SAndroid Build Coastguard Worker    # speed limited on echo handler
629*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
630*6236dae4SAndroid Build Coastguard Worker    def test_07_51_echo_speed_limit(self, env: Env, httpd, nghttpx, proto, repeat):
631*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
632*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
633*6236dae4SAndroid Build Coastguard Worker        count = 1
634*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-100k')
635*6236dae4SAndroid Build Coastguard Worker        speed_limit = 50 * 1024
636*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
637*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]'
638*6236dae4SAndroid Build Coastguard Worker        r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto,
639*6236dae4SAndroid Build Coastguard Worker                             with_headers=True, extra_args=[
640*6236dae4SAndroid Build Coastguard Worker            '--limit-rate', f'{speed_limit}'
641*6236dae4SAndroid Build Coastguard Worker        ])
642*6236dae4SAndroid Build Coastguard Worker        r.check_response(count=count, http_status=200)
643*6236dae4SAndroid Build Coastguard Worker        up_speed = r.stats[0]['speed_upload']
644*6236dae4SAndroid Build Coastguard Worker        assert (speed_limit * 0.5) <= up_speed <= (speed_limit * 1.5), f'{r.stats[0]}'
645*6236dae4SAndroid Build Coastguard Worker
646*6236dae4SAndroid Build Coastguard Worker    # upload larger data, triggering "Expect: 100-continue" code paths
647*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1'])
648*6236dae4SAndroid Build Coastguard Worker    def test_07_60_upload_exp100(self, env: Env, httpd, nghttpx, repeat, proto):
649*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-1m+')
650*6236dae4SAndroid Build Coastguard Worker        read_delay = 1
651*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
652*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put?id=[0-0]'\
653*6236dae4SAndroid Build Coastguard Worker              f'&read_delay={read_delay}s'
654*6236dae4SAndroid Build Coastguard Worker        r = curl.http_put(urls=[url], fdata=fdata, alpn_proto=proto, extra_args=[
655*6236dae4SAndroid Build Coastguard Worker            '--expect100-timeout', f'{read_delay+1}'
656*6236dae4SAndroid Build Coastguard Worker        ])
657*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=1, http_status=200, exitcode=0)
658*6236dae4SAndroid Build Coastguard Worker
659*6236dae4SAndroid Build Coastguard Worker    # upload larger data, triggering "Expect: 100-continue" code paths
660*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto", ['http/1.1'])
661*6236dae4SAndroid Build Coastguard Worker    def test_07_61_upload_exp100_timeout(self, env: Env, httpd, nghttpx, repeat, proto):
662*6236dae4SAndroid Build Coastguard Worker        fdata = os.path.join(env.gen_dir, 'data-1m+')
663*6236dae4SAndroid Build Coastguard Worker        read_delay = 2
664*6236dae4SAndroid Build Coastguard Worker        curl = CurlClient(env=env)
665*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put?id=[0-0]'\
666*6236dae4SAndroid Build Coastguard Worker              f'&read_delay={read_delay}s'
667*6236dae4SAndroid Build Coastguard Worker        r = curl.http_put(urls=[url], fdata=fdata, alpn_proto=proto, extra_args=[
668*6236dae4SAndroid Build Coastguard Worker            '--expect100-timeout', f'{read_delay-1}'
669*6236dae4SAndroid Build Coastguard Worker        ])
670*6236dae4SAndroid Build Coastguard Worker        r.check_stats(count=1, http_status=200, exitcode=0)
671*6236dae4SAndroid Build Coastguard Worker
672*6236dae4SAndroid Build Coastguard Worker    # nghttpx is the only server we have that supports TLS early data and
673*6236dae4SAndroid Build Coastguard Worker    # has a limit of 16k it announces
674*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx")
675*6236dae4SAndroid Build Coastguard Worker    @pytest.mark.parametrize("proto,upload_size,exp_early", [
676*6236dae4SAndroid Build Coastguard Worker        ['http/1.1', 100, 203],        # headers+body
677*6236dae4SAndroid Build Coastguard Worker        ['http/1.1', 10*1024, 10345],  # headers+body
678*6236dae4SAndroid Build Coastguard Worker        ['http/1.1', 32*1024, 16384],  # headers+body, limited by server max
679*6236dae4SAndroid Build Coastguard Worker        ['h2', 10*1024, 10378],        # headers+body
680*6236dae4SAndroid Build Coastguard Worker        ['h2', 32*1024, 16384],        # headers+body, limited by server max
681*6236dae4SAndroid Build Coastguard Worker        ['h3', 1024, 0],               # earlydata not supported
682*6236dae4SAndroid Build Coastguard Worker    ])
683*6236dae4SAndroid Build Coastguard Worker    def test_07_70_put_earlydata(self, env: Env, httpd, nghttpx, proto, upload_size, exp_early):
684*6236dae4SAndroid Build Coastguard Worker        if not env.curl_uses_lib('gnutls'):
685*6236dae4SAndroid Build Coastguard Worker            pytest.skip('TLS earlydata only implemented in GnuTLS')
686*6236dae4SAndroid Build Coastguard Worker        if proto == 'h3' and not env.have_h3():
687*6236dae4SAndroid Build Coastguard Worker            pytest.skip("h3 not supported")
688*6236dae4SAndroid Build Coastguard Worker        count = 2
689*6236dae4SAndroid Build Coastguard Worker        # we want this test to always connect to nghttpx, since it is
690*6236dae4SAndroid Build Coastguard Worker        # the only server we have that supports TLS earlydata
691*6236dae4SAndroid Build Coastguard Worker        port = env.port_for(proto)
692*6236dae4SAndroid Build Coastguard Worker        if proto != 'h3':
693*6236dae4SAndroid Build Coastguard Worker            port = env.nghttpx_https_port
694*6236dae4SAndroid Build Coastguard Worker        url = f'https://{env.domain1}:{port}/curltest/put?id=[0-{count-1}]'
695*6236dae4SAndroid Build Coastguard Worker        client = LocalClient(name='hx-upload', env=env)
696*6236dae4SAndroid Build Coastguard Worker        if not client.exists():
697*6236dae4SAndroid Build Coastguard Worker            pytest.skip(f'example client not built: {client.name}')
698*6236dae4SAndroid Build Coastguard Worker        r = client.run(args=[
699*6236dae4SAndroid Build Coastguard Worker             '-n', f'{count}',
700*6236dae4SAndroid Build Coastguard Worker             '-e',  # use TLS earlydata
701*6236dae4SAndroid Build Coastguard Worker             '-f',  # forbid reuse of connections
702*6236dae4SAndroid Build Coastguard Worker             '-l',  # announce upload length, no 'Expect: 100'
703*6236dae4SAndroid Build Coastguard Worker             '-S', f'{upload_size}',
704*6236dae4SAndroid Build Coastguard Worker             '-r', f'{env.domain1}:{port}:127.0.0.1',
705*6236dae4SAndroid Build Coastguard Worker             '-V', proto, url
706*6236dae4SAndroid Build Coastguard Worker        ])
707*6236dae4SAndroid Build Coastguard Worker        r.check_exit_code(0)
708*6236dae4SAndroid Build Coastguard Worker        self.check_downloads(client, [f"{upload_size}"], count)
709*6236dae4SAndroid Build Coastguard Worker        earlydata = {}
710*6236dae4SAndroid Build Coastguard Worker        for line in r.trace_lines:
711*6236dae4SAndroid Build Coastguard Worker            m = re.match(r'^\[t-(\d+)] EarlyData: (\d+)', line)
712*6236dae4SAndroid Build Coastguard Worker            if m:
713*6236dae4SAndroid Build Coastguard Worker                earlydata[int(m.group(1))] = int(m.group(2))
714*6236dae4SAndroid Build Coastguard Worker        assert earlydata[0] == 0, f'{earlydata}'
715*6236dae4SAndroid Build Coastguard Worker        assert earlydata[1] == exp_early, f'{earlydata}'
716*6236dae4SAndroid Build Coastguard Worker
717*6236dae4SAndroid Build Coastguard Worker    def check_downloads(self, client, source: List[str], count: int,
718*6236dae4SAndroid Build Coastguard Worker                        complete: bool = True):
719*6236dae4SAndroid Build Coastguard Worker        for i in range(count):
720*6236dae4SAndroid Build Coastguard Worker            dfile = client.download_file(i)
721*6236dae4SAndroid Build Coastguard Worker            assert os.path.exists(dfile)
722*6236dae4SAndroid Build Coastguard Worker            if complete:
723*6236dae4SAndroid Build Coastguard Worker                diff = "".join(difflib.unified_diff(a=source,
724*6236dae4SAndroid Build Coastguard Worker                                                    b=open(dfile).readlines(),
725*6236dae4SAndroid Build Coastguard Worker                                                    fromfile='-',
726*6236dae4SAndroid Build Coastguard Worker                                                    tofile=dfile,
727*6236dae4SAndroid Build Coastguard Worker                                                    n=1))
728*6236dae4SAndroid Build Coastguard Worker                assert not diff, f'download {dfile} differs:\n{diff}'
729