Пример #1
0
    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))
Пример #5
0
    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)]
Пример #6
0
    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)
Пример #7
0
    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)]
Пример #8
0
    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)
Пример #10
0
    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()
Пример #11
0
    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)
Пример #12
0
    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)
Пример #13
0
    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)
Пример #14
0
    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)
Пример #15
0
    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)
Пример #16
0
    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)
Пример #17
0
    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()))
Пример #18
0
    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)
Пример #19
0
    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 []
Пример #20
0
 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))
Пример #21
0
    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()