@@ -39,6 +39,7 @@ async def health(headers: dict[str, str], _body: Body) -> Body:
3939from __future__ import annotations
4040
4141import contextlib
42+ import json
4243import os
4344import platform as runtime_platform
4445import re
@@ -62,6 +63,38 @@ async def health(headers: dict[str, str], _body: Body) -> Body:
6263HEALTH_TIMEOUT_MS = 10000 # engine value for the type=health probe
6364UNHEALTHY = "Unhealthy. Please check '/health' endpoint."
6465
66+ # The engines' minimal landing page (platform-core public/index.html style);
67+ # the wrappers embed it - no static file service by design.
68+ INDEX_HTML = """<!DOCTYPE html>
69+ <html>
70+ <body>
71+
72+ <h2>Welcome</h2>
73+
74+ <p><a href="/info">INFO endpoint</a></p>
75+ <p><a href="/info/routes">Service list</a></p>
76+ <p><a href="/env">Environment endpoint</a></p>
77+ <p><a href="/health">Health endpoint</a></p>
78+ <p><a href="/livenessprobe">Liveness probe</a></p>
79+
80+ </body>
81+ </html>"""
82+
83+
84+ def _pretty (payload : Any ) -> str :
85+ """The engines' default serializer presentation: pretty-printed JSON."""
86+ return json .dumps (payload , indent = 2 , ensure_ascii = False )
87+
88+
89+ def _json_response (payload : dict [str , Any ], status : int = 200 ) -> web .Response :
90+ return web .json_response (payload , status = status , dumps = _pretty )
91+
92+
93+ def _error_response (status : int , message : str ) -> web .Response :
94+ """The engines' host-level error shape (SimpleHttpUtility signature)."""
95+ return _json_response ({"status" : status , "message" : message , "type" : "error" },
96+ status = status )
97+
6598_SPLIT = re .compile (r"[,\s]+" )
6699
67100_origin : str | None = None
@@ -138,9 +171,14 @@ def _app_block(self) -> dict[str, Any]:
138171 "description" : self .description }
139172
140173 async def handle (self , request : web .Request ) -> web .Response :
141- """The single GET dispatcher (aiohttp handlers must be coroutines):
142- /health awaits its dependency probes; the rest render synchronously."""
174+ """The single dispatcher (aiohttp handlers must be coroutines):
175+ /health awaits its dependency probes; the rest render synchronously.
176+ Unknown paths and non-GET methods answer the engines' error shape."""
177+ if request .method != "GET" :
178+ return _error_response (404 , "Resource not found" )
143179 match request .path :
180+ case "/" :
181+ return web .Response (text = INDEX_HTML , content_type = "text/html" )
144182 case "/info" :
145183 return self ._info ()
146184 case "/info/routes" :
@@ -151,12 +189,12 @@ async def handle(self, request: web.Request) -> web.Response:
151189 return await self ._health ()
152190 case "/livenessprobe" :
153191 return self ._liveness_probe ()
154- case _: # the router only maps the five paths above here
155- return web . Response ( status = 404 , text = "Not found" )
192+ case _:
193+ return _error_response ( 404 , "Resource not found" )
156194
157195 def _info (self ) -> web .Response :
158196 now = datetime .now (timezone .utc )
159- return web . json_response ({
197+ return _json_response ({
160198 "app" : self ._app_block (),
161199 "runtime" : {
162200 "language" : "python" ,
@@ -174,7 +212,7 @@ def _routes(self) -> web.Response:
174212 for route , service in sorted (self .registry .routes ().items ()):
175213 target = private if service .private else public
176214 target [route ] = service .instances
177- return web . json_response ({
215+ return _json_response ({
178216 "app" : self ._app_block (),
179217 "routing" : {"public" : public , "private" : private },
180218 })
@@ -185,7 +223,7 @@ def _env(self) -> web.Response:
185223 for name in _as_list (config .get ("show.env.variables" ))}
186224 properties = {name : config .get_property (name ) or ""
187225 for name in _as_list (config .get ("show.application.properties" ))}
188- return web . json_response ({
226+ return _json_response ({
189227 "app" : self ._app_block (),
190228 "env" : {"environment" : environment , "properties" : properties },
191229 })
@@ -204,7 +242,7 @@ async def _health(self) -> web.Response:
204242 result ["status" ] = "UP" if up else "DOWN"
205243 result ["origin" ] = app_origin ()
206244 result ["name" ] = self .app_name
207- return web . json_response (result , status = 200 if up else 400 )
245+ return _json_response (result , status = 200 if up else 400 )
208246
209247 def _liveness_probe (self ) -> web .Response :
210248 if self .healthy :
0 commit comments