-
-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
03efe46
commit 3d9b5d7
Showing
2 changed files
with
149 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
try: | ||
import uasyncio as asyncio | ||
except ImportError: | ||
import asyncio | ||
from microdot import Microdot as BaseMicrodot | ||
from microdot import print_exception | ||
from microdot import Request as BaseRequest | ||
from microdot import Response as BaseResponse | ||
|
||
|
||
class Request(BaseRequest): | ||
@staticmethod | ||
async def create(stream, client_addr): | ||
# request line | ||
line = (await stream.readline()).strip().decode() | ||
method, url, http_version = line.split() | ||
http_version = http_version.split('/', 1)[1] | ||
|
||
# headers | ||
headers = {} | ||
content_length = 0 | ||
while True: | ||
line = (await stream.readline()).strip().decode() | ||
if line == '': | ||
break | ||
header, value = line.split(':', 1) | ||
value = value.strip() | ||
headers[header] = value | ||
if header == 'Content-Length': | ||
content_length = int(value) | ||
|
||
# body | ||
body = await stream.read(content_length) \ | ||
if content_length else b'' | ||
|
||
return Request(client_addr, method, url, http_version, headers, body) | ||
|
||
|
||
class Response(BaseResponse): | ||
async def write(self, stream): | ||
self.complete() | ||
|
||
# status code | ||
await stream.awrite('HTTP/1.0 {status_code} {reason}\r\n'.format( | ||
status_code=self.status_code, | ||
reason='OK' if self.status_code == 200 else 'N/A').encode()) | ||
|
||
# headers | ||
for header, value in self.headers.items(): | ||
values = value if isinstance(value, list) else [value] | ||
for value in values: | ||
await stream.awrite('{header}: {value}\r\n'.format( | ||
header=header, value=value).encode()) | ||
await stream.awrite(b'\r\n') | ||
|
||
# body | ||
if self.body: | ||
await stream.awrite(self.body) | ||
|
||
|
||
class Microdot(BaseMicrodot): | ||
def run(self, host='0.0.0.0', port=5000, debug=False): | ||
async def serve(reader, writer): | ||
req = await Request.create(reader, | ||
writer.get_extra_info('peername')) | ||
res = await self.dispatch_request(req) | ||
if debug: # pragma: no cover | ||
print('{method} {path} {status_code}'.format( | ||
method=req.method, path=req.path, | ||
status_code=res.status_code)) | ||
|
||
if not hasattr(writer, 'awrite'): | ||
class AWriter: | ||
def __init__(self, writer): | ||
self.writer = writer | ||
|
||
async def awrite(self, data): | ||
self.writer.write(data) | ||
await self.writer.drain() | ||
|
||
async def aclose(self): | ||
self.writer.close() | ||
|
||
writer = AWriter(writer) | ||
await res.write(writer) | ||
await writer.aclose() | ||
|
||
if debug: # pragma: no cover | ||
print('Listening on {host}:{port}...'.format(host=host, port=port)) | ||
loop = asyncio.get_event_loop() | ||
loop.run_until_complete(asyncio.start_server(serve, host, port)) | ||
loop.run_forever() | ||
loop.close() | ||
|
||
async def dispatch_request(self, req): | ||
f = self.find_route(req) | ||
try: | ||
res = None | ||
if f: | ||
for handler in self.before_request_handlers: | ||
res = await self._invoke_handler(handler, req) | ||
if res: | ||
break | ||
if res is None: | ||
res = await self._invoke_handler(f, req, **req.url_args) | ||
if isinstance(res, tuple): | ||
res = Response(*res) | ||
elif not isinstance(res, Response): | ||
res = Response(res) | ||
for handler in self.after_request_handlers: | ||
res = await self._invoke_handler(handler, req, res) or res | ||
elif 404 in self.error_handlers: | ||
res = self._invoke_handler(self.error_handlers[404], req) | ||
else: | ||
res = 'Not found', 404 | ||
except Exception as exc: | ||
print_exception(exc) | ||
res = None | ||
if exc.__class__ in self.error_handlers: | ||
try: | ||
res = self._invoke_handler( | ||
self.error_handlers[exc.__class__], req, exc) | ||
except Exception as exc2: # pragma: no cover | ||
print_exception(exc2) | ||
if res is None: | ||
if 500 in self.error_handlers: | ||
res = await self._invoke_handler(self.error_handlers[500], | ||
req) | ||
else: | ||
res = 'Internal server error', 500 | ||
if isinstance(res, tuple): | ||
res = Response(*res) | ||
elif not isinstance(res, Response): | ||
res = Response(res) | ||
return res | ||
|
||
async def _invoke_handler(self, f_or_coro, *args, **kwargs): | ||
r = f_or_coro(*args, **kwargs) | ||
try: | ||
r = await r | ||
except TypeError: | ||
# not a coroutine | ||
pass | ||
return r | ||
|
||
|
||
redirect = Response.redirect | ||
send_file = Response.send_file |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters