summaryrefslogtreecommitdiff
path: root/include/py/homekit/http/http.py
blob: 8819c4638e5af8a46a0ede98158d7e2ea32399cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import logging
import asyncio

from enum import Enum
from aiohttp import web
from aiohttp.web import Response, HTTPFound
from aiohttp.web_exceptions import HTTPNotFound

from ..util import stringify, format_tb, Addr


_logger = logging.getLogger(__name__)


@web.middleware
async def errors_handler_middleware(request, handler):
    try:
        response = await handler(request)
        return response

    except HTTPNotFound:
        return web.json_response({'error': 'not found'}, status=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 web.json_response(data, status=500)


def serve(addr: Addr, route_table: web.RouteTableDef, handle_signals: bool = True):
    app = web.Application()
    app.add_routes(route_table)
    app.middlewares.append(errors_handler_middleware)

    host, port = addr

    web.run_app(app,
                host=host,
                port=port,
                handle_signals=handle_signals)


def routes() -> web.RouteTableDef:
    return web.RouteTableDef()


def 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'
    PUT = 'PUT'