-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathmicrodot_websocket_alt.py
118 lines (98 loc) · 3.52 KB
/
microdot_websocket_alt.py
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
import binascii
import hashlib
import select
import websocket as _websocket
from microdot import Response
class WebSocket:
CONT = 0
TEXT = 1
BINARY = 2
CLOSE = 8
PING = 9
PONG = 10
def __init__(self, request):
self.request = request
self.poll = select.poll()
self.poll.register(self.request.sock, select.POLLIN)
self.ws = _websocket.websocket(self.request.sock, True)
self.request.sock.setblocking(False)
def handshake(self):
response = self._handshake_response()
self.request.sock.write(b"HTTP/1.1 101 Switching Protocols\r\n")
self.request.sock.write(b"Upgrade: websocket\r\n")
self.request.sock.write(b"Connection: Upgrade\r\n")
self.request.sock.write(b"Sec-WebSocket-Accept: " + response + b"\r\n\r\n")
def receive(self):
while True:
self.poll.poll()
data = self.ws.read()
if data:
try:
data = data.decode()
except ValueError:
pass
return data
def send(self, data):
self.ws.write(data)
def close(self):
self.poll.unregister(self.request.sock)
self.ws.close()
def _handshake_response(self):
for header, value in self.request.headers.items():
h = header.lower()
if h == "connection" and not value.lower().startswith("upgrade"):
return self.request.app.abort(400)
elif h == "upgrade" and not value.lower() == "websocket":
return self.request.app.abort(400)
elif h == "sec-websocket-key":
websocket_key = value
if not websocket_key:
return self.request.app.abort(400)
d = hashlib.sha1(websocket_key.encode())
d.update(b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
return binascii.b2a_base64(d.digest())[:-1]
def websocket_upgrade(request):
"""Upgrade a request handler to a websocket connection.
This function can be called directly inside a route function to process a
WebSocket upgrade handshake, for example after the user's credentials are
verified. The function returns the websocket object::
@app.route('/echo')
def echo(request):
if not authenticate_user(request):
abort(401)
ws = websocket_upgrade(request)
while True:
message = ws.receive()
ws.send(message)
"""
ws = WebSocket(request)
ws.handshake()
@request.after_request
def after_request(request, response):
return Response.already_handled
return ws
def with_websocket(f):
"""Decorator to make a route a WebSocket endpoint.
This decorator is used to define a route that accepts websocket
connections. The route then receives a websocket object as a second
argument that it can use to send and receive messages::
@app.route('/echo')
@with_websocket
def echo(request, ws):
while True:
message = ws.receive()
ws.send(message)
"""
def wrapper(request, *args, **kwargs):
ws = websocket_upgrade(request)
try:
f(request, ws, *args, **kwargs)
except OSError as exc:
print("OS error!")
if exc.errno != 32 and exc.errno != 54:
raise
except BaseException as error:
print("An exception occurred: {}".format(error))
ws.close()
return ""
return wrapper