1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3#*************************************************************************** 4# _ _ ____ _ 5# Project ___| | | | _ \| | 6# / __| | | | |_) | | 7# | (__| |_| | _ <| |___ 8# \___|\___/|_| \_\_____| 9# 10# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 11# 12# This software is licensed as described in the file COPYING, which 13# you should have received as part of this distribution. The terms 14# are also available at https://curl.se/docs/copyright.html. 15# 16# You may opt to use, copy, modify, merge, publish, distribute and/or sell 17# copies of the Software, and permit persons to whom the Software is 18# furnished to do so, under the terms of the COPYING file. 19# 20# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 21# KIND, either express or implied. 22# 23# SPDX-License-Identifier: curl 24# 25########################################################################### 26# 27import logging 28import os 29import pytest 30 31from testenv import Env, CurlClient 32 33 34log = logging.getLogger(__name__) 35 36 37@pytest.mark.skipif(condition=Env.setup_incomplete(), 38 reason=f"missing: {Env.incomplete_reason()}") 39class TestUpload: 40 41 @pytest.fixture(autouse=True, scope='class') 42 def _class_scope(self, env, httpd, nghttpx): 43 if env.have_h3(): 44 nghttpx.start_if_needed() 45 env.make_data_file(indir=env.gen_dir, fname="data-100k", fsize=100*1024) 46 env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024) 47 httpd.clear_extra_configs() 48 httpd.reload() 49 50 # upload small data, check that this is what was echoed 51 @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 52 def test_07_01_upload_1_small(self, env: Env, httpd, nghttpx, repeat, proto): 53 if proto == 'h3' and not env.have_h3(): 54 pytest.skip("h3 not supported") 55 data = '0123456789' 56 curl = CurlClient(env=env) 57 url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]' 58 r = curl.http_upload(urls=[url], data=data, alpn_proto=proto) 59 assert r.exit_code == 0, f'{r}' 60 r.check_stats(count=1, exp_status=200) 61 respdata = open(curl.response_file(0)).readlines() 62 assert respdata == [data] 63 64 # upload large data, check that this is what was echoed 65 @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 66 def test_07_02_upload_1_large(self, env: Env, httpd, nghttpx, repeat, proto): 67 if proto == 'h3' and not env.have_h3(): 68 pytest.skip("h3 not supported") 69 fdata = os.path.join(env.gen_dir, 'data-100k') 70 curl = CurlClient(env=env) 71 url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]' 72 r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto) 73 assert r.exit_code == 0, f'{r}' 74 r.check_stats(count=1, exp_status=200) 75 indata = open(fdata).readlines() 76 respdata = open(curl.response_file(0)).readlines() 77 assert respdata == indata 78 79 # upload data sequentially, check that they were echoed 80 @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 81 def test_07_10_upload_sequential(self, env: Env, httpd, nghttpx, repeat, proto): 82 if proto == 'h3' and not env.have_h3(): 83 pytest.skip("h3 not supported") 84 count = 50 85 data = '0123456789' 86 curl = CurlClient(env=env) 87 url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]' 88 r = curl.http_upload(urls=[url], data=data, alpn_proto=proto) 89 assert r.exit_code == 0, f'{r}' 90 r.check_stats(count=count, exp_status=200) 91 for i in range(count): 92 respdata = open(curl.response_file(i)).readlines() 93 assert respdata == [data] 94 95 # upload data parallel, check that they were echoed 96 @pytest.mark.parametrize("proto", ['h2', 'h3']) 97 def test_07_11_upload_parallel(self, env: Env, httpd, nghttpx, repeat, proto): 98 if proto == 'h3' and not env.have_h3(): 99 pytest.skip("h3 not supported") 100 # limit since we use a separate connection in h1 101 count = 50 102 data = '0123456789' 103 curl = CurlClient(env=env) 104 url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]' 105 r = curl.http_upload(urls=[url], data=data, alpn_proto=proto, 106 extra_args=['--parallel']) 107 assert r.exit_code == 0, f'{r}' 108 r.check_stats(count=count, exp_status=200) 109 for i in range(count): 110 respdata = open(curl.response_file(i)).readlines() 111 assert respdata == [data] 112 113 # upload large data sequentially, check that this is what was echoed 114 @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 115 def test_07_20_upload_seq_large(self, env: Env, httpd, nghttpx, repeat, proto): 116 if proto == 'h3' and not env.have_h3(): 117 pytest.skip("h3 not supported") 118 fdata = os.path.join(env.gen_dir, 'data-100k') 119 count = 50 120 curl = CurlClient(env=env) 121 url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]' 122 r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto) 123 assert r.exit_code == 0, f'{r}' 124 r.check_stats(count=count, exp_status=200) 125 indata = open(fdata).readlines() 126 r.check_stats(count=count, exp_status=200) 127 for i in range(count): 128 respdata = open(curl.response_file(i)).readlines() 129 assert respdata == indata 130 131 # upload very large data sequentially, check that this is what was echoed 132 @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 133 def test_07_12_upload_seq_large(self, env: Env, httpd, nghttpx, repeat, proto): 134 if proto == 'h3' and not env.have_h3(): 135 pytest.skip("h3 not supported") 136 fdata = os.path.join(env.gen_dir, 'data-10m') 137 count = 2 138 curl = CurlClient(env=env) 139 url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]' 140 r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto) 141 assert r.exit_code == 0, f'{r}' 142 r.check_stats(count=count, exp_status=200) 143 indata = open(fdata).readlines() 144 r.check_stats(count=count, exp_status=200) 145 for i in range(count): 146 respdata = open(curl.response_file(i)).readlines() 147 assert respdata == indata 148 149 # upload data parallel, check that they were echoed 150 @pytest.mark.parametrize("proto", ['h2', 'h3']) 151 def test_07_20_upload_parallel(self, env: Env, httpd, nghttpx, repeat, proto): 152 if proto == 'h3' and not env.have_h3(): 153 pytest.skip("h3 not supported") 154 # limit since we use a separate connection in h1 155 count = 50 156 data = '0123456789' 157 curl = CurlClient(env=env) 158 url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]' 159 r = curl.http_upload(urls=[url], data=data, alpn_proto=proto, 160 extra_args=['--parallel']) 161 assert r.exit_code == 0, f'{r}' 162 r.check_stats(count=count, exp_status=200) 163 for i in range(count): 164 respdata = open(curl.response_file(i)).readlines() 165 assert respdata == [data] 166 167 # upload large data parallel, check that this is what was echoed 168 @pytest.mark.parametrize("proto", ['h2', 'h3']) 169 def test_07_21_upload_parallel_large(self, env: Env, httpd, nghttpx, repeat, proto): 170 if proto == 'h3' and not env.have_h3(): 171 pytest.skip("h3 not supported") 172 if proto == 'h3' and env.curl_uses_lib('quiche'): 173 pytest.skip("quiche stalls on parallel, large uploads, unless --trace is used???") 174 fdata = os.path.join(env.gen_dir, 'data-100k') 175 # limit since we use a separate connection in h1 176 count = 50 177 curl = CurlClient(env=env) 178 url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]' 179 r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto, 180 extra_args=['--parallel']) 181 assert r.exit_code == 0, f'{r}' 182 r.check_stats(count=count, exp_status=200) 183 indata = open(fdata).readlines() 184 r.check_stats(count=count, exp_status=200) 185 for i in range(count): 186 respdata = open(curl.response_file(i)).readlines() 187 assert respdata == indata 188 189 # PUT 100k 190 @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 191 def test_07_30_put_100k(self, env: Env, httpd, nghttpx, repeat, proto): 192 if proto == 'h3' and not env.have_h3(): 193 pytest.skip("h3 not supported") 194 fdata = os.path.join(env.gen_dir, 'data-100k') 195 count = 1 196 curl = CurlClient(env=env) 197 url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put?id=[0-{count-1}]' 198 r = curl.http_put(urls=[url], fdata=fdata, alpn_proto=proto, 199 extra_args=['--parallel']) 200 assert r.exit_code == 0, f'{r}' 201 r.check_stats(count=count, exp_status=200) 202 exp_data = [f'{os.path.getsize(fdata)}'] 203 r.check_stats(count=count, exp_status=200) 204 for i in range(count): 205 respdata = open(curl.response_file(i)).readlines() 206 assert respdata == exp_data 207 208 # PUT 10m 209 @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 210 def test_07_31_put_10m(self, env: Env, httpd, nghttpx, repeat, proto): 211 if proto == 'h3' and not env.have_h3(): 212 pytest.skip("h3 not supported") 213 fdata = os.path.join(env.gen_dir, 'data-10m') 214 count = 1 215 curl = CurlClient(env=env) 216 url = f'https://{env.authority_for(env.domain1, proto)}/curltest/put?id=[0-{count-1}]&chunk_delay=10ms' 217 r = curl.http_put(urls=[url], fdata=fdata, alpn_proto=proto, 218 extra_args=['--parallel']) 219 assert r.exit_code == 0, f'{r}' 220 r.check_stats(count=count, exp_status=200) 221 exp_data = [f'{os.path.getsize(fdata)}'] 222 r.check_stats(count=count, exp_status=200) 223 for i in range(count): 224 respdata = open(curl.response_file(i)).readlines() 225 assert respdata == exp_data 226 227