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
|
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import Queue
from . import ServerTransport
from geventwebsocket.resource import WebSocketApplication, Resource
class WSServerTransport(ServerTransport):
'''
Requires :py:mod:`geventwebsocket`.
Due to the nature of WS, this transport has a few pecularities: It must
be run in a thread, greenlet or some other form of concurrent execution
primitive.
This is due to
:py:prop:`~tinyrpc.transports.websocket.WSServerTransport.handle` which is
a :py:class:`geventwebsocket.resource.Resource` that joins a wsgi handler
for the / and a WebSocket handler for the /ws path. These resource is
used in combination with a :py:class:`geventwebsocket.server.WebSocketServer`
that blocks while waiting for a call to
:py:func:`~tinyrpc.transports.wsgi.WSServerTransport.send_reply`.
The parameter ``queue_class`` must be used to supply a proper queue class
for the chosen concurrency mechanism (i.e. when using :py:mod:`gevent`,
set it to :py:class:`gevent.queue.Queue`).
:param queue_class: The Queue class to use.
:param wsgi_handler: Can be used to change the standard response to a
http request to the /
'''
def __init__(self, queue_class=Queue.Queue, wsgi_handler=None):
self._queue_class = queue_class
self.messages = queue_class()
def static_wsgi_app(environ, start_response):
start_response("200 OK", [("Content-Type", "text/html")])
return 'Ready for WebSocket connection in /ws'
self.handle = Resource(
{'/': static_wsgi_app if wsgi_handler is None else wsgi_handler,
'/ws': WSApplicationFactory(self.messages, queue_class)})
def receive_message(self):
return self.messages.get()
def send_reply(self, context, reply):
context.put(reply)
class WSApplicationFactory(object):
'''
Creates WebSocketApplications with a messages queue and the queue_class
needed for the communication with the WSServerTransport.
'''
def __init__(self, messages, queue_class):
self.messages = messages
self._queue_class = queue_class
def __call__(self, ws):
'''
The fake __init__ for the WSApplication
'''
app = WSApplication(ws)
app.messages = self.messages
app._queue_class = self._queue_class
return app
@classmethod
def protocol(cls):
return WebSocketApplication.protocol()
class WSApplication(WebSocketApplication):
'''
This class is the bridge between the WSServerTransport and the WebSocket
protocol implemented by
:py:class:`geventwebsocket.resource.WebSocketApplication`
'''
def on_message(self, msg, *args, **kwargs):
# create new context
context = self._queue_class()
self.messages.put((context, msg))
response = context.get()
self.ws.send(response, *args, **kwargs)
|