xref: /curl/tests/http/test_06_eyeballs.py (revision ce3dce90)
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 json
28import logging
29from typing import Optional, Tuple, List, Dict
30import pytest
31
32from testenv import Env, CurlClient, ExecResult
33
34
35log = logging.getLogger(__name__)
36
37
38class TestEyeballs:
39
40    @pytest.fixture(autouse=True, scope='class')
41    def _class_scope(self, env, httpd, nghttpx):
42        if env.have_h3():
43            nghttpx.start_if_needed()
44        httpd.clear_extra_configs()
45        httpd.reload()
46
47    # download using only HTTP/3 on working server
48    @pytest.mark.skipif(condition=not Env.have_h3(), reason=f"missing HTTP/3 support")
49    def test_06_01_h3_only(self, env: Env, httpd, nghttpx, repeat):
50        curl = CurlClient(env=env)
51        urln = f'https://{env.authority_for(env.domain1, "h3")}/data.json'
52        r = curl.http_download(urls=[urln], extra_args=['--http3-only'])
53        r.check_response(count=1, http_status=200)
54        assert r.stats[0]['http_version'] == '3'
55
56    # download using only HTTP/3 on missing server
57    @pytest.mark.skipif(condition=not Env.have_h3(), reason=f"missing HTTP/3 support")
58    def test_06_02_h3_only(self, env: Env, httpd, nghttpx, repeat):
59        nghttpx.stop_if_running()
60        curl = CurlClient(env=env)
61        urln = f'https://{env.authority_for(env.domain1, "h3")}/data.json'
62        r = curl.http_download(urls=[urln], extra_args=['--http3-only'])
63        r.check_response(exitcode=7, http_status=None)
64
65    # download using HTTP/3 on missing server with fallback on h2
66    @pytest.mark.skipif(condition=not Env.have_h3(), reason=f"missing HTTP/3 support")
67    def test_06_03_h3_fallback_h2(self, env: Env, httpd, nghttpx, repeat):
68        nghttpx.stop_if_running()
69        curl = CurlClient(env=env)
70        urln = f'https://{env.authority_for(env.domain1, "h3")}/data.json'
71        r = curl.http_download(urls=[urln], extra_args=['--http3'])
72        r.check_response(count=1, http_status=200)
73        assert r.stats[0]['http_version'] == '2'
74
75    # download using HTTP/3 on missing server with fallback on http/1.1
76    @pytest.mark.skipif(condition=not Env.have_h3(), reason=f"missing HTTP/3 support")
77    def test_06_04_h3_fallback_h1(self, env: Env, httpd, nghttpx, repeat):
78        nghttpx.stop_if_running()
79        curl = CurlClient(env=env)
80        urln = f'https://{env.authority_for(env.domain2, "h3")}/data.json'
81        r = curl.http_download(urls=[urln], extra_args=['--http3'])
82        r.check_response(count=1, http_status=200)
83        assert r.stats[0]['http_version'] == '1.1'
84
85    # make a successful https: transfer and observer the timer stats
86    def test_06_10_stats_success(self, env: Env, httpd, nghttpx, repeat):
87        curl = CurlClient(env=env)
88        urln = f'https://{env.authority_for(env.domain1, "h2")}/data.json'
89        r = curl.http_download(urls=[urln])
90        r.check_response(count=1, http_status=200)
91        assert r.stats[0]['time_connect'] > 0.0
92        assert r.stats[0]['time_appconnect'] > 0.0
93
94    # make https: to a hostname that tcp connects, but will not verify
95    def test_06_11_stats_fail_verify(self, env: Env, httpd, nghttpx, repeat):
96        curl = CurlClient(env=env)
97        urln = f'https://not-valid.com:{env.https_port}/data.json'
98        r = curl.http_download(urls=[urln], extra_args=[
99            '--resolve', f'not-valid.com:{env.https_port}:127.0.0.1'
100        ])
101        r.check_response(count=1, http_status=0, exitcode=False)
102        assert r.stats[0]['time_connect'] > 0.0    # was tcp connected
103        assert r.stats[0]['time_appconnect'] == 0  # but not SSL verified
104
105    # make https: to an invalid address
106    def test_06_12_stats_fail_tcp(self, env: Env, httpd, nghttpx, repeat):
107        curl = CurlClient(env=env)
108        urln = f'https://not-valid.com:1/data.json'
109        r = curl.http_download(urls=[urln], extra_args=[
110            '--resolve', f'not-valid.com:{1}:127.0.0.1'
111        ])
112        r.check_response(count=1, http_status=None, exitcode=False)
113        assert r.stats[0]['time_connect'] == 0     # no one should have listened
114        assert r.stats[0]['time_appconnect'] == 0  # did not happen either
115