File: websocket.py

package info (click to toggle)
python-tinyrpc 0.6-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 324 kB
  • sloc: python: 1,700; makefile: 142; sh: 16
file content (87 lines) | stat: -rw-r--r-- 3,004 bytes parent folder | download | duplicates (3)
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)