File: sanic.py

package info (click to toggle)
python-engineio 4.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 772 kB
  • sloc: python: 9,613; makefile: 4; sh: 3
file content (143 lines) | stat: -rw-r--r-- 4,336 bytes parent folder | download
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import sys
from urllib.parse import urlsplit

try:  # pragma: no cover
    from sanic.response import HTTPResponse
    from sanic.websocket import WebSocketProtocol
except ImportError:
    HTTPResponse = None
    WebSocketProtocol = None


def create_route(app, engineio_server, engineio_endpoint):  # pragma: no cover
    """This function sets up the engine.io endpoint as a route for the
    application.

    Note that both GET and POST requests must be hooked up on the engine.io
    endpoint.
    """
    app.add_route(engineio_server.handle_request, engineio_endpoint,
                  methods=['GET', 'POST', 'OPTIONS'])
    try:
        app.enable_websocket()
    except AttributeError:
        # ignore, this version does not support websocket
        pass


def translate_request(request):  # pragma: no cover
    """This function takes the arguments passed to the request handler and
    uses them to generate a WSGI compatible environ dictionary.
    """
    class AwaitablePayload(object):
        def __init__(self, payload):
            self.payload = payload or b''

        async def read(self, length=None):
            if length is None:
                r = self.payload
                self.payload = b''
            else:
                r = self.payload[:length]
                self.payload = self.payload[length:]
            return r

    uri_parts = urlsplit(request.url)
    environ = {
        'wsgi.input': AwaitablePayload(request.body),
        'wsgi.errors': sys.stderr,
        'wsgi.version': (1, 0),
        'wsgi.async': True,
        'wsgi.multithread': False,
        'wsgi.multiprocess': False,
        'wsgi.run_once': False,
        'SERVER_SOFTWARE': 'sanic',
        'REQUEST_METHOD': request.method,
        'QUERY_STRING': uri_parts.query or '',
        'RAW_URI': request.url,
        'SERVER_PROTOCOL': 'HTTP/' + request.version,
        'REMOTE_ADDR': '127.0.0.1',
        'REMOTE_PORT': '0',
        'SERVER_NAME': 'sanic',
        'SERVER_PORT': '0',
        'sanic.request': request
    }

    for hdr_name, hdr_value in request.headers.items():
        hdr_name = hdr_name.upper()
        if hdr_name == 'CONTENT-TYPE':
            environ['CONTENT_TYPE'] = hdr_value
            continue
        elif hdr_name == 'CONTENT-LENGTH':
            environ['CONTENT_LENGTH'] = hdr_value
            continue

        key = 'HTTP_%s' % hdr_name.replace('-', '_')
        if key in environ:
            hdr_value = '%s,%s' % (environ[key], hdr_value)

        environ[key] = hdr_value

    environ['wsgi.url_scheme'] = environ.get('HTTP_X_FORWARDED_PROTO', 'http')

    path_info = uri_parts.path

    environ['PATH_INFO'] = path_info
    environ['SCRIPT_NAME'] = ''

    return environ


def make_response(status, headers, payload, environ):  # pragma: no cover
    """This function generates an appropriate response object for this async
    mode.
    """
    headers_dict = {}
    content_type = None
    for h in headers:
        if h[0].lower() == 'content-type':
            content_type = h[1]
        else:
            headers_dict[h[0]] = h[1]
    return HTTPResponse(body_bytes=payload, content_type=content_type,
                        status=int(status.split()[0]), headers=headers_dict)


class WebSocket(object):  # pragma: no cover
    """
    This wrapper class provides a sanic WebSocket interface that is
    somewhat compatible with eventlet's implementation.
    """
    def __init__(self, handler):
        self.handler = handler
        self._sock = None

    async def __call__(self, environ):
        request = environ['sanic.request']
        protocol = request.transport.get_protocol()
        self._sock = await protocol.websocket_handshake(request)

        self.environ = environ
        await self.handler(self)

    async def close(self):
        await self._sock.close()

    async def send(self, message):
        await self._sock.send(message)

    async def wait(self):
        data = await self._sock.recv()
        if not isinstance(data, bytes) and \
                not isinstance(data, str):
            raise IOError()
        return data


_async = {
    'asyncio': True,
    'create_route': create_route,
    'translate_request': translate_request,
    'make_response': make_response,
    'websocket': WebSocket if WebSocketProtocol else None,
}