xref: /aosp_15_r20/external/curl/tests/http/testenv/env.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 logging
28*6236dae4SAndroid Build Coastguard Workerimport os
29*6236dae4SAndroid Build Coastguard Workerimport re
30*6236dae4SAndroid Build Coastguard Workerimport shutil
31*6236dae4SAndroid Build Coastguard Workerimport socket
32*6236dae4SAndroid Build Coastguard Workerimport subprocess
33*6236dae4SAndroid Build Coastguard Workerimport tempfile
34*6236dae4SAndroid Build Coastguard Workerfrom configparser import ConfigParser, ExtendedInterpolation
35*6236dae4SAndroid Build Coastguard Workerfrom typing import Optional
36*6236dae4SAndroid Build Coastguard Worker
37*6236dae4SAndroid Build Coastguard Workerfrom .certs import CertificateSpec, Credentials, TestCA
38*6236dae4SAndroid Build Coastguard Workerfrom .ports import alloc_ports
39*6236dae4SAndroid Build Coastguard Worker
40*6236dae4SAndroid Build Coastguard Worker
41*6236dae4SAndroid Build Coastguard Workerlog = logging.getLogger(__name__)
42*6236dae4SAndroid Build Coastguard Worker
43*6236dae4SAndroid Build Coastguard Worker
44*6236dae4SAndroid Build Coastguard Workerdef init_config_from(conf_path):
45*6236dae4SAndroid Build Coastguard Worker    if os.path.isfile(conf_path):
46*6236dae4SAndroid Build Coastguard Worker        config = ConfigParser(interpolation=ExtendedInterpolation())
47*6236dae4SAndroid Build Coastguard Worker        config.read(conf_path)
48*6236dae4SAndroid Build Coastguard Worker        return config
49*6236dae4SAndroid Build Coastguard Worker    return None
50*6236dae4SAndroid Build Coastguard Worker
51*6236dae4SAndroid Build Coastguard Worker
52*6236dae4SAndroid Build Coastguard WorkerTESTS_HTTPD_PATH = os.path.dirname(os.path.dirname(__file__))
53*6236dae4SAndroid Build Coastguard WorkerTOP_PATH = os.path.join(os.getcwd(), os.path.pardir)
54*6236dae4SAndroid Build Coastguard WorkerDEF_CONFIG = init_config_from(os.path.join(TOP_PATH, 'tests', 'http', 'config.ini'))
55*6236dae4SAndroid Build Coastguard WorkerCURL = os.path.join(TOP_PATH, 'src', 'curl')
56*6236dae4SAndroid Build Coastguard Worker
57*6236dae4SAndroid Build Coastguard Worker
58*6236dae4SAndroid Build Coastguard Workerclass EnvConfig:
59*6236dae4SAndroid Build Coastguard Worker
60*6236dae4SAndroid Build Coastguard Worker    def __init__(self):
61*6236dae4SAndroid Build Coastguard Worker        self.tests_dir = TESTS_HTTPD_PATH
62*6236dae4SAndroid Build Coastguard Worker        self.gen_dir = os.path.join(self.tests_dir, 'gen')
63*6236dae4SAndroid Build Coastguard Worker        self.project_dir = os.path.dirname(os.path.dirname(self.tests_dir))
64*6236dae4SAndroid Build Coastguard Worker        self.build_dir = TOP_PATH
65*6236dae4SAndroid Build Coastguard Worker        self.config = DEF_CONFIG
66*6236dae4SAndroid Build Coastguard Worker        # check cur and its features
67*6236dae4SAndroid Build Coastguard Worker        self.curl = CURL
68*6236dae4SAndroid Build Coastguard Worker        if 'CURL' in os.environ:
69*6236dae4SAndroid Build Coastguard Worker            self.curl = os.environ['CURL']
70*6236dae4SAndroid Build Coastguard Worker        self.curl_props = {
71*6236dae4SAndroid Build Coastguard Worker            'version_string': '',
72*6236dae4SAndroid Build Coastguard Worker            'version': '',
73*6236dae4SAndroid Build Coastguard Worker            'os': '',
74*6236dae4SAndroid Build Coastguard Worker            'fullname': '',
75*6236dae4SAndroid Build Coastguard Worker            'features_string': '',
76*6236dae4SAndroid Build Coastguard Worker            'features': set(),
77*6236dae4SAndroid Build Coastguard Worker            'protocols_string': '',
78*6236dae4SAndroid Build Coastguard Worker            'protocols': set(),
79*6236dae4SAndroid Build Coastguard Worker            'libs': set(),
80*6236dae4SAndroid Build Coastguard Worker            'lib_versions': set(),
81*6236dae4SAndroid Build Coastguard Worker        }
82*6236dae4SAndroid Build Coastguard Worker        self.curl_is_debug = False
83*6236dae4SAndroid Build Coastguard Worker        self.curl_protos = []
84*6236dae4SAndroid Build Coastguard Worker        p = subprocess.run(args=[self.curl, '-V'],
85*6236dae4SAndroid Build Coastguard Worker                           capture_output=True, text=True)
86*6236dae4SAndroid Build Coastguard Worker        if p.returncode != 0:
87*6236dae4SAndroid Build Coastguard Worker            raise RuntimeError(f'{self.curl} -V failed with exit code: {p.returncode}')
88*6236dae4SAndroid Build Coastguard Worker        if p.stderr.startswith('WARNING:'):
89*6236dae4SAndroid Build Coastguard Worker            self.curl_is_debug = True
90*6236dae4SAndroid Build Coastguard Worker        for line in p.stdout.splitlines(keepends=False):
91*6236dae4SAndroid Build Coastguard Worker            if line.startswith('curl '):
92*6236dae4SAndroid Build Coastguard Worker                self.curl_props['version_string'] = line
93*6236dae4SAndroid Build Coastguard Worker                m = re.match(r'^curl (?P<version>\S+) (?P<os>\S+) (?P<libs>.*)$', line)
94*6236dae4SAndroid Build Coastguard Worker                if m:
95*6236dae4SAndroid Build Coastguard Worker                    self.curl_props['fullname'] = m.group(0)
96*6236dae4SAndroid Build Coastguard Worker                    self.curl_props['version'] = m.group('version')
97*6236dae4SAndroid Build Coastguard Worker                    self.curl_props['os'] = m.group('os')
98*6236dae4SAndroid Build Coastguard Worker                    self.curl_props['lib_versions'] = {
99*6236dae4SAndroid Build Coastguard Worker                        lib.lower() for lib in m.group('libs').split(' ')
100*6236dae4SAndroid Build Coastguard Worker                    }
101*6236dae4SAndroid Build Coastguard Worker                    self.curl_props['libs'] = {
102*6236dae4SAndroid Build Coastguard Worker                        re.sub(r'/[a-z0-9.-]*', '', lib) for lib in self.curl_props['lib_versions']
103*6236dae4SAndroid Build Coastguard Worker                    }
104*6236dae4SAndroid Build Coastguard Worker            if line.startswith('Features: '):
105*6236dae4SAndroid Build Coastguard Worker                self.curl_props['features_string'] = line[10:]
106*6236dae4SAndroid Build Coastguard Worker                self.curl_props['features'] = {
107*6236dae4SAndroid Build Coastguard Worker                    feat.lower() for feat in line[10:].split(' ')
108*6236dae4SAndroid Build Coastguard Worker                }
109*6236dae4SAndroid Build Coastguard Worker            if line.startswith('Protocols: '):
110*6236dae4SAndroid Build Coastguard Worker                self.curl_props['protocols_string'] = line[11:]
111*6236dae4SAndroid Build Coastguard Worker                self.curl_props['protocols'] = {
112*6236dae4SAndroid Build Coastguard Worker                    prot.lower() for prot in line[11:].split(' ')
113*6236dae4SAndroid Build Coastguard Worker                }
114*6236dae4SAndroid Build Coastguard Worker
115*6236dae4SAndroid Build Coastguard Worker        self.ports = alloc_ports(port_specs={
116*6236dae4SAndroid Build Coastguard Worker            'ftp': socket.SOCK_STREAM,
117*6236dae4SAndroid Build Coastguard Worker            'ftps': socket.SOCK_STREAM,
118*6236dae4SAndroid Build Coastguard Worker            'http': socket.SOCK_STREAM,
119*6236dae4SAndroid Build Coastguard Worker            'https': socket.SOCK_STREAM,
120*6236dae4SAndroid Build Coastguard Worker            'nghttpx_https': socket.SOCK_STREAM,
121*6236dae4SAndroid Build Coastguard Worker            'proxy': socket.SOCK_STREAM,
122*6236dae4SAndroid Build Coastguard Worker            'proxys': socket.SOCK_STREAM,
123*6236dae4SAndroid Build Coastguard Worker            'h2proxys': socket.SOCK_STREAM,
124*6236dae4SAndroid Build Coastguard Worker            'caddy': socket.SOCK_STREAM,
125*6236dae4SAndroid Build Coastguard Worker            'caddys': socket.SOCK_STREAM,
126*6236dae4SAndroid Build Coastguard Worker            'ws': socket.SOCK_STREAM,
127*6236dae4SAndroid Build Coastguard Worker        })
128*6236dae4SAndroid Build Coastguard Worker        self.httpd = self.config['httpd']['httpd']
129*6236dae4SAndroid Build Coastguard Worker        self.apachectl = self.config['httpd']['apachectl']
130*6236dae4SAndroid Build Coastguard Worker        self.apxs = self.config['httpd']['apxs']
131*6236dae4SAndroid Build Coastguard Worker        if len(self.apxs) == 0:
132*6236dae4SAndroid Build Coastguard Worker            self.apxs = None
133*6236dae4SAndroid Build Coastguard Worker        self._httpd_version = None
134*6236dae4SAndroid Build Coastguard Worker
135*6236dae4SAndroid Build Coastguard Worker        self.examples_pem = {
136*6236dae4SAndroid Build Coastguard Worker            'key': 'xxx',
137*6236dae4SAndroid Build Coastguard Worker            'cert': 'xxx',
138*6236dae4SAndroid Build Coastguard Worker        }
139*6236dae4SAndroid Build Coastguard Worker        self.htdocs_dir = os.path.join(self.gen_dir, 'htdocs')
140*6236dae4SAndroid Build Coastguard Worker        self.tld = 'http.curl.se'
141*6236dae4SAndroid Build Coastguard Worker        self.domain1 = f"one.{self.tld}"
142*6236dae4SAndroid Build Coastguard Worker        self.domain1brotli = f"brotli.one.{self.tld}"
143*6236dae4SAndroid Build Coastguard Worker        self.domain2 = f"two.{self.tld}"
144*6236dae4SAndroid Build Coastguard Worker        self.ftp_domain = f"ftp.{self.tld}"
145*6236dae4SAndroid Build Coastguard Worker        self.proxy_domain = f"proxy.{self.tld}"
146*6236dae4SAndroid Build Coastguard Worker        self.cert_specs = [
147*6236dae4SAndroid Build Coastguard Worker            CertificateSpec(domains=[self.domain1, self.domain1brotli, 'localhost', '127.0.0.1'], key_type='rsa2048'),
148*6236dae4SAndroid Build Coastguard Worker            CertificateSpec(domains=[self.domain2], key_type='rsa2048'),
149*6236dae4SAndroid Build Coastguard Worker            CertificateSpec(domains=[self.ftp_domain], key_type='rsa2048'),
150*6236dae4SAndroid Build Coastguard Worker            CertificateSpec(domains=[self.proxy_domain, '127.0.0.1'], key_type='rsa2048'),
151*6236dae4SAndroid Build Coastguard Worker            CertificateSpec(name="clientsX", sub_specs=[
152*6236dae4SAndroid Build Coastguard Worker               CertificateSpec(name="user1", client=True),
153*6236dae4SAndroid Build Coastguard Worker            ]),
154*6236dae4SAndroid Build Coastguard Worker        ]
155*6236dae4SAndroid Build Coastguard Worker
156*6236dae4SAndroid Build Coastguard Worker        self.nghttpx = self.config['nghttpx']['nghttpx']
157*6236dae4SAndroid Build Coastguard Worker        if len(self.nghttpx.strip()) == 0:
158*6236dae4SAndroid Build Coastguard Worker            self.nghttpx = None
159*6236dae4SAndroid Build Coastguard Worker        self._nghttpx_version = None
160*6236dae4SAndroid Build Coastguard Worker        self.nghttpx_with_h3 = False
161*6236dae4SAndroid Build Coastguard Worker        if self.nghttpx is not None:
162*6236dae4SAndroid Build Coastguard Worker            p = subprocess.run(args=[self.nghttpx, '-v'],
163*6236dae4SAndroid Build Coastguard Worker                               capture_output=True, text=True)
164*6236dae4SAndroid Build Coastguard Worker            if p.returncode != 0:
165*6236dae4SAndroid Build Coastguard Worker                # not a working nghttpx
166*6236dae4SAndroid Build Coastguard Worker                self.nghttpx = None
167*6236dae4SAndroid Build Coastguard Worker            else:
168*6236dae4SAndroid Build Coastguard Worker                self._nghttpx_version = re.sub(r'^nghttpx\s*', '', p.stdout.strip())
169*6236dae4SAndroid Build Coastguard Worker                self.nghttpx_with_h3 = re.match(r'.* nghttp3/.*', p.stdout.strip()) is not None
170*6236dae4SAndroid Build Coastguard Worker                log.debug(f'nghttpx -v: {p.stdout}')
171*6236dae4SAndroid Build Coastguard Worker
172*6236dae4SAndroid Build Coastguard Worker        self.caddy = self.config['caddy']['caddy']
173*6236dae4SAndroid Build Coastguard Worker        self._caddy_version = None
174*6236dae4SAndroid Build Coastguard Worker        if len(self.caddy.strip()) == 0:
175*6236dae4SAndroid Build Coastguard Worker            self.caddy = None
176*6236dae4SAndroid Build Coastguard Worker        if self.caddy is not None:
177*6236dae4SAndroid Build Coastguard Worker            try:
178*6236dae4SAndroid Build Coastguard Worker                p = subprocess.run(args=[self.caddy, 'version'],
179*6236dae4SAndroid Build Coastguard Worker                                   capture_output=True, text=True)
180*6236dae4SAndroid Build Coastguard Worker                if p.returncode != 0:
181*6236dae4SAndroid Build Coastguard Worker                    # not a working caddy
182*6236dae4SAndroid Build Coastguard Worker                    self.caddy = None
183*6236dae4SAndroid Build Coastguard Worker                m = re.match(r'v?(\d+\.\d+\.\d+).*', p.stdout)
184*6236dae4SAndroid Build Coastguard Worker                if m:
185*6236dae4SAndroid Build Coastguard Worker                    self._caddy_version = m.group(1)
186*6236dae4SAndroid Build Coastguard Worker                else:
187*6236dae4SAndroid Build Coastguard Worker                    raise RuntimeError(f'Unable to determine cadd version from: {p.stdout}')
188*6236dae4SAndroid Build Coastguard Worker            # TODO: specify specific exceptions here
189*6236dae4SAndroid Build Coastguard Worker            except:  # noqa: E722
190*6236dae4SAndroid Build Coastguard Worker                self.caddy = None
191*6236dae4SAndroid Build Coastguard Worker
192*6236dae4SAndroid Build Coastguard Worker        self.vsftpd = self.config['vsftpd']['vsftpd']
193*6236dae4SAndroid Build Coastguard Worker        self._vsftpd_version = None
194*6236dae4SAndroid Build Coastguard Worker        if self.vsftpd is not None:
195*6236dae4SAndroid Build Coastguard Worker            try:
196*6236dae4SAndroid Build Coastguard Worker                with tempfile.TemporaryFile('w+') as tmp:
197*6236dae4SAndroid Build Coastguard Worker                    p = subprocess.run(args=[self.vsftpd, '-v'],
198*6236dae4SAndroid Build Coastguard Worker                                       capture_output=True, text=True, stdin=tmp)
199*6236dae4SAndroid Build Coastguard Worker                    if p.returncode != 0:
200*6236dae4SAndroid Build Coastguard Worker                        # not a working vsftpd
201*6236dae4SAndroid Build Coastguard Worker                        self.vsftpd = None
202*6236dae4SAndroid Build Coastguard Worker                    if p.stderr:
203*6236dae4SAndroid Build Coastguard Worker                        ver_text = p.stderr
204*6236dae4SAndroid Build Coastguard Worker                    else:
205*6236dae4SAndroid Build Coastguard Worker                        # Oddly, some versions of vsftpd write to stdin (!)
206*6236dae4SAndroid Build Coastguard Worker                        # instead of stderr, which is odd but works. If there
207*6236dae4SAndroid Build Coastguard Worker                        # is nothing on stderr, read the file on stdin and use
208*6236dae4SAndroid Build Coastguard Worker                        # any data there instead.
209*6236dae4SAndroid Build Coastguard Worker                        tmp.seek(0)
210*6236dae4SAndroid Build Coastguard Worker                        ver_text = tmp.read()
211*6236dae4SAndroid Build Coastguard Worker                m = re.match(r'vsftpd: version (\d+\.\d+\.\d+)', ver_text)
212*6236dae4SAndroid Build Coastguard Worker                if m:
213*6236dae4SAndroid Build Coastguard Worker                    self._vsftpd_version = m.group(1)
214*6236dae4SAndroid Build Coastguard Worker                elif len(p.stderr) == 0:
215*6236dae4SAndroid Build Coastguard Worker                    # vsftp does not use stdout or stderr for printing its version... -.-
216*6236dae4SAndroid Build Coastguard Worker                    self._vsftpd_version = 'unknown'
217*6236dae4SAndroid Build Coastguard Worker                else:
218*6236dae4SAndroid Build Coastguard Worker                    raise Exception(f'Unable to determine VsFTPD version from: {p.stderr}')
219*6236dae4SAndroid Build Coastguard Worker            except Exception:
220*6236dae4SAndroid Build Coastguard Worker                self.vsftpd = None
221*6236dae4SAndroid Build Coastguard Worker
222*6236dae4SAndroid Build Coastguard Worker        self._tcpdump = shutil.which('tcpdump')
223*6236dae4SAndroid Build Coastguard Worker
224*6236dae4SAndroid Build Coastguard Worker    @property
225*6236dae4SAndroid Build Coastguard Worker    def httpd_version(self):
226*6236dae4SAndroid Build Coastguard Worker        if self._httpd_version is None and self.apxs is not None:
227*6236dae4SAndroid Build Coastguard Worker            try:
228*6236dae4SAndroid Build Coastguard Worker                p = subprocess.run(args=[self.apxs, '-q', 'HTTPD_VERSION'],
229*6236dae4SAndroid Build Coastguard Worker                                   capture_output=True, text=True)
230*6236dae4SAndroid Build Coastguard Worker                if p.returncode != 0:
231*6236dae4SAndroid Build Coastguard Worker                    log.error(f'{self.apxs} failed to query HTTPD_VERSION: {p}')
232*6236dae4SAndroid Build Coastguard Worker                else:
233*6236dae4SAndroid Build Coastguard Worker                    self._httpd_version = p.stdout.strip()
234*6236dae4SAndroid Build Coastguard Worker            except Exception:
235*6236dae4SAndroid Build Coastguard Worker                log.exception(f'{self.apxs} failed to run')
236*6236dae4SAndroid Build Coastguard Worker        return self._httpd_version
237*6236dae4SAndroid Build Coastguard Worker
238*6236dae4SAndroid Build Coastguard Worker    def versiontuple(self, v):
239*6236dae4SAndroid Build Coastguard Worker        v = re.sub(r'(\d+\.\d+(\.\d+)?)(-\S+)?', r'\1', v)
240*6236dae4SAndroid Build Coastguard Worker        return tuple(map(int, v.split('.')))
241*6236dae4SAndroid Build Coastguard Worker
242*6236dae4SAndroid Build Coastguard Worker    def httpd_is_at_least(self, minv):
243*6236dae4SAndroid Build Coastguard Worker        if self.httpd_version is None:
244*6236dae4SAndroid Build Coastguard Worker            return False
245*6236dae4SAndroid Build Coastguard Worker        hv = self.versiontuple(self.httpd_version)
246*6236dae4SAndroid Build Coastguard Worker        return hv >= self.versiontuple(minv)
247*6236dae4SAndroid Build Coastguard Worker
248*6236dae4SAndroid Build Coastguard Worker    def caddy_is_at_least(self, minv):
249*6236dae4SAndroid Build Coastguard Worker        if self.caddy_version is None:
250*6236dae4SAndroid Build Coastguard Worker            return False
251*6236dae4SAndroid Build Coastguard Worker        hv = self.versiontuple(self.caddy_version)
252*6236dae4SAndroid Build Coastguard Worker        return hv >= self.versiontuple(minv)
253*6236dae4SAndroid Build Coastguard Worker
254*6236dae4SAndroid Build Coastguard Worker    def is_complete(self) -> bool:
255*6236dae4SAndroid Build Coastguard Worker        return os.path.isfile(self.httpd) and \
256*6236dae4SAndroid Build Coastguard Worker               os.path.isfile(self.apachectl) and \
257*6236dae4SAndroid Build Coastguard Worker               self.apxs is not None and \
258*6236dae4SAndroid Build Coastguard Worker               os.path.isfile(self.apxs)
259*6236dae4SAndroid Build Coastguard Worker
260*6236dae4SAndroid Build Coastguard Worker    def get_incomplete_reason(self) -> Optional[str]:
261*6236dae4SAndroid Build Coastguard Worker        if self.httpd is None or len(self.httpd.strip()) == 0:
262*6236dae4SAndroid Build Coastguard Worker            return 'httpd not configured, see `--with-test-httpd=<path>`'
263*6236dae4SAndroid Build Coastguard Worker        if not os.path.isfile(self.httpd):
264*6236dae4SAndroid Build Coastguard Worker            return f'httpd ({self.httpd}) not found'
265*6236dae4SAndroid Build Coastguard Worker        if not os.path.isfile(self.apachectl):
266*6236dae4SAndroid Build Coastguard Worker            return f'apachectl ({self.apachectl}) not found'
267*6236dae4SAndroid Build Coastguard Worker        if self.apxs is None:
268*6236dae4SAndroid Build Coastguard Worker            return "command apxs not found (commonly provided in apache2-dev)"
269*6236dae4SAndroid Build Coastguard Worker        if not os.path.isfile(self.apxs):
270*6236dae4SAndroid Build Coastguard Worker            return f"apxs ({self.apxs}) not found"
271*6236dae4SAndroid Build Coastguard Worker        return None
272*6236dae4SAndroid Build Coastguard Worker
273*6236dae4SAndroid Build Coastguard Worker    @property
274*6236dae4SAndroid Build Coastguard Worker    def nghttpx_version(self):
275*6236dae4SAndroid Build Coastguard Worker        return self._nghttpx_version
276*6236dae4SAndroid Build Coastguard Worker
277*6236dae4SAndroid Build Coastguard Worker    @property
278*6236dae4SAndroid Build Coastguard Worker    def caddy_version(self):
279*6236dae4SAndroid Build Coastguard Worker        return self._caddy_version
280*6236dae4SAndroid Build Coastguard Worker
281*6236dae4SAndroid Build Coastguard Worker    @property
282*6236dae4SAndroid Build Coastguard Worker    def vsftpd_version(self):
283*6236dae4SAndroid Build Coastguard Worker        return self._vsftpd_version
284*6236dae4SAndroid Build Coastguard Worker
285*6236dae4SAndroid Build Coastguard Worker    @property
286*6236dae4SAndroid Build Coastguard Worker    def tcpdmp(self) -> Optional[str]:
287*6236dae4SAndroid Build Coastguard Worker        return self._tcpdump
288*6236dae4SAndroid Build Coastguard Worker
289*6236dae4SAndroid Build Coastguard Worker
290*6236dae4SAndroid Build Coastguard Workerclass Env:
291*6236dae4SAndroid Build Coastguard Worker
292*6236dae4SAndroid Build Coastguard Worker    CONFIG = EnvConfig()
293*6236dae4SAndroid Build Coastguard Worker
294*6236dae4SAndroid Build Coastguard Worker    @staticmethod
295*6236dae4SAndroid Build Coastguard Worker    def setup_incomplete() -> bool:
296*6236dae4SAndroid Build Coastguard Worker        return not Env.CONFIG.is_complete()
297*6236dae4SAndroid Build Coastguard Worker
298*6236dae4SAndroid Build Coastguard Worker    @staticmethod
299*6236dae4SAndroid Build Coastguard Worker    def incomplete_reason() -> Optional[str]:
300*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.get_incomplete_reason()
301*6236dae4SAndroid Build Coastguard Worker
302*6236dae4SAndroid Build Coastguard Worker    @staticmethod
303*6236dae4SAndroid Build Coastguard Worker    def have_nghttpx() -> bool:
304*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.nghttpx is not None
305*6236dae4SAndroid Build Coastguard Worker
306*6236dae4SAndroid Build Coastguard Worker    @staticmethod
307*6236dae4SAndroid Build Coastguard Worker    def have_h3_server() -> bool:
308*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.nghttpx_with_h3
309*6236dae4SAndroid Build Coastguard Worker
310*6236dae4SAndroid Build Coastguard Worker    @staticmethod
311*6236dae4SAndroid Build Coastguard Worker    def have_ssl_curl() -> bool:
312*6236dae4SAndroid Build Coastguard Worker        return Env.curl_has_feature('ssl') or Env.curl_has_feature('multissl')
313*6236dae4SAndroid Build Coastguard Worker
314*6236dae4SAndroid Build Coastguard Worker    @staticmethod
315*6236dae4SAndroid Build Coastguard Worker    def have_h2_curl() -> bool:
316*6236dae4SAndroid Build Coastguard Worker        return 'http2' in Env.CONFIG.curl_props['features']
317*6236dae4SAndroid Build Coastguard Worker
318*6236dae4SAndroid Build Coastguard Worker    @staticmethod
319*6236dae4SAndroid Build Coastguard Worker    def have_h3_curl() -> bool:
320*6236dae4SAndroid Build Coastguard Worker        return 'http3' in Env.CONFIG.curl_props['features']
321*6236dae4SAndroid Build Coastguard Worker
322*6236dae4SAndroid Build Coastguard Worker    @staticmethod
323*6236dae4SAndroid Build Coastguard Worker    def curl_uses_lib(libname: str) -> bool:
324*6236dae4SAndroid Build Coastguard Worker        return libname.lower() in Env.CONFIG.curl_props['libs']
325*6236dae4SAndroid Build Coastguard Worker
326*6236dae4SAndroid Build Coastguard Worker    @staticmethod
327*6236dae4SAndroid Build Coastguard Worker    def curl_uses_ossl_quic() -> bool:
328*6236dae4SAndroid Build Coastguard Worker        if Env.have_h3_curl():
329*6236dae4SAndroid Build Coastguard Worker            return not Env.curl_uses_lib('ngtcp2') and Env.curl_uses_lib('nghttp3')
330*6236dae4SAndroid Build Coastguard Worker        return False
331*6236dae4SAndroid Build Coastguard Worker
332*6236dae4SAndroid Build Coastguard Worker    @staticmethod
333*6236dae4SAndroid Build Coastguard Worker    def curl_version_string() -> str:
334*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.curl_props['version_string']
335*6236dae4SAndroid Build Coastguard Worker
336*6236dae4SAndroid Build Coastguard Worker    @staticmethod
337*6236dae4SAndroid Build Coastguard Worker    def curl_features_string() -> str:
338*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.curl_props['features_string']
339*6236dae4SAndroid Build Coastguard Worker
340*6236dae4SAndroid Build Coastguard Worker    @staticmethod
341*6236dae4SAndroid Build Coastguard Worker    def curl_has_feature(feature: str) -> bool:
342*6236dae4SAndroid Build Coastguard Worker        return feature.lower() in Env.CONFIG.curl_props['features']
343*6236dae4SAndroid Build Coastguard Worker
344*6236dae4SAndroid Build Coastguard Worker    @staticmethod
345*6236dae4SAndroid Build Coastguard Worker    def curl_protocols_string() -> str:
346*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.curl_props['protocols_string']
347*6236dae4SAndroid Build Coastguard Worker
348*6236dae4SAndroid Build Coastguard Worker    @staticmethod
349*6236dae4SAndroid Build Coastguard Worker    def curl_has_protocol(protocol: str) -> bool:
350*6236dae4SAndroid Build Coastguard Worker        return protocol.lower() in Env.CONFIG.curl_props['protocols']
351*6236dae4SAndroid Build Coastguard Worker
352*6236dae4SAndroid Build Coastguard Worker    @staticmethod
353*6236dae4SAndroid Build Coastguard Worker    def curl_lib_version(libname: str) -> str:
354*6236dae4SAndroid Build Coastguard Worker        prefix = f'{libname.lower()}/'
355*6236dae4SAndroid Build Coastguard Worker        for lversion in Env.CONFIG.curl_props['lib_versions']:
356*6236dae4SAndroid Build Coastguard Worker            if lversion.startswith(prefix):
357*6236dae4SAndroid Build Coastguard Worker                return lversion[len(prefix):]
358*6236dae4SAndroid Build Coastguard Worker        return 'unknown'
359*6236dae4SAndroid Build Coastguard Worker
360*6236dae4SAndroid Build Coastguard Worker    @staticmethod
361*6236dae4SAndroid Build Coastguard Worker    def curl_lib_version_at_least(libname: str, min_version) -> bool:
362*6236dae4SAndroid Build Coastguard Worker        lversion = Env.curl_lib_version(libname)
363*6236dae4SAndroid Build Coastguard Worker        if lversion != 'unknown':
364*6236dae4SAndroid Build Coastguard Worker            return Env.CONFIG.versiontuple(min_version) <= \
365*6236dae4SAndroid Build Coastguard Worker                   Env.CONFIG.versiontuple(lversion)
366*6236dae4SAndroid Build Coastguard Worker        return False
367*6236dae4SAndroid Build Coastguard Worker
368*6236dae4SAndroid Build Coastguard Worker    @staticmethod
369*6236dae4SAndroid Build Coastguard Worker    def curl_os() -> str:
370*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.curl_props['os']
371*6236dae4SAndroid Build Coastguard Worker
372*6236dae4SAndroid Build Coastguard Worker    @staticmethod
373*6236dae4SAndroid Build Coastguard Worker    def curl_fullname() -> str:
374*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.curl_props['fullname']
375*6236dae4SAndroid Build Coastguard Worker
376*6236dae4SAndroid Build Coastguard Worker    @staticmethod
377*6236dae4SAndroid Build Coastguard Worker    def curl_version() -> str:
378*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.curl_props['version']
379*6236dae4SAndroid Build Coastguard Worker
380*6236dae4SAndroid Build Coastguard Worker    @staticmethod
381*6236dae4SAndroid Build Coastguard Worker    def curl_is_debug() -> bool:
382*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.curl_is_debug
383*6236dae4SAndroid Build Coastguard Worker
384*6236dae4SAndroid Build Coastguard Worker    @staticmethod
385*6236dae4SAndroid Build Coastguard Worker    def have_h3() -> bool:
386*6236dae4SAndroid Build Coastguard Worker        return Env.have_h3_curl() and Env.have_h3_server()
387*6236dae4SAndroid Build Coastguard Worker
388*6236dae4SAndroid Build Coastguard Worker    @staticmethod
389*6236dae4SAndroid Build Coastguard Worker    def httpd_version() -> str:
390*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.httpd_version
391*6236dae4SAndroid Build Coastguard Worker
392*6236dae4SAndroid Build Coastguard Worker    @staticmethod
393*6236dae4SAndroid Build Coastguard Worker    def nghttpx_version() -> str:
394*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.nghttpx_version
395*6236dae4SAndroid Build Coastguard Worker
396*6236dae4SAndroid Build Coastguard Worker    @staticmethod
397*6236dae4SAndroid Build Coastguard Worker    def caddy_version() -> str:
398*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.caddy_version
399*6236dae4SAndroid Build Coastguard Worker
400*6236dae4SAndroid Build Coastguard Worker    @staticmethod
401*6236dae4SAndroid Build Coastguard Worker    def caddy_is_at_least(minv) -> bool:
402*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.caddy_is_at_least(minv)
403*6236dae4SAndroid Build Coastguard Worker
404*6236dae4SAndroid Build Coastguard Worker    @staticmethod
405*6236dae4SAndroid Build Coastguard Worker    def httpd_is_at_least(minv) -> bool:
406*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.httpd_is_at_least(minv)
407*6236dae4SAndroid Build Coastguard Worker
408*6236dae4SAndroid Build Coastguard Worker    @staticmethod
409*6236dae4SAndroid Build Coastguard Worker    def has_caddy() -> bool:
410*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.caddy is not None
411*6236dae4SAndroid Build Coastguard Worker
412*6236dae4SAndroid Build Coastguard Worker    @staticmethod
413*6236dae4SAndroid Build Coastguard Worker    def has_vsftpd() -> bool:
414*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.vsftpd is not None
415*6236dae4SAndroid Build Coastguard Worker
416*6236dae4SAndroid Build Coastguard Worker    @staticmethod
417*6236dae4SAndroid Build Coastguard Worker    def vsftpd_version() -> str:
418*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.vsftpd_version
419*6236dae4SAndroid Build Coastguard Worker
420*6236dae4SAndroid Build Coastguard Worker    @staticmethod
421*6236dae4SAndroid Build Coastguard Worker    def tcpdump() -> Optional[str]:
422*6236dae4SAndroid Build Coastguard Worker        return Env.CONFIG.tcpdmp
423*6236dae4SAndroid Build Coastguard Worker
424*6236dae4SAndroid Build Coastguard Worker    def __init__(self, pytestconfig=None):
425*6236dae4SAndroid Build Coastguard Worker        self._verbose = pytestconfig.option.verbose \
426*6236dae4SAndroid Build Coastguard Worker            if pytestconfig is not None else 0
427*6236dae4SAndroid Build Coastguard Worker        self._ca = None
428*6236dae4SAndroid Build Coastguard Worker        self._test_timeout = 300.0 if self._verbose > 1 else 60.0  # seconds
429*6236dae4SAndroid Build Coastguard Worker
430*6236dae4SAndroid Build Coastguard Worker    def issue_certs(self):
431*6236dae4SAndroid Build Coastguard Worker        if self._ca is None:
432*6236dae4SAndroid Build Coastguard Worker            ca_dir = os.path.join(self.CONFIG.gen_dir, 'ca')
433*6236dae4SAndroid Build Coastguard Worker            self._ca = TestCA.create_root(name=self.CONFIG.tld,
434*6236dae4SAndroid Build Coastguard Worker                                          store_dir=ca_dir,
435*6236dae4SAndroid Build Coastguard Worker                                          key_type="rsa2048")
436*6236dae4SAndroid Build Coastguard Worker        self._ca.issue_certs(self.CONFIG.cert_specs)
437*6236dae4SAndroid Build Coastguard Worker
438*6236dae4SAndroid Build Coastguard Worker    def setup(self):
439*6236dae4SAndroid Build Coastguard Worker        os.makedirs(self.gen_dir, exist_ok=True)
440*6236dae4SAndroid Build Coastguard Worker        os.makedirs(self.htdocs_dir, exist_ok=True)
441*6236dae4SAndroid Build Coastguard Worker        self.issue_certs()
442*6236dae4SAndroid Build Coastguard Worker
443*6236dae4SAndroid Build Coastguard Worker    def get_credentials(self, domain) -> Optional[Credentials]:
444*6236dae4SAndroid Build Coastguard Worker        creds = self.ca.get_credentials_for_name(domain)
445*6236dae4SAndroid Build Coastguard Worker        if len(creds) > 0:
446*6236dae4SAndroid Build Coastguard Worker            return creds[0]
447*6236dae4SAndroid Build Coastguard Worker        return None
448*6236dae4SAndroid Build Coastguard Worker
449*6236dae4SAndroid Build Coastguard Worker    @property
450*6236dae4SAndroid Build Coastguard Worker    def verbose(self) -> int:
451*6236dae4SAndroid Build Coastguard Worker        return self._verbose
452*6236dae4SAndroid Build Coastguard Worker
453*6236dae4SAndroid Build Coastguard Worker    @property
454*6236dae4SAndroid Build Coastguard Worker    def test_timeout(self) -> Optional[float]:
455*6236dae4SAndroid Build Coastguard Worker        return self._test_timeout
456*6236dae4SAndroid Build Coastguard Worker
457*6236dae4SAndroid Build Coastguard Worker    @test_timeout.setter
458*6236dae4SAndroid Build Coastguard Worker    def test_timeout(self, val: Optional[float]):
459*6236dae4SAndroid Build Coastguard Worker        self._test_timeout = val
460*6236dae4SAndroid Build Coastguard Worker
461*6236dae4SAndroid Build Coastguard Worker    @property
462*6236dae4SAndroid Build Coastguard Worker    def gen_dir(self) -> str:
463*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.gen_dir
464*6236dae4SAndroid Build Coastguard Worker
465*6236dae4SAndroid Build Coastguard Worker    @property
466*6236dae4SAndroid Build Coastguard Worker    def project_dir(self) -> str:
467*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.project_dir
468*6236dae4SAndroid Build Coastguard Worker
469*6236dae4SAndroid Build Coastguard Worker    @property
470*6236dae4SAndroid Build Coastguard Worker    def build_dir(self) -> str:
471*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.build_dir
472*6236dae4SAndroid Build Coastguard Worker
473*6236dae4SAndroid Build Coastguard Worker    @property
474*6236dae4SAndroid Build Coastguard Worker    def ca(self):
475*6236dae4SAndroid Build Coastguard Worker        return self._ca
476*6236dae4SAndroid Build Coastguard Worker
477*6236dae4SAndroid Build Coastguard Worker    @property
478*6236dae4SAndroid Build Coastguard Worker    def htdocs_dir(self) -> str:
479*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.htdocs_dir
480*6236dae4SAndroid Build Coastguard Worker
481*6236dae4SAndroid Build Coastguard Worker    @property
482*6236dae4SAndroid Build Coastguard Worker    def tld(self) -> str:
483*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.tld
484*6236dae4SAndroid Build Coastguard Worker
485*6236dae4SAndroid Build Coastguard Worker    @property
486*6236dae4SAndroid Build Coastguard Worker    def domain1(self) -> str:
487*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.domain1
488*6236dae4SAndroid Build Coastguard Worker
489*6236dae4SAndroid Build Coastguard Worker    @property
490*6236dae4SAndroid Build Coastguard Worker    def domain1brotli(self) -> str:
491*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.domain1brotli
492*6236dae4SAndroid Build Coastguard Worker
493*6236dae4SAndroid Build Coastguard Worker    @property
494*6236dae4SAndroid Build Coastguard Worker    def domain2(self) -> str:
495*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.domain2
496*6236dae4SAndroid Build Coastguard Worker
497*6236dae4SAndroid Build Coastguard Worker    @property
498*6236dae4SAndroid Build Coastguard Worker    def ftp_domain(self) -> str:
499*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.ftp_domain
500*6236dae4SAndroid Build Coastguard Worker
501*6236dae4SAndroid Build Coastguard Worker    @property
502*6236dae4SAndroid Build Coastguard Worker    def proxy_domain(self) -> str:
503*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.proxy_domain
504*6236dae4SAndroid Build Coastguard Worker
505*6236dae4SAndroid Build Coastguard Worker    @property
506*6236dae4SAndroid Build Coastguard Worker    def http_port(self) -> int:
507*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.ports['http']
508*6236dae4SAndroid Build Coastguard Worker
509*6236dae4SAndroid Build Coastguard Worker    @property
510*6236dae4SAndroid Build Coastguard Worker    def https_port(self) -> int:
511*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.ports['https']
512*6236dae4SAndroid Build Coastguard Worker
513*6236dae4SAndroid Build Coastguard Worker    @property
514*6236dae4SAndroid Build Coastguard Worker    def nghttpx_https_port(self) -> int:
515*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.ports['nghttpx_https']
516*6236dae4SAndroid Build Coastguard Worker
517*6236dae4SAndroid Build Coastguard Worker    @property
518*6236dae4SAndroid Build Coastguard Worker    def h3_port(self) -> int:
519*6236dae4SAndroid Build Coastguard Worker        return self.https_port
520*6236dae4SAndroid Build Coastguard Worker
521*6236dae4SAndroid Build Coastguard Worker    @property
522*6236dae4SAndroid Build Coastguard Worker    def proxy_port(self) -> int:
523*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.ports['proxy']
524*6236dae4SAndroid Build Coastguard Worker
525*6236dae4SAndroid Build Coastguard Worker    @property
526*6236dae4SAndroid Build Coastguard Worker    def proxys_port(self) -> int:
527*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.ports['proxys']
528*6236dae4SAndroid Build Coastguard Worker
529*6236dae4SAndroid Build Coastguard Worker    @property
530*6236dae4SAndroid Build Coastguard Worker    def ftp_port(self) -> int:
531*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.ports['ftp']
532*6236dae4SAndroid Build Coastguard Worker
533*6236dae4SAndroid Build Coastguard Worker    @property
534*6236dae4SAndroid Build Coastguard Worker    def ftps_port(self) -> int:
535*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.ports['ftps']
536*6236dae4SAndroid Build Coastguard Worker
537*6236dae4SAndroid Build Coastguard Worker    @property
538*6236dae4SAndroid Build Coastguard Worker    def h2proxys_port(self) -> int:
539*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.ports['h2proxys']
540*6236dae4SAndroid Build Coastguard Worker
541*6236dae4SAndroid Build Coastguard Worker    def pts_port(self, proto: str = 'http/1.1') -> int:
542*6236dae4SAndroid Build Coastguard Worker        # proxy tunnel port
543*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.ports['h2proxys' if proto == 'h2' else 'proxys']
544*6236dae4SAndroid Build Coastguard Worker
545*6236dae4SAndroid Build Coastguard Worker    @property
546*6236dae4SAndroid Build Coastguard Worker    def caddy(self) -> str:
547*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.caddy
548*6236dae4SAndroid Build Coastguard Worker
549*6236dae4SAndroid Build Coastguard Worker    @property
550*6236dae4SAndroid Build Coastguard Worker    def caddy_https_port(self) -> int:
551*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.ports['caddys']
552*6236dae4SAndroid Build Coastguard Worker
553*6236dae4SAndroid Build Coastguard Worker    @property
554*6236dae4SAndroid Build Coastguard Worker    def caddy_http_port(self) -> int:
555*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.ports['caddy']
556*6236dae4SAndroid Build Coastguard Worker
557*6236dae4SAndroid Build Coastguard Worker    @property
558*6236dae4SAndroid Build Coastguard Worker    def vsftpd(self) -> str:
559*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.vsftpd
560*6236dae4SAndroid Build Coastguard Worker
561*6236dae4SAndroid Build Coastguard Worker    @property
562*6236dae4SAndroid Build Coastguard Worker    def ws_port(self) -> int:
563*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.ports['ws']
564*6236dae4SAndroid Build Coastguard Worker
565*6236dae4SAndroid Build Coastguard Worker    @property
566*6236dae4SAndroid Build Coastguard Worker    def curl(self) -> str:
567*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.curl
568*6236dae4SAndroid Build Coastguard Worker
569*6236dae4SAndroid Build Coastguard Worker    @property
570*6236dae4SAndroid Build Coastguard Worker    def httpd(self) -> str:
571*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.httpd
572*6236dae4SAndroid Build Coastguard Worker
573*6236dae4SAndroid Build Coastguard Worker    @property
574*6236dae4SAndroid Build Coastguard Worker    def apachectl(self) -> str:
575*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.apachectl
576*6236dae4SAndroid Build Coastguard Worker
577*6236dae4SAndroid Build Coastguard Worker    @property
578*6236dae4SAndroid Build Coastguard Worker    def apxs(self) -> str:
579*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.apxs
580*6236dae4SAndroid Build Coastguard Worker
581*6236dae4SAndroid Build Coastguard Worker    @property
582*6236dae4SAndroid Build Coastguard Worker    def nghttpx(self) -> Optional[str]:
583*6236dae4SAndroid Build Coastguard Worker        return self.CONFIG.nghttpx
584*6236dae4SAndroid Build Coastguard Worker
585*6236dae4SAndroid Build Coastguard Worker    @property
586*6236dae4SAndroid Build Coastguard Worker    def slow_network(self) -> bool:
587*6236dae4SAndroid Build Coastguard Worker        return "CURL_DBG_SOCK_WBLOCK" in os.environ or \
588*6236dae4SAndroid Build Coastguard Worker               "CURL_DBG_SOCK_WPARTIAL" in os.environ
589*6236dae4SAndroid Build Coastguard Worker
590*6236dae4SAndroid Build Coastguard Worker    @property
591*6236dae4SAndroid Build Coastguard Worker    def ci_run(self) -> bool:
592*6236dae4SAndroid Build Coastguard Worker        return "CURL_CI" in os.environ
593*6236dae4SAndroid Build Coastguard Worker
594*6236dae4SAndroid Build Coastguard Worker    def port_for(self, alpn_proto: Optional[str] = None):
595*6236dae4SAndroid Build Coastguard Worker        if alpn_proto is None or \
596*6236dae4SAndroid Build Coastguard Worker                alpn_proto in ['h2', 'http/1.1', 'http/1.0', 'http/0.9']:
597*6236dae4SAndroid Build Coastguard Worker            return self.https_port
598*6236dae4SAndroid Build Coastguard Worker        if alpn_proto in ['h3']:
599*6236dae4SAndroid Build Coastguard Worker            return self.h3_port
600*6236dae4SAndroid Build Coastguard Worker        return self.http_port
601*6236dae4SAndroid Build Coastguard Worker
602*6236dae4SAndroid Build Coastguard Worker    def authority_for(self, domain: str, alpn_proto: Optional[str] = None):
603*6236dae4SAndroid Build Coastguard Worker        return f'{domain}:{self.port_for(alpn_proto=alpn_proto)}'
604*6236dae4SAndroid Build Coastguard Worker
605*6236dae4SAndroid Build Coastguard Worker    def make_data_file(self, indir: str, fname: str, fsize: int,
606*6236dae4SAndroid Build Coastguard Worker                       line_length: int = 1024) -> str:
607*6236dae4SAndroid Build Coastguard Worker        if line_length < 11:
608*6236dae4SAndroid Build Coastguard Worker            raise RuntimeError('line_length less than 11 not supported')
609*6236dae4SAndroid Build Coastguard Worker        fpath = os.path.join(indir, fname)
610*6236dae4SAndroid Build Coastguard Worker        s10 = "0123456789"
611*6236dae4SAndroid Build Coastguard Worker        s = round((line_length / 10) + 1) * s10
612*6236dae4SAndroid Build Coastguard Worker        s = s[0:line_length-11]
613*6236dae4SAndroid Build Coastguard Worker        with open(fpath, 'w') as fd:
614*6236dae4SAndroid Build Coastguard Worker            for i in range(int(fsize / line_length)):
615*6236dae4SAndroid Build Coastguard Worker                fd.write(f"{i:09d}-{s}\n")
616*6236dae4SAndroid Build Coastguard Worker            remain = int(fsize % line_length)
617*6236dae4SAndroid Build Coastguard Worker            if remain != 0:
618*6236dae4SAndroid Build Coastguard Worker                i = int(fsize / line_length) + 1
619*6236dae4SAndroid Build Coastguard Worker                fd.write(f"{i:09d}-{s}"[0:remain-1] + "\n")
620*6236dae4SAndroid Build Coastguard Worker        return fpath
621