import gevent
import logging
import re
import types
from jadi import interface
[docs]def url(pattern):
"""
Exposes the decorated method of your :class:`HttpPlugin` via HTTP
:param pattern: URL regex (``^`` and ``$`` are implicit)
:type pattern: str
:rtype: function
Named capture groups will be fed to function as ``**kwargs``
"""
def decorator(f):
f.url_pattern = re.compile('^%s$' % pattern)
return f
return decorator
[docs]class BaseHttpHandler():
"""
Base class for everything that can process HTTP requests
"""
[docs] def handle(self, http_context):
"""
Should create a HTTP response in the given ``http_context`` and return
the plain output
:param http_context: HTTP context
:type http_context: :class:`aj.http.HttpContext`
"""
[docs]@interface
class HttpMiddleware(BaseHttpHandler):
def __init__(self, context):
self.context = context
[docs] def handle(self, http_context):
pass
[docs]@interface
class HttpPlugin():
"""
A base interface for HTTP request handling::
@component
class HelloHttp(HttpPlugin):
@url('/hello/(?P<name>.+)')
def get_page(self, http_context, name=None):
context.add_header('Content-Type', 'text/plain')
context.respond_ok()
return 'Hello, %s!' % name
"""
def __init__(self, context):
self.context = context
[docs] def handle(self, http_context):
"""
Finds and executes the handler for given request context (handlers are
methods decorated with :func:`url` )
:param http_context: HTTP context
:type http_context: :class:`aj.http.HttpContext`
:returns: reponse data
"""
for name, method in self.__class__.__dict__.items():
if hasattr(method, 'url_pattern'):
method = getattr(self, name)
match = method.url_pattern.match(http_context.path)
if match:
http_context.route_data = match.groupdict()
data = method(http_context, **http_context.route_data)
if isinstance(data, str):
data = data.encode('utf-8')
if isinstance(data, types.GeneratorType):
return data
else:
return [data]
[docs]@interface
class SocketEndpoint():
"""
Base interface for Socket.IO endpoints.
"""
plugin = None
"""arbitrary plugin ID for socket message routing"""
def __init__(self, context):
self.context = context
self.greenlets = []
[docs] def on_connect(self, message):
"""
Called on a successful client connection
"""
[docs] def on_disconnect(self, message):
"""
Called on a client disconnect
"""
[docs] def destroy(self):
"""
Destroys endpoint, killing the running greenlets
"""
for gl in self.greenlets:
gl.kill(block=False)
[docs] def on_message(self, message, *args):
"""
Called when a socket message arrives to this endpoint
"""
[docs] def spawn(self, target, *args, **kwargs):
"""
Spawns a greenlet in this endpoint, which will be auto-killed when the client disconnects
:param target: target function
"""
logging.debug(
'Spawning sub-Socket Greenlet (in a namespace): %s' % (
target.__name__
)
)
greenlet = gevent.spawn(target, *args, **kwargs)
self.greenlets.append(greenlet)
return greenlet
[docs] def send(self, data, plugin=None):
"""
Sends a message to the client.the
:param data: message object
:param plugin: routing ID (this endpoint's ID if not specified)
:type plugin: str
"""
self.context.worker.send_to_upstream({
'type': 'socket',
'message': {
'plugin': plugin or self.plugin,
'data': data,
},
})