Skip to content

Commit 9cd303f

Browse files
committed
Implement http readiness checking
Send a HTTP request to the server if user specified readiness timeout.
1 parent 334b87f commit 9cd303f

3 files changed

Lines changed: 59 additions & 0 deletions

File tree

pytest_httpserver/httpserver.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import threading
99
import time
1010
import urllib.parse
11+
import urllib.request
1112
from collections import defaultdict
1213
from collections.abc import Callable
1314
from collections.abc import Generator
@@ -735,6 +736,7 @@ def thread_target(self) -> None:
735736
This should not be called directly, but can be overridden to tailor it to your needs.
736737
"""
737738
assert self.server is not None
739+
time.sleep(5)
738740
self.server.serve_forever()
739741

740742
def is_running(self) -> bool:
@@ -938,6 +940,9 @@ class HTTPServer(HTTPServerBase): # pylint: disable=too-many-instance-attribute
938940
939941
:param threaded: whether to handle concurrent requests in separate threads
940942
943+
:param startup_timeout: maximum time in seconds to wait for server readiness.
944+
Set to ``None`` to disable readiness waiting.
945+
941946
.. py:attribute:: no_handler_status_code
942947
943948
Attribute containing the http status code (int) which will be the response
@@ -956,6 +961,7 @@ def __init__(
956961
default_waiting_settings: WaitingSettings | None = None,
957962
*,
958963
threaded: bool = False,
964+
startup_timeout: float | None = None,
959965
) -> None:
960966
"""
961967
Initializes the instance.
@@ -972,6 +978,32 @@ def __init__(
972978
self.default_waiting_settings = WaitingSettings()
973979
self._waiting_settings = copy(self.default_waiting_settings)
974980
self._waiting_result: queue.LifoQueue[bool] = queue.LifoQueue(maxsize=1)
981+
self.startup_timeout = startup_timeout
982+
self._readiness_check_pending = False
983+
984+
def start(self) -> None:
985+
if self.startup_timeout is None:
986+
self._readiness_check_pending = False
987+
else:
988+
self._readiness_check_pending = True
989+
990+
super().start()
991+
self.wait_for_server_ready()
992+
993+
def wait_for_server_ready(self) -> None:
994+
"""
995+
Waits until the server is ready to serve requests.
996+
"""
997+
if not self._readiness_check_pending:
998+
return
999+
1000+
url = self.url_for("/")
1001+
if not url.startswith(("http://", "https://")):
1002+
raise ValueError(f"Invalid URL generated for readiness check check: {url}") # noqa: EM102
1003+
1004+
with urllib.request.urlopen(url, timeout=self.startup_timeout) as resp: # noqa: S310
1005+
if resp.status != 200 or resp.read() != b"OK":
1006+
raise HTTPServerError("Readiness check failed with status code: {}".format(resp.status))
9751007

9761008
def clear(self) -> None:
9771009
"""
@@ -1272,6 +1304,9 @@ def dispatch(self, request: Request) -> Response:
12721304
:param request: the request object from the werkzeug library
12731305
:return: the response object what the handler responded, or a response which contains the error
12741306
"""
1307+
if self._readiness_check_pending:
1308+
self._readiness_check_pending = False
1309+
return Response("OK", status=200)
12751310

12761311
if self.permanently_failed:
12771312
return self.respond_permanent_failure()

tests/test_readiness.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from collections.abc import Generator
2+
3+
import pytest
4+
import requests
5+
6+
from pytest_httpserver.httpserver import HTTPServer
7+
8+
9+
@pytest.fixture
10+
def httpserver() -> Generator[HTTPServer, None, None]:
11+
server = HTTPServer(startup_timeout=10)
12+
server.start()
13+
yield server
14+
server.clear()
15+
if server.is_running():
16+
server.stop()
17+
18+
19+
def test_httpserver_readiness(httpserver: HTTPServer):
20+
httpserver.expect_request("/").respond_with_data("Hello, world!")
21+
resp = requests.get(httpserver.url_for("/"))
22+
assert resp.status_code == 200
23+
assert resp.text == "Hello, world!"

tests/test_release.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ def test_sdist_contents(build: Build, version: str):
231231
"test_port_changing.py",
232232
"test_querymatcher.py",
233233
"test_querystring.py",
234+
"test_readiness.py",
234235
"test_release.py",
235236
"test_ssl.py",
236237
"test_thread_type.py",

0 commit comments

Comments
 (0)