-
Notifications
You must be signed in to change notification settings - Fork 5
/
aiobottle.py
126 lines (113 loc) · 4.76 KB
/
aiobottle.py
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
117
118
119
120
121
122
123
124
125
126
from bottle import Bottle, ServerAdapter, response, request, HTTPResponse\
, HTTPError, tob, _e, html_escape, DEBUG, RouteReset
from traceback import format_exc
import sys
import logging
import asyncio
from aiohttp.wsgi import WSGIServerHttpProtocol
import inspect
__all__ = ['AsyncServer', 'AsyncBottle']
logger = logging.getLogger('asyncbottle')
FORMAT = '%(asctime)-15s - %(message)s'
logger.setLevel(logging.ERROR)
formater = logging.Formatter(FORMAT)
ch = logging.StreamHandler()
ch.setFormatter(formater)
logger.addHandler(ch)
class AsyncServer(ServerAdapter):
def run(self, handler):
def wsgi_app(env, start):
def start_response(status_line, headerlist, exc_info=None):
status_code = status_line.split(' ', 1)[0]
headerdict = dict(map(lambda x: (x[0].lower(), x[1]), headerlist))
length = headerdict.get('content-length', 0)
logger.error('{} {} {} {}'.format(env['REQUEST_METHOD'],
env['RAW_URI'], status_code, length))
return start(status_line, headerlist, exc_info)
return handler(env, start_response)
loop = asyncio.get_event_loop()
f = loop.create_server(
lambda: WSGIServerHttpProtocol(wsgi_app, loop = loop, readpayload=True),
self.host, self.port)
loop.run_until_complete(f)
try:
loop.run_forever()
except KeyboardInterrupt:
pass
class AsyncBottle(Bottle):
def _handle(self, environ):
path = environ['bottle.raw_path'] = environ['PATH_INFO']
try:
environ['PATH_INFO'] = path.encode('latin1').decode('utf8')
except UnicodeError:
return HTTPError(400, 'Invalid path string. Expected UTF-8')
try:
environ['bottle.app'] = self
request.bind(environ)
response.bind()
try:
self.trigger_hook("before_request")
route, args = self.router.match(environ)
environ['route.handle'] = route
environ['bottle.route'] = route
environ['route.url_args'] = args
out = route.call(**args)
if isinstance(out, asyncio.Future) or inspect.isgenerator(out):
out = yield from out
return out
finally:
self.trigger_hook("after_request")
except HTTPResponse:
return _e()
except RouteReset:
route.reset()
return (yield from self._handle(environ))
except (KeyboardInterrupt, SystemExit, MemoryError):
raise
except Exception:
if not self.catchall: raise
stacktrace = format_exc()
environ['wsgi.errors'].write(stacktrace)
return HTTPError(500, "Internal Server Error", _e(), stacktrace)
def wsgi(self, environ, start_response):
""" The bottle WSGI-interface. """
try:
out = self._cast((yield from self._handle(environ)))
# rfc2616 section 4.3
if response._status_code in (100, 101, 204, 304)\
or environ['REQUEST_METHOD'] == 'HEAD':
if hasattr(out, 'close'): out.close()
out = []
start_response(response._status_line, response.headerlist)
return out
except (KeyboardInterrupt, SystemExit, MemoryError):
raise
except Exception:
if not self.catchall: raise
err = '<h1>Critical error while processing request: %s</h1>' \
% html_escape(environ.get('PATH_INFO', '/'))
if DEBUG:
err += '<h2>Error:</h2>\n<pre>\n%s\n</pre>\n' \
'<h2>Traceback:</h2>\n<pre>\n%s\n</pre>\n' \
% (html_escape(repr(_e())), html_escape(format_exc()))
environ['wsgi.errors'].write(err)
headers = [('Content-Type', 'text/html; charset=UTF-8')]
start_response('500 INTERNAL SERVER ERROR', headers, sys.exc_info())
return [tob(err)]
def __call__(self, environ, start_response):
''' Each instance of :class:'Bottle' is a WSGI application. '''
return (yield from self.wsgi(environ, start_response))
try:
from aiohttp.worker import AsyncGunicornWorker as _AsyncGunicornWorker
__all__.append('AsyncGunicornWorker')
class AsyncGunicornWorker(_AsyncGunicornWorker):
def factory(self, wsgi, host, port):
proto = WSGIServerHttpProtocol(
wsgi, loop=self.loop,
log=self.log,
access_log=self.log.access_log,
access_log_format=self.cfg.access_log_format,
readpayload=True)
return self.wrap_protocol(proto)
except:
pass