2016-05-28 20:17:02 +00:00
|
|
|
"""
|
|
|
|
Colleciton of utility functions that implement small portions of the RFC6455
|
|
|
|
WebSockets Protocol Useful for building WebSocket clients and servers.
|
2015-07-08 07:34:10 +00:00
|
|
|
|
2016-05-28 20:17:02 +00:00
|
|
|
Emphassis is on readabilty, simplicity and modularity, not performance or
|
|
|
|
completeness
|
2015-09-05 16:15:47 +00:00
|
|
|
|
2016-05-28 20:17:02 +00:00
|
|
|
This is a work in progress and does not yet contain all the utilites need to
|
|
|
|
create fully complient client/servers #
|
|
|
|
Spec: https://tools.ietf.org/html/rfc6455
|
2015-07-08 07:34:10 +00:00
|
|
|
|
2016-05-28 20:17:02 +00:00
|
|
|
The magic sha that websocket servers must know to prove they understand
|
|
|
|
RFC6455
|
|
|
|
"""
|
2015-07-08 07:34:10 +00:00
|
|
|
|
2015-09-05 16:15:47 +00:00
|
|
|
from __future__ import absolute_import
|
|
|
|
import base64
|
|
|
|
import hashlib
|
|
|
|
import os
|
2015-09-20 22:44:17 +00:00
|
|
|
|
2015-09-17 13:16:12 +00:00
|
|
|
import six
|
2016-05-31 23:12:10 +00:00
|
|
|
|
2016-06-08 11:07:56 +00:00
|
|
|
from netlib import http, strutils
|
2015-09-05 16:15:47 +00:00
|
|
|
|
2015-09-20 22:44:17 +00:00
|
|
|
websockets_magic = b'258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
|
2015-07-08 07:34:10 +00:00
|
|
|
VERSION = "13"
|
|
|
|
|
2015-08-10 18:44:36 +00:00
|
|
|
|
2015-07-08 07:34:10 +00:00
|
|
|
class Masker(object):
|
|
|
|
|
|
|
|
"""
|
|
|
|
Data sent from the server must be masked to prevent malicious clients
|
|
|
|
from sending data over the wire in predictable patterns
|
|
|
|
|
|
|
|
Servers do not have to mask data they send to the client.
|
|
|
|
https://tools.ietf.org/html/rfc6455#section-5.3
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, key):
|
|
|
|
self.key = key
|
|
|
|
self.offset = 0
|
|
|
|
|
|
|
|
def mask(self, offset, data):
|
2015-09-20 22:44:17 +00:00
|
|
|
result = bytearray(data)
|
|
|
|
if six.PY2:
|
|
|
|
for i in range(len(data)):
|
|
|
|
result[i] ^= ord(self.key[offset % 4])
|
|
|
|
offset += 1
|
|
|
|
result = str(result)
|
|
|
|
else:
|
|
|
|
|
|
|
|
for i in range(len(data)):
|
|
|
|
result[i] ^= self.key[offset % 4]
|
|
|
|
offset += 1
|
|
|
|
result = bytes(result)
|
2015-07-08 07:34:10 +00:00
|
|
|
return result
|
|
|
|
|
|
|
|
def __call__(self, data):
|
|
|
|
ret = self.mask(self.offset, data)
|
|
|
|
self.offset += len(ret)
|
|
|
|
return ret
|
|
|
|
|
2015-08-10 18:44:36 +00:00
|
|
|
|
2015-07-08 07:34:10 +00:00
|
|
|
class WebsocketsProtocol(object):
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def client_handshake_headers(self, key=None, version=VERSION):
|
|
|
|
"""
|
|
|
|
Create the headers for a valid HTTP upgrade request. If Key is not
|
|
|
|
specified, it is generated, and can be found in sec-websocket-key in
|
|
|
|
the returned header set.
|
|
|
|
|
2016-05-31 23:12:10 +00:00
|
|
|
Returns an instance of http.Headers
|
2015-07-08 07:34:10 +00:00
|
|
|
"""
|
|
|
|
if not key:
|
2015-09-21 23:48:35 +00:00
|
|
|
key = base64.b64encode(os.urandom(16)).decode('ascii')
|
2016-05-31 23:12:10 +00:00
|
|
|
return http.Headers(
|
2015-09-25 16:24:18 +00:00
|
|
|
sec_websocket_key=key,
|
|
|
|
sec_websocket_version=version,
|
|
|
|
connection="Upgrade",
|
|
|
|
upgrade="websocket",
|
|
|
|
)
|
2015-07-08 07:34:10 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def server_handshake_headers(self, key):
|
|
|
|
"""
|
|
|
|
The server response is a valid HTTP 101 response.
|
|
|
|
"""
|
2016-05-31 23:12:10 +00:00
|
|
|
return http.Headers(
|
2015-09-25 16:24:18 +00:00
|
|
|
sec_websocket_accept=self.create_server_nonce(key),
|
|
|
|
connection="Upgrade",
|
|
|
|
upgrade="websocket"
|
|
|
|
)
|
2015-07-08 07:34:10 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def check_client_handshake(self, headers):
|
2015-09-21 23:48:35 +00:00
|
|
|
if headers.get("upgrade") != "websocket":
|
2015-07-08 07:34:10 +00:00
|
|
|
return
|
2015-09-25 16:24:18 +00:00
|
|
|
return headers.get("sec-websocket-key")
|
2015-07-08 07:34:10 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def check_server_handshake(self, headers):
|
2015-09-21 23:48:35 +00:00
|
|
|
if headers.get("upgrade") != "websocket":
|
2015-07-08 07:34:10 +00:00
|
|
|
return
|
2015-09-25 16:24:18 +00:00
|
|
|
return headers.get("sec-websocket-accept")
|
2015-07-08 07:34:10 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def create_server_nonce(self, client_nonce):
|
2016-06-08 11:07:56 +00:00
|
|
|
return base64.b64encode(hashlib.sha1(strutils.always_bytes(client_nonce) + websockets_magic).digest())
|