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