diff --git a/tests/conftest.py b/tests/conftest.py index 14c0fc75..3423d1c7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -149,6 +149,11 @@ def client_ca(certipy): # self.internal_ssl_cert, # cafile=self.internal_ssl_ca, ) + # FIXME: generate certs with Authority Key defined for strict checking + # upstream PR: https://github.com/LLNL/certipy/pull/23/ + # avoids Missing Authority Key Identifier SSL errors + # with new defaults in Python 3.13 + ssl_context.verify_flags &= ~ssl.VERIFY_X509_STRICT AsyncHTTPClient.configure(None, defaults={"ssl_options": ssl_context}) return record['files']['ca'] @@ -311,6 +316,8 @@ def _check_ssl(proxy, client_ca): ) context.check_hostname = True context.verify_mode = ssl.VerifyMode.CERT_REQUIRED + # required for Python 3.13 until we get a certipy update + context.verify_flags &= ~ssl.VERIFY_X509_STRICT context.load_cert_chain(proxy.ssl_cert, proxy.ssl_key) url = urlparse(Config.public_url) diff --git a/tests/dummy_http_server.py b/tests/dummy_http_server.py index 2293ce22..4428043f 100644 --- a/tests/dummy_http_server.py +++ b/tests/dummy_http_server.py @@ -11,32 +11,59 @@ from http import HTTPStatus import websockets +from packaging.version import parse as V +_old_ws = V(websockets.__version__) < V("14") + + +# websockets 14 changed APIs +# drop _old_ws logic when we drop Python 3.8 +# _old_ws signature: (path, request_headers, port) +async def process_request(connection, request, port): + if _old_ws: + path = connection + request_headers = request + else: + path = request.path + request_headers = request.headers -async def process_request(path, request_headers, port): if path.endswith("/ws"): return None + headers = { "Content-Type": "text/plain", "Host": request_headers.get("Host", "None"), "Origin": request_headers.get("Origin", "None"), } - return (HTTPStatus.OK, headers, str(port).encode("utf8")) + if _old_ws: + return (HTTPStatus.OK, headers, str(port).encode()) + else: + response = connection.respond(HTTPStatus.OK, str(port)) + response.headers.update(headers) + return response + + +async def send_port(websocket): + _ip, port = websocket.local_address + await websocket.send(str(port)) -async def send_port(websocket, path): - await websocket.send(str(websocket.port)) +async def main(port, *, _start_future=None, _stop_future=None): + # allow signaling a stop (in tests) + if _stop_future is None: + _stop_future = asyncio.Future() -async def main(port): async with websockets.serve( send_port, host="127.0.0.1", port=port, process_request=partial(process_request, port=port), ): + if _start_future: + _start_future.set_result(None) # wait forever - await asyncio.Future() + await _stop_future if __name__ == "__main__": diff --git a/tests/test_dummy_http_server.py b/tests/test_dummy_http_server.py new file mode 100644 index 00000000..322ad031 --- /dev/null +++ b/tests/test_dummy_http_server.py @@ -0,0 +1,40 @@ +import asyncio + +import websockets +from tornado.httpclient import AsyncHTTPClient + +from .dummy_http_server import main + + +# quick test that the dummy http server for tests works! +async def test_dummy_server(request): + port = 5678 + start_future = asyncio.Future() + stop_future = asyncio.Future() + # stat dummy server, wait for it to start + main_future = asyncio.ensure_future( + main(port, _start_future=start_future, _stop_future=stop_future) + ) + await asyncio.wait( + [start_future, main_future], timeout=5, return_when=asyncio.FIRST_COMPLETED + ) + if main_future.done(): + main_future.result() + + try: + http_url = f"http://127.0.0.1:{port}/test" + ws_url = f"ws://127.0.0.1:{port}/ws" + resp = await AsyncHTTPClient().fetch(http_url) + assert resp.body == str(port).encode() + + ws_url = f"ws://127.0.0.1:{port}/ws" + + async with websockets.connect(ws_url) as websocket: + ws_port = await websocket.recv() + assert ws_port == str(port) + finally: + stop_future.cancel() + try: + await main_future + except asyncio.CancelledError: + pass