diff options
Diffstat (limited to 'include/py/homekit/http/http.py')
-rw-r--r-- | include/py/homekit/http/http.py | 132 |
1 files changed, 60 insertions, 72 deletions
diff --git a/include/py/homekit/http/http.py b/include/py/homekit/http/http.py index 8819c46..a8c7d82 100644 --- a/include/py/homekit/http/http.py +++ b/include/py/homekit/http/http.py @@ -1,17 +1,46 @@ import logging import asyncio +import html from enum import Enum from aiohttp import web -from aiohttp.web import Response, HTTPFound +from aiohttp.web import HTTPFound from aiohttp.web_exceptions import HTTPNotFound - from ..util import stringify, format_tb, Addr - _logger = logging.getLogger(__name__) +def _render_error(error_type, error_message, traceback=None, code=500): + traceback_html = '' + if traceback: + traceback = '\n\n'.join(traceback) + traceback_html = f""" +<div class="error_traceback"> + <div class="error_title">Traceback</div> + <div class="error_traceback_content">{html.escape(traceback)}</div> +</div> +""" + + buf = f""" +<!doctype html> +<html lang=en> +<head> +<title>Error: {html.escape(error_type)}</title> +<meta http-equiv="content-type" content="text/html; charset=utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> +<link rel="stylesheet" type="text/css" href="/assets/error_page.css"> +</head> +<body> +<div class="error_title">{html.escape(error_type)}</div> +<div class="error_message">{html.escape(error_message)}</div> +{traceback_html} +</body> +</html> +""" + return web.Response(text=buf, status=code, content_type='text/html') + + @web.middleware async def errors_handler_middleware(request, handler): try: @@ -19,97 +48,56 @@ async def errors_handler_middleware(request, handler): return response except HTTPNotFound: - return web.json_response({'error': 'not found'}, status=404) + return _render_error( + error_type='Not Found', + error_message='The page you requested has not been found.', + code=404 + ) except HTTPFound as exc: raise exc except Exception as exc: _logger.exception(exc) - data = { - 'error': exc.__class__.__name__, - 'message': exc.message if hasattr(exc, 'message') else str(exc) - } - tb = format_tb(exc) - if tb: - data['stacktrace'] = tb + return _render_error( + error_type=exc.__class__.__name__, + error_message=exc.message if hasattr(exc, 'message') else str(exc), + traceback=format_tb(exc) + ) - return web.json_response(data, status=500) - -def serve(addr: Addr, route_table: web.RouteTableDef, handle_signals: bool = True): +def serve(addr: Addr, before_start=None, handle_signals=True, routes=None, event_loop=None): app = web.Application() - app.add_routes(route_table) app.middlewares.append(errors_handler_middleware) - host, port = addr + if routes is not None: + app.add_routes(routes) - web.run_app(app, - host=host, - port=port, - handle_signals=handle_signals) + if callable(before_start): + before_start(app) + if not event_loop: + event_loop = asyncio.get_event_loop() -def routes() -> web.RouteTableDef: - return web.RouteTableDef() + runner = web.AppRunner(app, handle_signals=handle_signals) + event_loop.run_until_complete(runner.setup()) + host, port = addr + site = web.TCPSite(runner, host=host, port=port) + event_loop.run_until_complete(site.start()) -def ok(data=None): + _logger.info(f'Server started at http://{host}:{port}') + + event_loop.run_forever() + + +def ajax_ok(data=None): if data is None: data = 1 response = {'response': data} return web.json_response(response, dumps=stringify) -class HTTPServer: - def __init__(self, addr: Addr, handle_errors=True): - self.addr = addr - self.app = web.Application() - self.logger = logging.getLogger(self.__class__.__name__) - - if handle_errors: - self.app.middlewares.append(errors_handler_middleware) - - def _add_route(self, - method: str, - path: str, - handler: callable): - self.app.router.add_routes([getattr(web, method)(path, handler)]) - - def get(self, path, handler): - self._add_route('get', path, handler) - - def post(self, path, handler): - self._add_route('post', path, handler) - - def put(self, path, handler): - self._add_route('put', path, handler) - - def delete(self, path, handler): - self._add_route('delete', path, handler) - - def run(self, event_loop=None, handle_signals=True): - if not event_loop: - event_loop = asyncio.get_event_loop() - - runner = web.AppRunner(self.app, handle_signals=handle_signals) - event_loop.run_until_complete(runner.setup()) - - host, port = self.addr - site = web.TCPSite(runner, host=host, port=port) - event_loop.run_until_complete(site.start()) - - self.logger.info(f'Server started at http://{host}:{port}') - - event_loop.run_forever() - - def ok(self, data=None): - return ok(data) - - def plain(self, text: str): - return Response(text=text, content_type='text/plain') - - class HTTPMethod(Enum): GET = 'GET' POST = 'POST' |