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
@@ -938,6 +939,9 @@ class HTTPServer(HTTPServerBase): # pylint: disable=too-many-instance-attribute
938939
939940 :param threaded: whether to handle concurrent requests in separate threads
940941
942+ :param startup_timeout: maximum time in seconds to wait for server readiness.
943+ By default, no readiness check is performed.
944+
941945 .. py:attribute:: no_handler_status_code
942946
943947 Attribute containing the http status code (int) which will be the response
@@ -956,6 +960,7 @@ def __init__(
956960 default_waiting_settings : WaitingSettings | None = None ,
957961 * ,
958962 threaded : bool = False ,
963+ startup_timeout : float | None = None ,
959964 ) -> None :
960965 """
961966 Initializes the instance.
@@ -972,6 +977,32 @@ def __init__(
972977 self .default_waiting_settings = WaitingSettings ()
973978 self ._waiting_settings = copy (self .default_waiting_settings )
974979 self ._waiting_result : queue .LifoQueue [bool ] = queue .LifoQueue (maxsize = 1 )
980+ self .startup_timeout = startup_timeout
981+ self ._readiness_check_pending = False
982+
983+ def start (self ) -> None :
984+ if self .startup_timeout is None :
985+ self ._readiness_check_pending = False
986+ else :
987+ self ._readiness_check_pending = True
988+
989+ super ().start ()
990+ self .wait_for_server_ready ()
991+
992+ def wait_for_server_ready (self ) -> None :
993+ """
994+ Waits until the server is ready to serve requests.
995+ """
996+ if not self ._readiness_check_pending :
997+ return
998+
999+ url = self .url_for ("/" )
1000+ if not url .startswith (("http://" , "https://" )):
1001+ raise ValueError (f"Invalid URL generated for readiness check check: { url } " ) # noqa: EM102
1002+
1003+ with urllib .request .urlopen (url , timeout = self .startup_timeout ) as resp : # noqa: S310
1004+ if resp .status != 200 or resp .read () != b"OK" :
1005+ raise HTTPServerError ("Readiness check failed with status code: {}" .format (resp .status ))
9751006
9761007 def clear (self ) -> None :
9771008 """
@@ -1272,6 +1303,9 @@ def dispatch(self, request: Request) -> Response:
12721303 :param request: the request object from the werkzeug library
12731304 :return: the response object what the handler responded, or a response which contains the error
12741305 """
1306+ if self ._readiness_check_pending :
1307+ self ._readiness_check_pending = False
1308+ return Response ("OK" , status = 200 )
12751309
12761310 if self .permanently_failed :
12771311 return self .respond_permanent_failure ()
0 commit comments