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 logging 28*6236dae4SAndroid Build Coastguard Workerimport pytest 29*6236dae4SAndroid Build Coastguard Worker 30*6236dae4SAndroid Build Coastguard Workerfrom testenv import Env 31*6236dae4SAndroid Build Coastguard Workerfrom testenv import CurlClient 32*6236dae4SAndroid Build Coastguard Worker 33*6236dae4SAndroid Build Coastguard Worker 34*6236dae4SAndroid Build Coastguard Workerlog = logging.getLogger(__name__) 35*6236dae4SAndroid Build Coastguard Worker 36*6236dae4SAndroid Build Coastguard Worker 37*6236dae4SAndroid Build Coastguard Workerclass TestBasic: 38*6236dae4SAndroid Build Coastguard Worker 39*6236dae4SAndroid Build Coastguard Worker @pytest.fixture(autouse=True, scope='class') 40*6236dae4SAndroid Build Coastguard Worker def _class_scope(self, env, nghttpx): 41*6236dae4SAndroid Build Coastguard Worker if env.have_h3(): 42*6236dae4SAndroid Build Coastguard Worker nghttpx.start_if_needed() 43*6236dae4SAndroid Build Coastguard Worker 44*6236dae4SAndroid Build Coastguard Worker # simple http: GET 45*6236dae4SAndroid Build Coastguard Worker def test_01_01_http_get(self, env: Env, httpd): 46*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 47*6236dae4SAndroid Build Coastguard Worker url = f'http://{env.domain1}:{env.http_port}/data.json' 48*6236dae4SAndroid Build Coastguard Worker r = curl.http_get(url=url) 49*6236dae4SAndroid Build Coastguard Worker r.check_response(http_status=200) 50*6236dae4SAndroid Build Coastguard Worker assert r.json['server'] == env.domain1 51*6236dae4SAndroid Build Coastguard Worker 52*6236dae4SAndroid Build Coastguard Worker # simple https: GET, any http version 53*6236dae4SAndroid Build Coastguard Worker @pytest.mark.skipif(condition=not Env.have_ssl_curl(), reason="curl without SSL") 54*6236dae4SAndroid Build Coastguard Worker def test_01_02_https_get(self, env: Env, httpd): 55*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 56*6236dae4SAndroid Build Coastguard Worker url = f'https://{env.domain1}:{env.https_port}/data.json' 57*6236dae4SAndroid Build Coastguard Worker r = curl.http_get(url=url) 58*6236dae4SAndroid Build Coastguard Worker r.check_response(http_status=200) 59*6236dae4SAndroid Build Coastguard Worker assert r.json['server'] == env.domain1 60*6236dae4SAndroid Build Coastguard Worker 61*6236dae4SAndroid Build Coastguard Worker # simple https: GET, h2 wanted and got 62*6236dae4SAndroid Build Coastguard Worker @pytest.mark.skipif(condition=not Env.have_ssl_curl(), reason="curl without SSL") 63*6236dae4SAndroid Build Coastguard Worker def test_01_03_h2_get(self, env: Env, httpd): 64*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 65*6236dae4SAndroid Build Coastguard Worker url = f'https://{env.domain1}:{env.https_port}/data.json' 66*6236dae4SAndroid Build Coastguard Worker r = curl.http_get(url=url, extra_args=['--http2']) 67*6236dae4SAndroid Build Coastguard Worker r.check_response(http_status=200, protocol='HTTP/2') 68*6236dae4SAndroid Build Coastguard Worker assert r.json['server'] == env.domain1 69*6236dae4SAndroid Build Coastguard Worker 70*6236dae4SAndroid Build Coastguard Worker # simple https: GET, h2 unsupported, fallback to h1 71*6236dae4SAndroid Build Coastguard Worker @pytest.mark.skipif(condition=not Env.have_ssl_curl(), reason="curl without SSL") 72*6236dae4SAndroid Build Coastguard Worker def test_01_04_h2_unsupported(self, env: Env, httpd): 73*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 74*6236dae4SAndroid Build Coastguard Worker url = f'https://{env.domain2}:{env.https_port}/data.json' 75*6236dae4SAndroid Build Coastguard Worker r = curl.http_get(url=url, extra_args=['--http2']) 76*6236dae4SAndroid Build Coastguard Worker r.check_response(http_status=200, protocol='HTTP/1.1') 77*6236dae4SAndroid Build Coastguard Worker assert r.json['server'] == env.domain2 78*6236dae4SAndroid Build Coastguard Worker 79*6236dae4SAndroid Build Coastguard Worker # simple h3: GET, want h3 and get it 80*6236dae4SAndroid Build Coastguard Worker @pytest.mark.skipif(condition=not Env.have_h3(), reason="h3 not supported") 81*6236dae4SAndroid Build Coastguard Worker def test_01_05_h3_get(self, env: Env, httpd, nghttpx): 82*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 83*6236dae4SAndroid Build Coastguard Worker url = f'https://{env.domain1}:{env.h3_port}/data.json' 84*6236dae4SAndroid Build Coastguard Worker r = curl.http_get(url=url, extra_args=['--http3-only']) 85*6236dae4SAndroid Build Coastguard Worker r.check_response(http_status=200, protocol='HTTP/3') 86*6236dae4SAndroid Build Coastguard Worker assert r.json['server'] == env.domain1 87*6236dae4SAndroid Build Coastguard Worker 88*6236dae4SAndroid Build Coastguard Worker # simple download, check connect/handshake timings 89*6236dae4SAndroid Build Coastguard Worker @pytest.mark.skipif(condition=not Env.have_ssl_curl(), reason="curl without SSL") 90*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 91*6236dae4SAndroid Build Coastguard Worker def test_01_06_timings(self, env: Env, httpd, nghttpx, repeat, proto): 92*6236dae4SAndroid Build Coastguard Worker if proto == 'h3' and not env.have_h3(): 93*6236dae4SAndroid Build Coastguard Worker pytest.skip("h3 not supported") 94*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 95*6236dae4SAndroid Build Coastguard Worker url = f'https://{env.authority_for(env.domain1, proto)}/data.json' 96*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[url], alpn_proto=proto, with_stats=True) 97*6236dae4SAndroid Build Coastguard Worker r.check_stats(http_status=200, count=1, 98*6236dae4SAndroid Build Coastguard Worker remote_port=env.port_for(alpn_proto=proto), 99*6236dae4SAndroid Build Coastguard Worker remote_ip='127.0.0.1') 100*6236dae4SAndroid Build Coastguard Worker assert r.stats[0]['time_connect'] > 0, f'{r.stats[0]}' 101*6236dae4SAndroid Build Coastguard Worker assert r.stats[0]['time_appconnect'] > 0, f'{r.stats[0]}' 102*6236dae4SAndroid Build Coastguard Worker 103*6236dae4SAndroid Build Coastguard Worker # simple https: HEAD 104*6236dae4SAndroid Build Coastguard Worker @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3']) 105*6236dae4SAndroid Build Coastguard Worker @pytest.mark.skipif(condition=not Env.have_ssl_curl(), reason="curl without SSL") 106*6236dae4SAndroid Build Coastguard Worker def test_01_07_head(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 curl = CurlClient(env=env) 110*6236dae4SAndroid Build Coastguard Worker url = f'https://{env.authority_for(env.domain1, proto)}/data.json' 111*6236dae4SAndroid Build Coastguard Worker r = curl.http_download(urls=[url], with_stats=True, with_headers=True, 112*6236dae4SAndroid Build Coastguard Worker extra_args=['-I']) 113*6236dae4SAndroid Build Coastguard Worker r.check_stats(http_status=200, count=1, exitcode=0, 114*6236dae4SAndroid Build Coastguard Worker remote_port=env.port_for(alpn_proto=proto), 115*6236dae4SAndroid Build Coastguard Worker remote_ip='127.0.0.1') 116*6236dae4SAndroid Build Coastguard Worker # got the Conten-Length: header, but did not download anything 117*6236dae4SAndroid Build Coastguard Worker assert r.responses[0]['header']['content-length'] == '30', f'{r.responses[0]}' 118*6236dae4SAndroid Build Coastguard Worker assert r.stats[0]['size_download'] == 0, f'{r.stats[0]}' 119*6236dae4SAndroid Build Coastguard Worker 120*6236dae4SAndroid Build Coastguard Worker # http: GET for HTTP/2, see Upgrade:, 101 switch 121*6236dae4SAndroid Build Coastguard Worker def test_01_08_h2_upgrade(self, env: Env, httpd): 122*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 123*6236dae4SAndroid Build Coastguard Worker url = f'http://{env.domain1}:{env.http_port}/data.json' 124*6236dae4SAndroid Build Coastguard Worker r = curl.http_get(url=url, extra_args=['--http2']) 125*6236dae4SAndroid Build Coastguard Worker r.check_exit_code(0) 126*6236dae4SAndroid Build Coastguard Worker assert len(r.responses) == 2, f'{r.responses}' 127*6236dae4SAndroid Build Coastguard Worker assert r.responses[0]['status'] == 101, f'{r.responses[0]}' 128*6236dae4SAndroid Build Coastguard Worker assert r.responses[1]['status'] == 200, f'{r.responses[1]}' 129*6236dae4SAndroid Build Coastguard Worker assert r.responses[1]['protocol'] == 'HTTP/2', f'{r.responses[1]}' 130*6236dae4SAndroid Build Coastguard Worker assert r.json['server'] == env.domain1 131*6236dae4SAndroid Build Coastguard Worker 132*6236dae4SAndroid Build Coastguard Worker # http: GET for HTTP/2 with prior knowledge 133*6236dae4SAndroid Build Coastguard Worker def test_01_09_h2_prior_knowledge(self, env: Env, httpd): 134*6236dae4SAndroid Build Coastguard Worker curl = CurlClient(env=env) 135*6236dae4SAndroid Build Coastguard Worker url = f'http://{env.domain1}:{env.http_port}/data.json' 136*6236dae4SAndroid Build Coastguard Worker r = curl.http_get(url=url, extra_args=['--http2-prior-knowledge']) 137*6236dae4SAndroid Build Coastguard Worker r.check_exit_code(0) 138*6236dae4SAndroid Build Coastguard Worker assert len(r.responses) == 1, f'{r.responses}' 139*6236dae4SAndroid Build Coastguard Worker assert r.response['status'] == 200, f'{r.responsw}' 140*6236dae4SAndroid Build Coastguard Worker assert r.response['protocol'] == 'HTTP/2', f'{r.response}' 141*6236dae4SAndroid Build Coastguard Worker assert r.json['server'] == env.domain1 142