From e0b9260644bc90b480614015fac237cef635cbce Mon Sep 17 00:00:00 2001 From: Channel Cat Date: Sun, 2 Oct 2016 18:45:44 -0700 Subject: [PATCH] Sanic can now parse args and json --- sanic/server.py | 65 ++++++++++++++++++++++++++++++++++++++----------- test.aiohttp.py | 13 ++++++++++ test.py | 9 +++++++ 3 files changed, 73 insertions(+), 14 deletions(-) create mode 100644 test.aiohttp.py diff --git a/sanic/server.py b/sanic/server.py index 3deee929..4b9f9673 100644 --- a/sanic/server.py +++ b/sanic/server.py @@ -5,6 +5,8 @@ import signal import functools import httptools import logging +from ujson import loads as json_loads +from urllib.parse import parse_qs import httptools try: @@ -21,7 +23,7 @@ from .response import HTTPResponse PRINT = 0 class Request: - __slots__ = ('protocol', 'url', 'headers', 'version', 'method') + __slots__ = ('protocol', 'url', 'headers', 'version', 'method', 'query_string', 'body', 'parsed_json', 'parsed_args') def __init__(self, protocol, url, headers, version, method): self.protocol = protocol @@ -30,6 +32,35 @@ class Request: self.version = version self.method = method + # Capture query string + query_string_position = self.url.find(b"?") + if query_string_position != -1: + self.query_string = self.url[query_string_position+1:] + self.url = self.url[:query_string_position] + else: + self.query_string = None + + # Init but do not inhale + self.body = None + self.parsed_json = None + self.parsed_args = None + + @property + def json(self): + if not self.parsed_json: + if not self.body: + raise ValueError("No body to parse") + self.parsed_json = json_loads(self.body) + + return self.parsed_json + + @property + def args(self): + if not self.parsed_args and self.query_string: + self.parsed_args = {k:v if len(v)>1 else v[0] for k,v in parse_qs(self.query_string).items()} + + return self.parsed_args + class HttpProtocol(asyncio.Protocol): __slots__ = ('loop', @@ -79,8 +110,8 @@ class HttpProtocol(asyncio.Protocol): try: #print(data) self.parser.feed_data(data) - except httptools.parser.errors.HttpParserError: - #log.error("Invalid request data, connection closed") + except httptools.parser.errors.HttpParserError as e: + log.error("Invalid request data, connection closed ({})".format(e)) self.transport.close() def on_url(self, url): @@ -98,21 +129,27 @@ class HttpProtocol(asyncio.Protocol): method=self.parser.get_method() ) #print("res {} - {}".format(n, self.request)) - self.loop.call_soon(self.handle, self.request) + + def on_body(self, body): + self.request.body = body + def on_message_complete(self): + self.loop.create_task(self.get_response(self.request)) # -------------------------------------------- # # Responding # -------------------------------------------- # - def handle(self, request): + async def get_response(self, request): handler = self.router.get(request.url) - if handler.is_async: - future = asyncio.Future() - self.loop.create_task(self.handle_response(future, handler, request)) - future.add_done_callback(self.handle_result) - else: - response = handler(request) - self.write_response(request, response) + try: + if handler.is_async: + response = await handler(request) + else: + response = handler(request) + except Exception as e: + response = HTTPResponse("Error: {}".format(e)) + + self.write_response(request, response) def write_response(self, request, response): #print("response - {} - {}".format(self.n, self.request)) @@ -122,8 +159,8 @@ class HttpProtocol(asyncio.Protocol): #print("KA - {}".format(self.parser.should_keep_alive())) if not keep_alive: self.transport.close() - except: - log.error("Writing request failed, connection closed") + except Exception as e: + log.error("Writing request failed, connection closed {}".format(e)) self.transport.close() self.parser = None diff --git a/test.aiohttp.py b/test.aiohttp.py new file mode 100644 index 00000000..8e33e8d0 --- /dev/null +++ b/test.aiohttp.py @@ -0,0 +1,13 @@ +from aiohttp import web + +async def handle(request): + name = request.match_info.get('name', "Anonymous") + text = "Hello, " + name + return web.Response(text=text) + + +app = web.Application() +app.router.add_get('/', handle) +app.router.add_get('/{name}', handle) + +web.run_app(app) diff --git a/test.py b/test.py index 4d32cba6..8a059f69 100644 --- a/test.py +++ b/test.py @@ -6,8 +6,17 @@ app = Sanic("test") @app.route("/") async def test(request): return json({ "test": True }) + @app.route("/text") def test(request): return text('hi') +@app.route("/post_json") +def test(request): + return json({ "received": True, "message": request.json }) + +@app.route("/query_string") +def test(request): + return json({ "parsed": True, "args": request.args, "url": request.url, "query_string": request.query_string }) + app.run(host="0.0.0.0") \ No newline at end of file