def process_handshake_header(self, headers): protocols = [] extensions = [] headers = headers.strip() for header_line in headers.split('\r\n'): header, value = header_line.split(':', 1) header = header.strip().lower() value = value.strip().lower() if header == 'upgrade' and value != 'websocket': raise HandshakeError("Invalid Upgrade header: %s" % value) elif header == 'connection' and value != 'upgrade': raise HandshakeError("Invalid Connection header: %s" % value) elif header == 'sec-websocket-accept': match = b64encode(sha1(self.key + WS_KEY).digest()) if value != match.lower(): raise HandshakeError("Invalid challenge response: %s" % value) elif header == 'sec-websocket-protocol': protocols = ','.join(value) elif header == 'sec-websocket-extensions': extensions = ','.join(value) return protocols, extensions
def process_handshake_header(self, headers): """ Read the upgrade handshake's response headers and validate them against :rfc:`6455`. """ protocols = [] extensions = [] headers = headers.strip() for header_line in headers.split(b'\r\n'): header, value = header_line.split(b':', 1) header = header.strip().lower() value = value.strip().lower() if header == b'upgrade' and value != b'websocket': raise HandshakeError("Invalid Upgrade header: %s" % value) elif header == b'connection' and value != b'upgrade': raise HandshakeError("Invalid Connection header: %s" % value) elif header == b'sec-websocket-accept': match = b64encode(sha1(self.key + WS_KEY).digest()) if value != match.lower(): raise HandshakeError("Invalid challenge response: %s" % value) elif header == b'sec-websocket-protocol': protocols.extend([x.strip() for x in value.split(b',')]) elif header == b'sec-websocket-extensions': extensions.extend([x.strip() for x in value.split(b',')]) return protocols, extensions
def connect(self): """ Starts the upgrade handshake with the remote endpoint. """ self._write(self.handshake_request) response = b'' doubleCLRF = b'\r\n\r\n' while True: bytes = self.sock.recv(128) if not bytes: break response += bytes if doubleCLRF in response: break if not response: self.close_connection() raise HandshakeError("Invalid response") headers, _, body = response.partition(doubleCLRF) response_line, _, headers = headers.partition(b'\r\n') try: self.process_response_line(response_line) self.protocols, self.extensions = self.process_handshake_header(headers) except HandshakeError: self.close_connection() raise self.handshake_ok() if body: self.process(body)
def process_response_line(self, response_line): """ Ensure that we received a HTTP `101` status code in response to our request and if not raises :exc:`HandshakeError`. """ protocol, code, status = response_line.split(b' ', 2) if code != b'101': raise HandshakeError("Invalid response status: %s %s" % (code, status))
def __call__(self, environ, start_response): # Initial handshake validation try: if 'websocket' not in environ.get('upgrade.protocol', environ.get('HTTP_UPGRADE', '')).lower(): raise HandshakeError("Upgrade protocol is not websocket") if environ.get('REQUEST_METHOD') != 'GET': raise HandshakeError('Method is not GET') key = environ.get('HTTP_SEC_WEBSOCKET_KEY') if key: ws_key = base64.b64decode(key) if len(ws_key) != 16: raise HandshakeError("WebSocket key's length is invalid") else: raise HandshakeError("Not a valid HyBi WebSocket request") version = environ.get('HTTP_SEC_WEBSOCKET_VERSION') version_is_valid = False if version: try: version = int(version) except: pass else: version_is_valid = version in self.versions if not version_is_valid: raise HandshakeError('Unsupported WebSocket version: %s' % version) environ['websocket.version'] = str(version) except HandshakeError, e: if self.fallback_app: return self.fallback_app(environ, start_response) else: start_response( "400 Bad Handshake", [('Sec-WebSocket-Version', self.supported_versions)]) return [str(e)]
def wrapper(*args, **kwargs): try: if 'websocket' not in request.environ.get('upgrade.protocol', ''): raise HandshakeError("Upgrade protocol is not websocket") if request.environ.get('REQUEST_METHOD') != 'GET': raise HandshakeError('Method is not GET') key = request.environ.get('HTTP_SEC_WEBSOCKET_KEY') if key: ws_key = base64.b64decode(key) if len(ws_key) != 16: raise HandshakeError("WebSocket key's length is invalid") else: raise HandshakeError("Not a valid HyBi WebSocket request") version = request.environ.get('HTTP_SEC_WEBSOCKET_VERSION') if version: if version != str(WS_VERSION): raise HandshakeError('Unsupported WebSocket version') request.environ['websocket.version'] = str(WS_VERSION) else: raise HandshakeError('WebSocket version required') except HandshakeError, e: raise HTTPError(code=400, output=e)
def __call__(self, environ, start_response): # Initial handshake validation try: if 'websocket' not in environ.get('upgrade.protocol', '').lower(): raise HandshakeError("Upgrade protocol is not websocket") if environ.get('REQUEST_METHOD') != 'GET': raise HandshakeError('Method is not GET') key = environ.get('HTTP_SEC_WEBSOCKET_KEY') if key: ws_key = base64.b64decode(key) if len(ws_key) != 16: raise HandshakeError("WebSocket key's length is invalid") else: raise HandshakeError("Not a valid HyBi WebSocket request") version = environ.get('HTTP_SEC_WEBSOCKET_VERSION') if version: if version != str(WS_VERSION): raise HandshakeError('Unsupported WebSocket version') environ['websocket.version'] = str(WS_VERSION) else: raise HandshakeError('WebSocket version required') except HandshakeError, e: if self.fallback_app: return self.fallback_app(environ, start_response) else: start_response("400 Bad Handshake", []) return [str(e)]
def check_response_line(self, response_line): """ Ensure that we received a HTTP valid status code in response to our request and if not raises :exc:`HandshakeError`. """ protocol, code, status = response_line.split(b' ', 2) if code in (b'301', b'302', b'303', b'307', b'308'): return 'redirect' elif code != b'101': raise HandshakeError("Invalid response status: %s %s" % (code, status)) return 'ok'
def connect(self): """ Connects this websocket and starts the upgrade handshake with the remote endpoint. """ if self.scheme == "wss": # default port is now 443; upgrade self.sender to send ssl self.sock = self.ssl_context.wrap_socket(self.sock, server_hostname=self.host) self._is_secure = True self.sock.connect(self.bind_addr) self._write(self.handshake_request) response = b'' doubleCLRF = b'\r\n\r\n' while True: bytes = self.sock.recv(128) if not bytes: break response += bytes if doubleCLRF in response: break if not response: self.close_connection() raise HandshakeError("Invalid response") headers, _, body = response.partition(doubleCLRF) response_line, _, headers = headers.partition(b'\r\n') try: self.process_response_line(response_line) self.protocols, self.extensions = self.process_handshake_header( headers) except HandshakeError: self.close_connection() raise self.handshake_ok() if body: self.process(body)
def connect(self): #self.sock.settimeout(3) parts = urlsplit(self.url) host, port = parts.netloc, 80 if parts.scheme == "wss": # default port is now 443; upgrade self.sender to send ssl self.sock = ssl.wrap_socket(self.sock) port = 443 self.sender = self.sock.sendall if ':' in host: host, port = parts.netloc.split(':') self.sock.connect((host, int(port))) self.sender(self.handshake_request) response = '' while True: bytes = self.sock.recv(128) if not bytes: break response += bytes if '\r\n\r\n' in response: break if not response: self.close_connection() raise HandshakeError("Invalid response") headers, _, body = response.partition('\r\n\r\n') response_line, _, headers = headers.partition('\r\n') self.__buffer = body try: self.process_response_line(response_line) self.protocols, self.extensions = self.process_handshake_header(headers) except HandshakeError: self.close_connection() raise self.handshake_ok()
def connect(self): #self.sock.settimeout(3) if self.scheme == "wss": self.ssl_options = {"server_hostname": self.host} # default port is now 443; upgrade self.sender to send ssl self.sock = ssl.SSLSocket(self.sock, **self.ssl_options) # self.sock = ssl.wrap_socket(self.sock) # altered by tectract self.sender = self.sock.sendall self.sock.connect((self.host, int(self.port))) self.sender(self.handshake_request) response = enc('') doubleCLRF = enc('\r\n\r\n') while True: bytes = self.sock.recv(128) if not bytes: break response += bytes if doubleCLRF in response: break if not response: self.close_connection() raise HandshakeError("Invalid response") headers, _, body = response.partition(doubleCLRF) response_line, _, headers = headers.partition(enc('\r\n')) try: self.process_response_line(response_line) self.protocols, self.extensions = self.process_handshake_header( headers) except HandshakeError: self.close_connection() raise self.handshake_ok() if body: self.process(body)
def connect(self): """ Connects this websocket and starts the upgrade handshake with the remote endpoint. """ if self.scheme == "wss": # default port is now 443; upgrade self.sender to send ssl self.sock = ssl.wrap_socket(self.sock) self.sock.connect((self.host, self.port)) self.sock.sendall(self.handshake_request) response = enc('') doubleCLRF = enc('\r\n\r\n') while True: bytes = self.sock.recv(128) if not bytes: break response += bytes if doubleCLRF in response: break if not response: self.close_connection() raise HandshakeError("Invalid response") headers, _, body = response.partition(doubleCLRF) response_line, _, headers = headers.partition(enc('\r\n')) try: self.process_response_line(response_line) self.protocols, self.extensions = self.process_handshake_header( headers) except HandshakeError: self.close_connection() raise self.handshake_ok() if body: self.process(body)
def connect(self): parts = urlsplit(self.url) host, port = parts.netloc, 80 if ':' in host: host, port = parts.netloc.split(':') self.sock.connect((host, int(port))) if parts.scheme == "wss": self.sock = ssl.wrap_socket(self.sock) self.write_to_connection(self.handshake_request) response = '' while True: bytes = self.sock.recv(128) if not bytes: break response += bytes if '\r\n\r\n' in response: break if not response: self.close_connection() raise HandshakeError("Invalid response") headers, _, body = response.partition('\r\n\r\n') response_line, _, headers = headers.partition('\r\n') self.__buffer = body try: self.process_response_line(response_line) protocols, extensions = self.process_handshake_header(headers) except HandshakeError: self.close_connection() raise self._th.start() self.opened(protocols, extensions)
def connect(self): #self.sock.settimeout(3) if self.scheme == "wss": # default port is now 443; upgrade self.sender to send ssl self.sock = ssl.wrap_socket(self.sock) self.sender = self.sock.sendall self.sock.connect((self.host, int(self.port))) self.sender(self.handshake_request) response = '' while True: bytes = self.sock.recv(128) if not bytes: break response += bytes if '\r\n\r\n' in response: break if not response: self.close_connection() raise HandshakeError("Invalid response") headers, _, body = response.partition('\r\n\r\n') response_line, _, headers = headers.partition('\r\n') try: self.process_response_line(response_line) self.protocols, self.extensions = self.process_handshake_header( headers) except HandshakeError: self.close_connection() raise self.handshake_ok() if body: self.process(body)
def connect(self): """ Connects this websocket and starts the upgrade handshake with the remote endpoint. """ if self.unix_socket_path: sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0) sock.connect(self.bind_addr) self._is_secure = hasattr(sock, '_ssl') or hasattr(sock, '_sslobj') if self.scheme == "wss": # default port is now 443; upgrade self.sender to send ssl sock = ssl.wrap_socket(sock, **self.ssl_options) self._is_secure = True else: # Let's handle IPv4 and IPv6 addresses # Simplified from CherryPy's code try: addrinfo = socket.getaddrinfo( self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE ) except socket.gaierror: raise OSError('Hostname unknown. Cannot connect to {}'.format(self.bind_addr[0])) for family, socktype, proto, canonname, sa in addrinfo: sock = socket.socket(family, socktype, proto) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if ( hasattr(socket, 'AF_INET6') and family == socket.AF_INET6 and self.host.startswith('::') ): try: sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) except (AttributeError, socket.error): pass self._is_secure = hasattr(sock, '_ssl') or hasattr(sock, '_sslobj') if self.scheme == "wss": # default port is now 443; upgrade self.sender to send ssl sock = ssl.wrap_socket(sock, **self.ssl_options) self._is_secure = True try: sock.connect(sa) except socket.error: sock.close() continue self.sa = sa break else: raise OSError('Cannot connect to {}'.format(self.bind_addr[0])) self.sock = sock self._write(self.handshake_request) response = b'' doubleCLRF = b'\r\n\r\n' while True: bytes = self.sock.recv(128) if not bytes: break response += bytes if doubleCLRF in response: break if not response: self.close_connection() raise HandshakeError("Invalid response") headers, _, body = response.partition(doubleCLRF) response_line, _, headers = headers.partition(b'\r\n') try: if self.check_response_line(response_line) == 'redirect': self.handle_redirect(headers) return self.protocols, self.extensions = self.process_handshake_header(headers) except HandshakeError: self.close_connection() raise self.handshake_ok() if body: self.process(body)
def upgrade(self, protocols=None, extensions=None, version=8, handler_cls=WebSocketHandler): """ Performs the upgrade of the connection to the WebSocket protocol. The provided protocols may be a list of WebSocket protocols supported by the instance of the tool. When no list is provided and no protocol is either during the upgrade, then the protocol parameter is not taken into account. On the other hand, if the protocol from the handshake isn't part of the provided list, the upgrade fails immediatly. """ request = cherrypy.serving.request request.process_request_body = False ws_protocols = None ws_location = None ws_version = version ws_key = None ws_extensions = [] if request.method != 'GET': raise HandshakeError('Method is not GET') for key, expected_value in [('Upgrade', 'websocket'), ('Connection', 'Upgrade')]: actual_value = request.headers.get(key) if not actual_value: raise HandshakeError('Header %s is not defined' % key) if expected_value and expected_value not in actual_value: raise HandshakeError('Illegal value for header %s: %s' % (key, actual_value)) key = request.headers.get('Sec-WebSocket-Key') if key: ws_key = base64.b64decode(key) if len(ws_key) != 16: raise HandshakeError("WebSocket key's length is invalid") version = request.headers.get('Sec-WebSocket-Version') if version: if version != str(ws_version): raise HandshakeError('Unsupported WebSocket version') else: raise HandshakeError('WebSocket version required') protocols = protocols or [] subprotocols = request.headers.get('Sec-WebSocket-Protocol') if subprotocols: ws_protocols = [] for s in subprotocols.split(','): s = s.strip() if s in protocols: ws_protocols.append(s) exts = extensions or [] extensions = request.headers.get('Sec-WebSocket-Extensions') if extensions: for ext in extensions.split(','): ext = ext.strip() if ext in exts: ws_extensions.append(ext) location = [] include_port = False if request.scheme == "https": location.append("wss://") include_port = request.local.port != 443 else: location.append("ws://") include_port = request.local.port != 80 location.append('localhost') if include_port: location.append(":%d" % request.local.port) location.append(request.path_info) if request.query_string != "": location.append("?%s" % request.query_string) ws_location = ''.join(location) response = cherrypy.serving.response response.stream = True response.status = '101 Switching Protocols' response.headers['Content-Type'] = 'text/plain' response.headers['Upgrade'] = 'websocket' response.headers['Connection'] = 'Upgrade' response.headers['Sec-WebSocket-Version'] = str(ws_version) response.headers['Sec-WebSocket-Accept'] = base64.b64encode( sha1(key + WS_KEY).digest()) if ws_protocols: response.headers['Sec-WebSocket-Protocol'] = ', '.join( ws_protocols) if ws_extensions: response.headers['Sec-WebSocket-Extensions'] = ','.join( ws_extensions) addr = (request.remote.ip, request.remote.port) ws_conn = request.rfile.rfile._sock request.ws_handler = handler_cls(ws_conn, ws_protocols, ws_extensions) # Start tracking the handler cherrypy.engine.publish('handle-websocket', request.ws_handler, addr)
def __call__(self, environ, start_response): # Initial handshake validation try: if 'websocket' not in environ.get('upgrade.protocol', environ.get('HTTP_UPGRADE', '')).lower(): raise HandshakeError("Upgrade protocol is not websocket") if environ.get('REQUEST_METHOD') != 'GET': raise HandshakeError('Method is not GET') key = environ.get('HTTP_SEC_WEBSOCKET_KEY') if key: ws_key = base64.b64decode(key) if len(ws_key) != 16: raise HandshakeError("WebSocket key's length is invalid") else: raise HandshakeError("Not a valid HyBi WebSocket request") version = environ.get('HTTP_SEC_WEBSOCKET_VERSION') version_is_valid = False if version: try: version = int(version) except: pass else: version_is_valid = version in self.versions if not version_is_valid: raise HandshakeError('Unsupported WebSocket version: %s' % version) environ['websocket.version'] = str(version) except HandshakeError as e: if self.fallback_app: return self.fallback_app(environ, start_response) else: start_response( "400 Bad Handshake", [('Sec-WebSocket-Version', self.supported_versions)]) return [str(e)] # Collect supported subprotocols protocols = self.protocols or [] subprotocols = environ.get('HTTP_SEC_WEBSOCKET_PROTOCOL') ws_protocols = [] if subprotocols: for s in subprotocols.split(','): s = s.strip() if s in protocols: ws_protocols.append(s) # Collect supported extensions exts = self.extensions or [] ws_extensions = [] extensions = environ.get('HTTP_SEC_WEBSOCKET_EXTENSIONS') if extensions: for ext in extensions.split(','): ext = ext.strip() if ext in exts: ws_extensions.append(ext) # Build and start the HTTP response headers = [ ('Upgrade', 'websocket'), ('Connection', 'Upgrade'), ('Sec-WebSocket-Version', environ['websocket.version']), ('Sec-WebSocket-Accept', base64.b64encode(sha1(key + WS_KEY).digest())), ] if ws_protocols: headers.append(('Sec-WebSocket-Protocol', ', '.join(ws_protocols))) if ws_extensions: headers.append( ('Sec-WebSocket-Extensions', ','.join(ws_extensions))) start_response("101 Web Socket Hybi Handshake", headers) if 'upgrade.socket' in environ: upgrade_socket = environ['upgrade.socket'] else: upgrade_socket = environ['wsgi.input']._sock return self.app( self.websocket_class(upgrade_socket, ws_protocols, ws_extensions, environ.copy()))
def upgrade(self, protocols=None, extensions=None, version=WS_VERSION, handler_cls=WebSocket, heartbeat_freq=None): """ Performs the upgrade of the connection to the WebSocket protocol. The provided protocols may be a list of WebSocket protocols supported by the instance of the tool. When no list is provided and no protocol is either during the upgrade, then the protocol parameter is not taken into account. On the other hand, if the protocol from the handshake isn't part of the provided list, the upgrade fails immediatly. """ request = cherrypy.serving.request request.process_request_body = False ws_protocols = None ws_location = None ws_version = version ws_key = None ws_extensions = [] if request.method != 'GET': raise HandshakeError('HTTP method must be a GET') for key, expected_value in [('Upgrade', 'websocket'), ('Connection', 'upgrade')]: actual_value = request.headers.get(key, '').lower() if not actual_value: raise HandshakeError('Header %s is not defined' % key) if expected_value not in actual_value: raise HandshakeError('Illegal value for header %s: %s' % (key, actual_value)) version = request.headers.get('Sec-WebSocket-Version') supported_versions = ', '.join([str(v) for v in ws_version]) version_is_valid = False if version: try: version = int(version) except: pass else: version_is_valid = version in ws_version if not version_is_valid: cherrypy.response.headers[ 'Sec-WebSocket-Version'] = supported_versions raise HandshakeError('Unhandled or missing WebSocket version') key = request.headers.get('Sec-WebSocket-Key') if key: ws_key = base64.b64decode(key.encode('utf-8')) if len(ws_key) != 16: raise HandshakeError("WebSocket key's length is invalid") protocols = protocols or [] subprotocols = request.headers.get('Sec-WebSocket-Protocol') if subprotocols: ws_protocols = [] for s in subprotocols.split(','): s = s.strip() if s in protocols: ws_protocols.append(s) exts = extensions or [] extensions = request.headers.get('Sec-WebSocket-Extensions') if extensions: for ext in extensions.split(','): ext = ext.strip() if ext in exts: ws_extensions.append(ext) location = [] include_port = False if request.scheme == "https": location.append("wss://") include_port = request.local.port != 443 else: location.append("ws://") include_port = request.local.port != 80 location.append('localhost') if include_port: location.append(":%d" % request.local.port) location.append(request.path_info) if request.query_string != "": location.append("?%s" % request.query_string) ws_location = ''.join(location) response = cherrypy.serving.response response.stream = True response.status = '101 Switching Protocols' response.headers['Content-Type'] = 'text/plain' response.headers['Upgrade'] = 'websocket' response.headers['Connection'] = 'Upgrade' response.headers['Sec-WebSocket-Version'] = str(version) response.headers['Sec-WebSocket-Accept'] = base64.b64encode( sha1(key.encode('utf-8') + WS_KEY).digest()) if ws_protocols: response.headers['Sec-WebSocket-Protocol'] = ', '.join( ws_protocols) if ws_extensions: response.headers['Sec-WebSocket-Extensions'] = ','.join( ws_extensions) addr = (request.remote.ip, request.remote.port) ws_conn = get_connection(request.rfile.rfile) request.ws_handler = handler_cls(ws_conn, ws_protocols, ws_extensions, request.wsgi_environ.copy(), heartbeat_freq=heartbeat_freq)
def __call__(self, environ, start_response): if environ.get('REQUEST_METHOD') != 'GET': raise HandshakeError('HTTP method must be a GET') for key, expected_value in [('HTTP_UPGRADE', 'websocket'), ('HTTP_CONNECTION', 'upgrade')]: actual_value = environ.get(key, '').lower() if not actual_value: raise HandshakeError('Header %s is not defined' % key) if expected_value not in actual_value: raise HandshakeError('Illegal value for header %s: %s' % (key, actual_value)) key = environ.get('HTTP_SEC_WEBSOCKET_KEY') if key: ws_key = base64.b64decode(enc(key)) if len(ws_key) != 16: raise HandshakeError("WebSocket key's length is invalid") version = environ.get('HTTP_SEC_WEBSOCKET_VERSION') supported_versions = ', '.join([str(v) for v in WS_VERSION]) version_is_valid = False if version: try: version = int(version) except: pass else: version_is_valid = version in WS_VERSION if not version_is_valid: environ['websocket.version'] = str(version) raise HandshakeError('Unhandled or missing WebSocket version') ws_protocols = [] protocols = self.protocols or [] subprotocols = environ.get('HTTP_SEC_WEBSOCKET_PROTOCOL') if subprotocols: for s in subprotocols.split(','): s = s.strip() if s in protocols: ws_protocols.append(s) ws_extensions = [] exts = self.extensions or [] extensions = environ.get('HTTP_SEC_WEBSOCKET_EXTENSIONS') if extensions: for ext in extensions.split(','): ext = ext.strip() if ext in exts: ws_extensions.append(ext) upgrade_headers = [ ('Upgrade', 'websocket'), ('Connection', 'Upgrade'), ('Sec-WebSocket-Version', str(version)), ('Sec-WebSocket-Accept', base64.b64encode(sha1(key + WS_KEY).digest())), ] if ws_protocols: upgrade_headers.append(('Sec-WebSocket-Protocol', ', '.join(ws_protocols))) if ws_extensions: upgrade_headers.append(('Sec-WebSocket-Extensions', ','.join(ws_extensions))) start_response("101 Switching Protocols", upgrade_headers) self.make_websocket(environ['ws4py.socket'], ws_protocols, ws_extensions, environ) return []
def process_response_line(self, response_line): protocol, code, status = response_line.split(' ', 2) if code != '101': raise HandshakeError("Invalid response status: %s %s" % (code, status))
def handle_initial_handshake(self): """ Performs the HTTP handshake described in :rfc:`6455`. Note that this implementation is really basic and it is strongly advised against using it in production. It would probably break for most clients. If you want a better support for HTTP, please use a more reliable HTTP server implemented using asyncio. """ request_line = yield from self.next_line() method, uri, req_protocol = request_line.strip().split(SPACE, 2) # GET required if method.upper() != b'GET': raise HandshakeError('HTTP method must be a GET') headers = yield from self.read_headers() if req_protocol == b'HTTP/1.1' and 'Host' not in headers: raise ValueError("Missing host header") for key, expected_value in [('Upgrade', 'websocket'), ('Connection', 'upgrade')]: actual_value = headers.get(key, '').lower() if not actual_value: raise HandshakeError('Header %s is not defined' % str(key)) if expected_value not in actual_value: raise HandshakeError('Illegal value for header %s: %s' % (key, actual_value)) response_headers = {} ws_version = WS_VERSION version = headers.get('Sec-WebSocket-Version') supported_versions = ', '.join([str(v) for v in ws_version]) version_is_valid = False if version: try: version = int(version) except: pass else: version_is_valid = version in ws_version if not version_is_valid: response_headers['Sec-WebSocket-Version'] = supported_versions raise HandshakeError('Unhandled or missing WebSocket version') key = headers.get('Sec-WebSocket-Key') if key: ws_key = base64.b64decode(key.encode('utf-8')) if len(ws_key) != 16: raise HandshakeError("WebSocket key's length is invalid") protocols = [] ws_protocols = [] subprotocols = headers.get('Sec-WebSocket-Protocol') if subprotocols: for s in subprotocols.split(','): s = s.strip() if s in protocols: ws_protocols.append(s) exts = [] ws_extensions = [] extensions = headers.get('Sec-WebSocket-Extensions') if extensions: for ext in extensions.split(','): ext = ext.strip() if ext in exts: ws_extensions.append(ext) self.ws.protocols = ws_protocols self.ws.extensions = ws_extensions self.ws.headers = headers response = [req_protocol + b' 101 Switching Protocols'] response.append(b'Upgrade: websocket') response.append(b'Content-Type: text/plain') response.append(b'Content-Length: 0') response.append(b'Connection: Upgrade') response.append(b'Sec-WebSocket-Version:' + bytes(str(version), 'utf-8')) response.append( b'Sec-WebSocket-Accept:' + base64.b64encode(sha1(key.encode('utf-8') + WS_KEY).digest())) if ws_protocols: response.append(b'Sec-WebSocket-Protocol:' + b', '.join(ws_protocols)) if ws_extensions: response.append(b'Sec-WebSocket-Extensions:' + b','.join(ws_extensions)) response.append(b'') response.append(b'') self.writer.write(CRLF.join(response)) yield from self.handle_websocket()