Переглянути джерело

Implement more tests with yocto_http

Ludovic Gasc (GMLudo) 10 роки тому
батько
коміт
e244e6a4da

+ 21 - 1
frameworks/Python/AsyncIO/benchmark_config.json

@@ -60,10 +60,30 @@
       "display_name": "API-Hour+AsyncIO",
       "notes": "Python 3 + API-Hour + AsyncIO"
     },
+    "dbs": {
+      "setup_file": "yocto_http/setup",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "port": 8081,
+      "approach": "Stripped",
+      "classification": "Platform",
+      "database": "None",
+      "framework": "AsyncIO",
+      "language": "Python",
+      "orm": "Raw",
+      "platform": "API-Hour",
+      "webserver": "Gunicorn",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "API-Hour+AsyncIO",
+      "notes": "Python 3 + API-Hour + AsyncIO"
+    },
     "plaintext": {
       "setup_file": "yocto_http/setup",
       "plaintext_url": "/plaintext",
-      "port": 8084,
+      "port": 8082,
       "approach": "Stripped",
       "classification": "Platform",
       "database": "None",

+ 1 - 1
frameworks/Python/AsyncIO/requirements.txt

@@ -1,7 +1,7 @@
 aiohttp==0.16.3
 -e git+https://github.com/Eyepea/aiohttp_jinja2.git@c9675e5c1e1ee7741b30aea8d8fbffcde016c7a0#egg=aiohttp_jinja2-master
 aiopg==0.7.0
-api-hour==0.6.2
+-e git+https://github.com/Eyepea/API-Hour.git@577abbdcbb8cc2810dad46e260b338b15db4d0e3#egg=api_hour-master
 asyncio-redis==0.13.4
 chardet==2.3.0
 gunicorn==19.3.0

+ 1 - 1
frameworks/Python/AsyncIO/yocto_http/etc/hello/api_hour/gunicorn_conf.py

@@ -7,7 +7,7 @@ workers = multiprocessing.cpu_count() * 2
 if _is_travis:
     workers = 2
 
-bind = ['0.0.0.0:8080', '0.0.0.0:8084']
+bind = ['0.0.0.0:8080', '0.0.0.0:8081', '0.0.0.0:8082']
 keepalive = 120
 errorlog = '-'
 pidfile = 'api_hour.pid'

+ 11 - 0
frameworks/Python/AsyncIO/yocto_http/hello/__init__.py

@@ -3,6 +3,7 @@ import asyncio
 import os
 
 import aiopg
+import jinja2
 import psycopg2.extras
 import asyncio_redis
 from asyncio_redis.protocol import HiRedisProtocol
@@ -10,6 +11,7 @@ import api_hour
 
 from . import endpoints
 from . import servers
+from .utils import yocto_http
 
 LOG = logging.getLogger(__name__)
 
@@ -17,9 +19,18 @@ LOG = logging.getLogger(__name__)
 class Container(api_hour.Container):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
+        self.servers['http'] = yocto_http.Application(loop=kwargs['loop'])
+        self.servers['http'].ah_container = self # keep a reference to Container
+        # routes
+        self.servers['http'].add_route('/db', endpoints.world.db)
+        self.servers['http'].add_route('/queries', endpoints.world.queries)
+        self.servers['http'].add_route('/fortunes', endpoints.world.fortunes, content_type='text/html; charset=UTF-8')
+        self.servers['http'].add_route('/updates', endpoints.world.updates)
+        self.servers['http']['j2_env'] = jinja2.Environment(loader=jinja2.PackageLoader('hello'))
 
     def make_servers(self):
         return [servers.yocto_http.YoctoHttpJson,
+                self.servers['http'].handler,
                 servers.yocto_http.YoctoHttpText]
 
     @asyncio.coroutine

+ 8 - 55
frameworks/Python/AsyncIO/yocto_http/hello/endpoints/world.py

@@ -1,9 +1,6 @@
 import logging
 import asyncio
-
-from aiohttp.web import Response
-from api_hour.plugins.aiohttp import JSON
-import aiohttp_jinja2
+import ujson
 
 from ..services import queries_number
 from ..services.world import get_random_record, get_random_records, update_random_records, get_fortunes
@@ -11,76 +8,32 @@ from ..services import redis
 
 LOG = logging.getLogger(__name__)
 
[email protected]
-def json(request):
-    """Test type 1: JSON serialization"""
-    return JSON({'message': 'Hello, World!'})
-
 @asyncio.coroutine
 def db(request):
     """Test type 2: Single database query"""
     container = request.app.ah_container
 
-    return JSON((yield from get_random_record(container)))
-
[email protected]
-def db_redis(request):
-    """Test type 2: Single database query"""
-    container = request.app.ah_container
-
-    return JSON((yield from redis.get_random_record(container)))
+    return ujson.dumps((yield from get_random_record(container)))
 
 @asyncio.coroutine
 def queries(request):
     """Test type 3: Multiple database queries"""
     container = request.app.ah_container
-    limit = queries_number(request.GET.get('queries', 1))
-
-    return JSON((yield from get_random_records(container, limit)))
-
[email protected]
-def queries_redis(request):
-    """Test type 3: Multiple database queries"""
-    container = request.app.ah_container
-    limit = queries_number(request.GET.get('queries', 1))
+    limit = queries_number(request.params.get('queries', 1))
 
-    return JSON((yield from redis.get_random_records(container, limit)))
+    return ujson.dumps((yield from get_random_records(container, limit)))
 
 @asyncio.coroutine
 def fortunes(request):
     """Test type 4: Fortunes"""
     container = request.app.ah_container
-
-    return aiohttp_jinja2.render_template('fortunes.html.j2',
-                                          request,
-                                          {'fortunes': (yield from get_fortunes(container))})
-
[email protected]
-def fortunes_redis(request):
-    """Test type 4: Fortunes"""
-    container = request.app.ah_container
-
-    return aiohttp_jinja2.render_template('fortunes.html.j2',
-                                          request,
-                                          {'fortunes': (yield from redis.get_fortunes(container))})
+    template = request.app['j2_env'].get_template('fortunes.html.j2')
+    return template.render({'fortunes': (yield from get_fortunes(container))})
 
 @asyncio.coroutine
 def updates(request):
     """Test type 5: Database updates"""
     container = request.app.ah_container
-    limit = queries_number(request.GET.get('queries', 1))
-
-    return JSON((yield from update_random_records(container, limit)))
+    limit = queries_number(request.params.get('queries', 1))
 
[email protected]
-def updates_redis(request):
-    """Test type 5: Database updates"""
-    container = request.app.ah_container
-    limit = queries_number(request.GET.get('queries', 1))
-
-    return JSON((yield from redis.update_random_records(container, limit)))
-
[email protected]
-def plaintext(request):
-    """Test type 6: Plaintext"""
-    return Response(text='Hello, World!')
+    return ujson.dumps((yield from update_random_records(container, limit)))

+ 1 - 1
frameworks/Python/AsyncIO/yocto_http/hello/servers/yocto_http.py

@@ -2,7 +2,7 @@ import asyncio
 
 import ujson
 
-from ..utils import generate_http_response
+from ..utils.yocto_http.utils import generate_http_response
 
 class YoctoHttpJson(asyncio.Protocol):
     def connection_made(self, transport):

+ 0 - 11
frameworks/Python/AsyncIO/yocto_http/hello/utils/__init__.py

@@ -1,14 +1,3 @@
 import logging
-from wsgiref.handlers import format_date_time
 
 LOG = logging.getLogger(__name__)
-
-def generate_http_response(payload, content_type='application/json'):
-    return ("""HTTP/1.1 200 OK
-CONTENT-TYPE: %s
-CONTENT-LENGTH: %s
-CONNECTION: keep-alive
-DATE: %s
-SERVER: yocto_http/0.0.1
-
-%s""" % (content_type, len(payload), format_date_time(None), payload)).encode('utf-8')

+ 1 - 0
frameworks/Python/AsyncIO/yocto_http/hello/utils/yocto_http/__init__.py

@@ -0,0 +1 @@
+from .application import Application

+ 78 - 0
frameworks/Python/AsyncIO/yocto_http/hello/utils/yocto_http/application.py

@@ -0,0 +1,78 @@
+import asyncio
+import logging
+from collections import OrderedDict
+
+from .request import Request
+from .utils import generate_http_response
+
+log = logging.getLogger(__name__)
+
+class Application(dict):
+
+    def __init__(self, default_encoding='utf-8', decode_headers=False, loop=None):
+        super(Application, self).__init__()
+        self.default_encoding = default_encoding
+        if loop is None:
+            loop = asyncio.get_event_loop()
+        self.decode_headers = decode_headers
+        self.loop = loop
+        self._route = OrderedDict()
+
+    def add_route(self, path, endpoint, content_type='application/json'):
+        assert callable(endpoint), endpoint
+        if not asyncio.iscoroutinefunction(endpoint):
+            endpoint = asyncio.coroutine(endpoint)
+        endpoint.content_type = content_type
+        self._route[path] = endpoint
+
+    @asyncio.coroutine
+    def handler(self, reader, writer):
+        # while True:
+            buffer = b''
+            while b'\r\n\r\n' not in buffer:
+                buffer += yield from reader.read(100)
+            lines = buffer[:-2].decode(self.default_encoding).split('\r\n')
+
+            url = lines[0].split(' ')[1].split('?')
+            path = url[0]
+            params = OrderedDict()
+            if len(url) == 2:
+                k, v = url[1].split('=', 1)  # @TODO: support several parameters
+                params[k] = v
+
+            # log.info('Received HTTP request from %r for "%s" route',
+            #          writer.get_extra_info('peername'),
+            #          uri)
+
+            headers = {}
+            if self.decode_headers:
+                for line in lines:
+                    k, v = line.split(': ', 1)
+                    headers[k] = v
+                log.debug("HTTP Headers: %r",
+                          headers)
+
+            if path in self._route:
+                request = Request(app=self,
+                                  path=path,
+                                  params=params,
+                                  headers=headers,
+                                  reader=reader, writer=writer,
+                                  encoding=self.default_encoding)
+                try:
+                    response = yield from self._route[path](request)
+                    writer.write(generate_http_response(response, content_type=self._route[path].content_type))
+                    try:
+                        yield from writer.drain()
+                    except ConnectionError:
+                        pass
+                except Exception as e:
+                    log.exception(e)
+            else:
+                log.error('No route for the request "%s"', path)
+                writer.write(generate_http_response(''))
+                try:
+                    yield from writer.drain()
+                except ConnectionError:
+                    pass
+            writer.close()

+ 9 - 0
frameworks/Python/AsyncIO/yocto_http/hello/utils/yocto_http/request.py

@@ -0,0 +1,9 @@
+class Request:
+    def __init__(self, app, path, params, headers, reader, writer, encoding='utf-8'):
+        self.app = app
+        self.path = path
+        self.params = params
+        self.headers = headers
+        self.reader = reader
+        self.writer = writer
+        self.encoding = encoding

+ 11 - 0
frameworks/Python/AsyncIO/yocto_http/hello/utils/yocto_http/utils.py

@@ -0,0 +1,11 @@
+from wsgiref.handlers import format_date_time
+
+def generate_http_response(payload, content_type='application/json'):
+    return ("""HTTP/1.1 200 OK
+CONTENT-TYPE: %s
+CONTENT-LENGTH: %s
+CONNECTION: keep-alive
+DATE: %s
+SERVER: yocto_http/0.0.2
+
+%s""" % (content_type, len(payload), format_date_time(None), payload)).encode('utf-8')