88import threading
99import time
1010import urllib .parse
11+ import urllib .request
1112from collections import defaultdict
1213from collections .abc import Callable
1314from 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 ()
0 commit comments