Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/curl/tests/http/testenv/nghttpx.py
2659 views
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
#***************************************************************************
4
# _ _ ____ _
5
# Project ___| | | | _ \| |
6
# / __| | | | |_) | |
7
# | (__| |_| | _ <| |___
8
# \___|\___/|_| \_\_____|
9
#
10
# Copyright (C) Daniel Stenberg, <[email protected]>, 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
#
27
import logging
28
import os
29
import signal
30
import socket
31
import subprocess
32
import time
33
from typing import Optional, Dict
34
from datetime import datetime, timedelta
35
36
from .env import Env, NghttpxUtil
37
from .curl import CurlClient
38
from .ports import alloc_ports_and_do
39
40
log = logging.getLogger(__name__)
41
42
43
class Nghttpx:
44
45
def __init__(self, env: Env, name: str, domain: str, cred_name: str):
46
self.env = env
47
self._name = name
48
self._domain = domain
49
self._port = 0
50
self._https_port = 0
51
self._cmd = env.nghttpx
52
self._run_dir = os.path.join(env.gen_dir, name)
53
self._pid_file = os.path.join(self._run_dir, 'nghttpx.pid')
54
self._conf_file = os.path.join(self._run_dir, 'nghttpx.conf')
55
self._error_log = os.path.join(self._run_dir, 'nghttpx.log')
56
self._stderr = os.path.join(self._run_dir, 'nghttpx.stderr')
57
self._tmp_dir = os.path.join(self._run_dir, 'tmp')
58
self._process: Optional[subprocess.Popen] = None
59
self._cred_name = self._def_cred_name = cred_name
60
self._loaded_cred_name = ''
61
self._version = NghttpxUtil.version(self._cmd)
62
63
def supports_h3(self):
64
return NghttpxUtil.version_with_h3(self._version)
65
66
def set_cred_name(self, name: str):
67
self._cred_name = name
68
69
def reset_config(self):
70
self._cred_name = self._def_cred_name
71
72
def reload_if_config_changed(self):
73
if self._process and self._port > 0 and \
74
self._loaded_cred_name == self._cred_name:
75
return True
76
return self.reload()
77
78
@property
79
def https_port(self):
80
return self._https_port
81
82
def exists(self):
83
return self._cmd and os.path.exists(self._cmd)
84
85
def clear_logs(self):
86
self._rmf(self._error_log)
87
self._rmf(self._stderr)
88
89
def is_running(self):
90
if self._process:
91
self._process.poll()
92
return self._process.returncode is None
93
return False
94
95
def start_if_needed(self):
96
if not self.is_running():
97
return self.start()
98
return True
99
100
def initial_start(self):
101
self._rmf(self._pid_file)
102
self._rmf(self._error_log)
103
self._mkpath(self._run_dir)
104
self._write_config()
105
106
def start(self, wait_live=True):
107
pass
108
109
def stop(self, wait_dead=True):
110
self._mkpath(self._tmp_dir)
111
if self._process:
112
self._process.terminate()
113
self._process.wait(timeout=2)
114
self._process = None
115
return not wait_dead or self.wait_dead(timeout=timedelta(seconds=5))
116
return True
117
118
def restart(self):
119
self.stop()
120
return self.start()
121
122
def reload(self, timeout: timedelta = timedelta(seconds=Env.SERVER_TIMEOUT)):
123
if self._process:
124
running = self._process
125
self._process = None
126
os.kill(running.pid, signal.SIGQUIT)
127
end_wait = datetime.now() + timedelta(seconds=5)
128
if not self.start(wait_live=False):
129
self._process = running
130
return False
131
while datetime.now() < end_wait:
132
try:
133
log.debug(f'waiting for nghttpx({running.pid}) to exit.')
134
running.wait(1)
135
log.debug(f'nghttpx({running.pid}) terminated -> {running.returncode}')
136
running = None
137
break
138
except subprocess.TimeoutExpired:
139
log.warning(f'nghttpx({running.pid}), not shut down yet.')
140
os.kill(running.pid, signal.SIGQUIT)
141
if datetime.now() >= end_wait:
142
log.error(f'nghttpx({running.pid}), terminate forcefully.')
143
os.kill(running.pid, signal.SIGKILL)
144
running.terminate()
145
running.wait(1)
146
return self.wait_live(timeout=timeout)
147
return False
148
149
def wait_dead(self, timeout: timedelta):
150
curl = CurlClient(env=self.env, run_dir=self._tmp_dir)
151
try_until = datetime.now() + timeout
152
while datetime.now() < try_until:
153
if self._https_port > 0:
154
check_url = f'https://{self._domain}:{self._port}/'
155
r = curl.http_get(url=check_url, extra_args=[
156
'--trace', 'curl.trace', '--trace-time',
157
'--connect-timeout', '1'
158
])
159
else:
160
check_url = f'https://{self._domain}:{self._port}/'
161
r = curl.http_get(url=check_url, extra_args=[
162
'--trace', 'curl.trace', '--trace-time',
163
'--http3-only', '--connect-timeout', '1'
164
])
165
if r.exit_code != 0:
166
return True
167
log.debug(f'waiting for nghttpx to stop responding: {r}')
168
time.sleep(.1)
169
log.debug(f"Server still responding after {timeout}")
170
return False
171
172
def wait_live(self, timeout: timedelta):
173
curl = CurlClient(env=self.env, run_dir=self._tmp_dir)
174
try_until = datetime.now() + timeout
175
while datetime.now() < try_until:
176
if self._https_port > 0:
177
check_url = f'https://{self._domain}:{self._port}/'
178
r = curl.http_get(url=check_url, extra_args=[
179
'--trace', 'curl.trace', '--trace-time',
180
'--connect-timeout', '1'
181
])
182
else:
183
check_url = f'https://{self._domain}:{self._port}/'
184
r = curl.http_get(url=check_url, extra_args=[
185
'--http3-only', '--trace', 'curl.trace', '--trace-time',
186
'--connect-timeout', '1'
187
])
188
if r.exit_code == 0:
189
return True
190
time.sleep(.1)
191
log.error(f"Server still not responding after {timeout}")
192
return False
193
194
def _rmf(self, path):
195
if os.path.exists(path):
196
return os.remove(path)
197
198
def _mkpath(self, path):
199
if not os.path.exists(path):
200
return os.makedirs(path)
201
202
def _write_config(self):
203
with open(self._conf_file, 'w') as fd:
204
fd.write('# nghttpx test config')
205
fd.write("\n".join([
206
'# do we need something here?'
207
]))
208
209
210
class NghttpxQuic(Nghttpx):
211
212
PORT_SPECS = {
213
'nghttpx_https': socket.SOCK_STREAM,
214
}
215
216
def __init__(self, env: Env):
217
super().__init__(env=env, name='nghttpx-quic',
218
domain=env.domain1, cred_name=env.domain1)
219
self._https_port = env.https_port
220
221
def initial_start(self):
222
super().initial_start()
223
224
def startup(ports: Dict[str, int]) -> bool:
225
self._port = ports['nghttpx_https']
226
if self.start():
227
self.env.update_ports(ports)
228
return True
229
self.stop()
230
self._port = 0
231
return False
232
233
return alloc_ports_and_do(NghttpxQuic.PORT_SPECS, startup,
234
self.env.gen_root, max_tries=3)
235
236
def start(self, wait_live=True):
237
self._mkpath(self._tmp_dir)
238
if self._process:
239
self.stop()
240
creds = self.env.get_credentials(self._cred_name)
241
assert creds # convince pytype this isn't None
242
self._loaded_cred_name = self._cred_name
243
args = [self._cmd, f'--frontend=*,{self._port};tls']
244
if self.supports_h3():
245
args.extend([
246
f'--frontend=*,{self.env.h3_port};quic',
247
'--frontend-quic-early-data',
248
])
249
args.extend([
250
f'--backend=127.0.0.1,{self.env.https_port};{self._domain};sni={self._domain};proto=h2;tls',
251
f'--backend=127.0.0.1,{self.env.http_port}',
252
'--log-level=ERROR',
253
f'--pid-file={self._pid_file}',
254
f'--errorlog-file={self._error_log}',
255
f'--conf={self._conf_file}',
256
f'--cacert={self.env.ca.cert_file}',
257
creds.pkey_file,
258
creds.cert_file,
259
'--frontend-http3-window-size=1M',
260
'--frontend-http3-max-window-size=10M',
261
'--frontend-http3-connection-window-size=10M',
262
'--frontend-http3-max-connection-window-size=100M',
263
# f'--frontend-quic-debug-log',
264
])
265
ngerr = open(self._stderr, 'a')
266
self._process = subprocess.Popen(args=args, stderr=ngerr)
267
if self._process.returncode is not None:
268
return False
269
return not wait_live or self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT))
270
271
272
class NghttpxFwd(Nghttpx):
273
274
def __init__(self, env: Env):
275
super().__init__(env=env, name='nghttpx-fwd',
276
domain=env.proxy_domain,
277
cred_name=env.proxy_domain)
278
279
def initial_start(self):
280
super().initial_start()
281
282
def startup(ports: Dict[str, int]) -> bool:
283
self._port = ports['h2proxys']
284
if self.start():
285
self.env.update_ports(ports)
286
return True
287
self.stop()
288
self._port = 0
289
return False
290
291
return alloc_ports_and_do({'h2proxys': socket.SOCK_STREAM},
292
startup, self.env.gen_root, max_tries=3)
293
294
def start(self, wait_live=True):
295
assert self._port > 0
296
self._mkpath(self._tmp_dir)
297
if self._process:
298
self.stop()
299
creds = self.env.get_credentials(self._cred_name)
300
assert creds # convince pytype this isn't None
301
self._loaded_cred_name = self._cred_name
302
args = [
303
self._cmd,
304
'--http2-proxy',
305
f'--frontend=*,{self._port}',
306
f'--backend=127.0.0.1,{self.env.proxy_port}',
307
'--log-level=ERROR',
308
f'--pid-file={self._pid_file}',
309
f'--errorlog-file={self._error_log}',
310
f'--conf={self._conf_file}',
311
f'--cacert={self.env.ca.cert_file}',
312
creds.pkey_file,
313
creds.cert_file,
314
]
315
ngerr = open(self._stderr, 'a')
316
self._process = subprocess.Popen(args=args, stderr=ngerr)
317
if self._process.returncode is not None:
318
return False
319
return not wait_live or self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT))
320
321
def wait_dead(self, timeout: timedelta):
322
curl = CurlClient(env=self.env, run_dir=self._tmp_dir)
323
try_until = datetime.now() + timeout
324
while datetime.now() < try_until:
325
check_url = f'https://{self.env.proxy_domain}:{self._port}/'
326
r = curl.http_get(url=check_url)
327
if r.exit_code != 0:
328
return True
329
log.debug(f'waiting for nghttpx-fwd to stop responding: {r}')
330
time.sleep(.1)
331
log.debug(f"Server still responding after {timeout}")
332
return False
333
334
def wait_live(self, timeout: timedelta):
335
curl = CurlClient(env=self.env, run_dir=self._tmp_dir)
336
try_until = datetime.now() + timeout
337
while datetime.now() < try_until:
338
check_url = f'https://{self.env.proxy_domain}:{self._port}/'
339
r = curl.http_get(url=check_url, extra_args=[
340
'--trace', 'curl.trace', '--trace-time'
341
])
342
if r.exit_code == 0:
343
return True
344
time.sleep(.1)
345
log.error(f"Server still not responding after {timeout}")
346
return False
347
348