summaryrefslogtreecommitdiff
path: root/include/py/homekit/http/http.py
diff options
context:
space:
mode:
Diffstat (limited to 'include/py/homekit/http/http.py')
-rw-r--r--include/py/homekit/http/http.py132
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'