def _create_handshake_response(self, accept): response = [] response.append('HTTP/1.1 101 Switching Protocols\r\n') # WebSocket headers response.append(format_header( common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE)) response.append(format_header( common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE)) response.append(format_header( common.SEC_WEBSOCKET_ACCEPT_HEADER, accept)) if self._request.ws_protocol is not None: response.append(format_header( common.SEC_WEBSOCKET_PROTOCOL_HEADER, self._request.ws_protocol)) if (self._request.ws_extensions is not None and len(self._request.ws_extensions) != 0): response.append(format_header( common.SEC_WEBSOCKET_EXTENSIONS_HEADER, common.format_extensions(self._request.ws_extensions))) # Headers not specific for WebSocket for name, value in self._request.extra_headers: response.append(format_header(name, value)) response.append('\r\n') return ''.join(response)
def _send_handshake(self, accept): response = [] response.append('HTTP/1.1 101 Switching Protocols\r\n') response.append(format_header( common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE)) response.append(format_header( common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE)) response.append(format_header( common.SEC_WEBSOCKET_ACCEPT_HEADER, accept)) if self._request.ws_protocol is not None: response.append(format_header( common.SEC_WEBSOCKET_PROTOCOL_HEADER, self._request.ws_protocol)) if (self._request.ws_extensions is not None and len(self._request.ws_extensions) != 0): response.append(format_header( common.SEC_WEBSOCKET_EXTENSIONS_HEADER, common.format_extensions(self._request.ws_extensions))) response.append('\r\n') raw_response = ''.join(response) self._request.connection.write(raw_response) self._logger.debug('Sent server\'s opening handshake: %r', raw_response)
def _create_handshake_response(self, accept): response = [] response.append(u'HTTP/1.1 101 Switching Protocols\r\n') # WebSocket headers response.append( format_header(common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE)) response.append( format_header(common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE)) response.append( format_header(common.SEC_WEBSOCKET_ACCEPT_HEADER, accept.decode('UTF-8'))) if self._request.ws_protocol is not None: response.append( format_header(common.SEC_WEBSOCKET_PROTOCOL_HEADER, self._request.ws_protocol)) if (self._request.ws_extensions is not None and len(self._request.ws_extensions) != 0): response.append( format_header( common.SEC_WEBSOCKET_EXTENSIONS_HEADER, common.format_extensions(self._request.ws_extensions))) # Headers not specific for WebSocket for name, value in self._request.extra_headers: response.append(format_header(name, value)) response.append(u'\r\n') return u''.join(response)
def _send_handshake(self, accept): response = [] response.append('HTTP/1.1 101 Switching Protocols\r\n') response.append( format_header(common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE)) response.append( format_header(common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE)) response.append( format_header(common.SEC_WEBSOCKET_ACCEPT_HEADER, accept)) if self._request.ws_protocol is not None: response.append( format_header(common.SEC_WEBSOCKET_PROTOCOL_HEADER, self._request.ws_protocol)) if (self._request.ws_extensions is not None and len(self._request.ws_extensions) != 0): response.append( format_header( common.SEC_WEBSOCKET_EXTENSIONS_HEADER, common.format_extensions(self._request.ws_extensions))) response.append('\r\n') raw_response = ''.join(response) self._request.connection.write(raw_response) self._logger.debug('Sent server\'s opening handshake: %r', raw_response)
def test_format_extensions(self): for formatted_string, definitions in _TEST_TOKEN_EXTENSION_DATA: extensions = [] for definition in definitions: (name, parameters) = definition extension = ExtensionParameter(name) extension._parameters = parameters extensions.append(extension) self.assertEqual(formatted_string, format_extensions(extensions))
def test_format_extensions(self): for formatted_string, definitions in _TEST_TOKEN_EXTENSION_DATA: extensions = [] for definition in definitions: (name, parameters) = definition extension = ExtensionParameter(name) extension._parameters = parameters extensions.append(extension) self.assertEqual( formatted_string, format_extensions(extensions))
def _prepare_handshake_response(self): self._request.status = 200 self._request.headers_out['upgrade'] = common.WEBSOCKET_UPGRADE_TYPE self._request.headers_out['connection'] = common.UPGRADE_CONNECTION_TYPE if self._request.ws_protocol is not None: self._request.headers_out['sec-websocket-protocol'] = self._request.ws_protocol if (self._request.ws_extensions is not None and len(self._request.ws_extensions) != 0): self._request.headers_out['sec-websocket-extensions'] = common.format_extensions(self._request.ws_extensions) # Headers not specific for WebSocket for name, value in self._request.extra_headers: self._request.headers_out[name] = value
def _send_handshake(self): # We are not actually sending the handshake, but just preparing it. It # will be flushed by the caller. self._request.status = 200 self._request.headers_out['upgrade'] = common.WEBSOCKET_UPGRADE_TYPE self._request.headers_out[ 'connection'] = common.UPGRADE_CONNECTION_TYPE if self._request.ws_protocol is not None: self._request.headers_out[ 'sec-websocket-protocol'] = self._request.ws_protocol if (self._request.ws_extensions is not None and len(self._request.ws_extensions) != 0): self._request.headers_out[ 'sec-websocket-extensions'] = common.format_extensions( self._request.ws_extensions) # Headers not specific for WebSocket for name, value in self._request.extra_headers: self._request.headers_out[name] = value
def _create_handshake_response(self, accept): response = [] response.append('HTTP/1.1 101 Switching Protocols\r\n') response.append(format_header( common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE)) response.append(format_header( common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE)) response.append(format_header( common.SEC_WEBSOCKET_ACCEPT_HEADER, accept)) if self._request.ws_protocol is not None: response.append(format_header( common.SEC_WEBSOCKET_PROTOCOL_HEADER, self._request.ws_protocol)) if (self._request.ws_extensions is not None and len(self._request.ws_extensions) != 0): response.append(format_header( common.SEC_WEBSOCKET_EXTENSIONS_HEADER, common.format_extensions(self._request.ws_extensions))) response.append('\r\n') return ''.join(response)
def _create_handshake_response(self, accept): response = [] response.append('HTTP/1.1 101 Switching Protocols\r\n') # WebSocket headers response.append( format_header(common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE)) response.append( format_header(common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE)) response.append( format_header(common.SEC_WEBSOCKET_ACCEPT_HEADER, accept)) if self._request.ws_protocol is not None: response.append( format_header(common.SEC_WEBSOCKET_PROTOCOL_HEADER, self._request.ws_protocol)) if (self._request.ws_extensions is not None and len(self._request.ws_extensions) != 0): response.append( format_header( common.SEC_WEBSOCKET_EXTENSIONS_HEADER, common.format_extensions(self._request.ws_extensions))) # MOZILLA: Add HSTS header if requested to if self._request.sts is not None: response.append( format_header("Strict-Transport-Security", self._request.sts)) # /MOZILLA # Headers not specific for WebSocket for name, value in self._request.extra_headers: response.append(format_header(name, value)) response.append('\r\n') return ''.join(response)
def handshake(self): """Performs opening handshake on the specified socket. Raises: ClientHandshakeError: handshake failed. """ request_line = _build_method_line(self._options.resource) self._logger.debug('Client\'s opening handshake Request-Line: %r', request_line) self._socket.sendall(request_line) fields = [] fields.append(_format_host_header( self._options.server_host, self._options.server_port, self._options.use_tls)) fields.append(_UPGRADE_HEADER) fields.append(_CONNECTION_HEADER) if self._options.origin is not None: if self._options.protocol_version == _PROTOCOL_VERSION_HYBI08: fields.append(_origin_header( common.SEC_WEBSOCKET_ORIGIN_HEADER, self._options.origin)) else: fields.append(_origin_header(common.ORIGIN_HEADER, self._options.origin)) original_key = os.urandom(16) self._key = base64.b64encode(original_key) self._logger.debug( '%s: %r (%s)', common.SEC_WEBSOCKET_KEY_HEADER, self._key, util.hexify(original_key)) fields.append( '%s: %s\r\n' % (common.SEC_WEBSOCKET_KEY_HEADER, self._key)) if self._options.version_header > 0: fields.append('%s: %d\r\n' % (common.SEC_WEBSOCKET_VERSION_HEADER, self._options.version_header)) elif self._options.protocol_version == _PROTOCOL_VERSION_HYBI08: fields.append('%s: %d\r\n' % (common.SEC_WEBSOCKET_VERSION_HEADER, common.VERSION_HYBI08)) else: fields.append('%s: %d\r\n' % (common.SEC_WEBSOCKET_VERSION_HEADER, common.VERSION_HYBI_LATEST)) extensions_to_request = [] if self._options.deflate_frame: extensions_to_request.append( common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)) if self._options.use_permessage_deflate: extension = common.ExtensionParameter( common.PERMESSAGE_DEFLATE_EXTENSION) # Accept the client_max_window_bits extension parameter by default. extension.add_parameter( PerMessageDeflateExtensionProcessor. _CLIENT_MAX_WINDOW_BITS_PARAM, None) extensions_to_request.append(extension) if len(extensions_to_request) != 0: fields.append( '%s: %s\r\n' % (common.SEC_WEBSOCKET_EXTENSIONS_HEADER, common.format_extensions(extensions_to_request))) for field in fields: self._socket.sendall(field) self._socket.sendall('\r\n') self._logger.debug('Sent client\'s opening handshake headers: %r', fields) self._logger.debug('Start reading Status-Line') status_line = '' while True: ch = _receive_bytes(self._socket, 1) status_line += ch if ch == '\n': break m = re.match('HTTP/\\d+\.\\d+ (\\d\\d\\d) .*\r\n', status_line) if m is None: raise ClientHandshakeError( 'Wrong status line format: %r' % status_line) status_code = m.group(1) if status_code != '101': self._logger.debug('Unexpected status code %s with following ' 'headers: %r', status_code, self._read_fields()) raise ClientHandshakeError( 'Expected HTTP status code 101 but found %r' % status_code) self._logger.debug('Received valid Status-Line') self._logger.debug('Start reading headers until we see an empty line') fields = self._read_fields() ch = _receive_bytes(self._socket, 1) if ch != '\n': # 0x0A raise ClientHandshakeError( 'Expected LF but found %r while reading value %r for header ' 'name %r' % (ch, value, name)) self._logger.debug('Received an empty line') self._logger.debug('Server\'s opening handshake headers: %r', fields) _validate_mandatory_header( fields, common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE, False) _validate_mandatory_header( fields, common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE, False) accept = _get_mandatory_header( fields, common.SEC_WEBSOCKET_ACCEPT_HEADER) # Validate try: binary_accept = base64.b64decode(accept) except TypeError, e: raise HandshakeError( 'Illegal value for header %s: %r' % (common.SEC_WEBSOCKET_ACCEPT_HEADER, accept))
def handshake(self): """Performs opening handshake on the specified socket. Raises: ClientHandshakeError: handshake failed. """ request_line = _build_method_line(self._options.resource) self._logger.debug('Client\'s opening handshake Request-Line: %r', request_line) self._socket.sendall(request_line) fields = [] fields.append( _format_host_header(self._options.server_host, self._options.server_port, self._options.use_tls)) fields.append(_UPGRADE_HEADER) fields.append(_CONNECTION_HEADER) if self._options.origin is not None: if self._options.protocol_version == _PROTOCOL_VERSION_HYBI08: fields.append( _origin_header(common.SEC_WEBSOCKET_ORIGIN_HEADER, self._options.origin)) else: fields.append( _origin_header(common.ORIGIN_HEADER, self._options.origin)) # TODO: adapt original library code # original code uses os.urandom which is not available on digi gateways # get 16 bytes out of the int range 33..126 (readable ascii codes) random_int_list = random.sample(range(33, 127), 16) original_key = ''.join(chr(i) for i in random_int_list) self._key = base64.b64encode(original_key) self._logger.debug('%s: %r (%s)', common.SEC_WEBSOCKET_KEY_HEADER, self._key, util.hexify(original_key)) fields.append('%s: %s\r\n' % (common.SEC_WEBSOCKET_KEY_HEADER, self._key)) if self._options.version_header > 0: fields.append('%s: %d\r\n' % (common.SEC_WEBSOCKET_VERSION_HEADER, self._options.version_header)) elif self._options.protocol_version == _PROTOCOL_VERSION_HYBI08: fields.append( '%s: %d\r\n' % (common.SEC_WEBSOCKET_VERSION_HEADER, common.VERSION_HYBI08)) else: fields.append('%s: %d\r\n' % (common.SEC_WEBSOCKET_VERSION_HEADER, common.VERSION_HYBI_LATEST)) extensions_to_request = [] if self._options.deflate_stream: extensions_to_request.append( common.ExtensionParameter(common.DEFLATE_STREAM_EXTENSION)) if self._options.deflate_frame: extensions_to_request.append( common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)) if len(extensions_to_request) != 0: fields.append('%s: %s\r\n' % (common.SEC_WEBSOCKET_EXTENSIONS_HEADER, common.format_extensions(extensions_to_request))) for field in fields: self._socket.sendall(field) self._socket.sendall('\r\n') self._logger.debug('Sent client\'s opening handshake headers: %r', fields) self._logger.debug('Start reading Status-Line') status_line = '' while True: ch = _receive_bytes(self._socket, 1) status_line += ch if ch == '\n': break m = re.match('HTTP/\\d+\.\\d+ (\\d\\d\\d) .*\r\n', status_line) if m is None: raise ClientHandshakeError('Wrong status line format: %r' % status_line) status_code = m.group(1) if status_code != '101': self._logger.debug( 'Unexpected status code %s with following ' 'headers: %r', status_code, self._read_fields()) raise ClientHandshakeError( 'Expected HTTP status code 101 but found %r' % status_code) self._logger.debug('Received valid Status-Line') self._logger.debug('Start reading headers until we see an empty line') fields = self._read_fields() ch = _receive_bytes(self._socket, 1) if ch != '\n': # 0x0A raise ClientHandshakeError( 'Expected LF but found %r while reading value %r for header ' 'name %r' % (ch, value, name)) self._logger.debug('Received an empty line') self._logger.debug('Server\'s opening handshake headers: %r', fields) _validate_mandatory_header(fields, common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE, False) _validate_mandatory_header(fields, common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE, False) accept = _get_mandatory_header(fields, common.SEC_WEBSOCKET_ACCEPT_HEADER) # Validate try: binary_accept = base64.b64decode(accept) except TypeError, e: raise HandshakeError('Illegal value for header %s: %r' % (common.SEC_WEBSOCKET_ACCEPT_HEADER, accept))
def handshake(self): """Performs opening handshake on the specified socket. Raises: ClientHandshakeError: handshake failed. """ request_line = _build_method_line(self._options.resource) self._logger.debug('Client\'s opening handshake Request-Line: %r', request_line) self._socket.sendall(request_line) fields = [] fields.append( _format_host_header(self._options.server_host, self._options.server_port, self._options.use_tls)) fields.append(_UPGRADE_HEADER) fields.append(_CONNECTION_HEADER) if self._options.origin is not None: if self._options.protocol_version == _PROTOCOL_VERSION_HYBI08: fields.append( _origin_header(common.SEC_WEBSOCKET_ORIGIN_HEADER, self._options.origin)) else: fields.append( _origin_header(common.ORIGIN_HEADER, self._options.origin)) original_key = os.urandom(16) self._key = base64.b64encode(original_key) self._logger.debug('%s: %r (%s)', common.SEC_WEBSOCKET_KEY_HEADER, self._key, util.hexify(original_key)) fields.append('%s: %s\r\n' % (common.SEC_WEBSOCKET_KEY_HEADER, self._key)) if self._options.version_header > 0: fields.append('%s: %d\r\n' % (common.SEC_WEBSOCKET_VERSION_HEADER, self._options.version_header)) elif self._options.protocol_version == _PROTOCOL_VERSION_HYBI08: fields.append( '%s: %d\r\n' % (common.SEC_WEBSOCKET_VERSION_HEADER, common.VERSION_HYBI08)) else: fields.append('%s: %d\r\n' % (common.SEC_WEBSOCKET_VERSION_HEADER, common.VERSION_HYBI_LATEST)) extensions_to_request = [] if self._options.deflate_frame: extensions_to_request.append( common.ExtensionParameter(common.DEFLATE_FRAME_EXTENSION)) if self._options.use_permessage_deflate: extension = common.ExtensionParameter( common.PERMESSAGE_DEFLATE_EXTENSION) # Accept the client_max_window_bits extension parameter by default. extension.add_parameter( PerMessageDeflateExtensionProcessor. _CLIENT_MAX_WINDOW_BITS_PARAM, None) extensions_to_request.append(extension) if len(extensions_to_request) != 0: fields.append('%s: %s\r\n' % (common.SEC_WEBSOCKET_EXTENSIONS_HEADER, common.format_extensions(extensions_to_request))) for field in fields: self._socket.sendall(field) self._socket.sendall('\r\n') self._logger.debug('Sent client\'s opening handshake headers: %r', fields) self._logger.debug('Start reading Status-Line') status_line = '' while True: ch = _receive_bytes(self._socket, 1) status_line += ch if ch == '\n': break m = re.match('HTTP/\\d+\\.\\d+ (\\d\\d\\d) .*\r\n', status_line) if m is None: raise ClientHandshakeError('Wrong status line format: %r' % status_line) status_code = m.group(1) if status_code != '101': self._logger.debug( 'Unexpected status code %s with following ' 'headers: %r', status_code, self._read_fields()) raise ClientHandshakeError( 'Expected HTTP status code 101 but found %r' % status_code) self._logger.debug('Received valid Status-Line') self._logger.debug('Start reading headers until we see an empty line') fields = self._read_fields() ch = _receive_bytes(self._socket, 1) if ch != '\n': # 0x0A raise ClientHandshakeError( 'Expected LF but found %r while reading value %r for header ' 'name %r' % (ch, value, name)) self._logger.debug('Received an empty line') self._logger.debug('Server\'s opening handshake headers: %r', fields) _validate_mandatory_header(fields, common.UPGRADE_HEADER, common.WEBSOCKET_UPGRADE_TYPE, False) _validate_mandatory_header(fields, common.CONNECTION_HEADER, common.UPGRADE_CONNECTION_TYPE, False) accept = _get_mandatory_header(fields, common.SEC_WEBSOCKET_ACCEPT_HEADER) # Validate try: binary_accept = base64.b64decode(accept) except TypeError: raise HandshakeError('Illegal value for header %s: %r' % (common.SEC_WEBSOCKET_ACCEPT_HEADER, accept)) if len(binary_accept) != 20: raise ClientHandshakeError( 'Decoded value of %s is not 20-byte long' % common.SEC_WEBSOCKET_ACCEPT_HEADER) self._logger.debug('Response for challenge : %r (%s)', accept, util.hexify(binary_accept)) binary_expected_accept = util.sha1_hash( self._key + common.WEBSOCKET_ACCEPT_UUID).digest() expected_accept = base64.b64encode(binary_expected_accept) self._logger.debug('Expected response for challenge: %r (%s)', expected_accept, util.hexify(binary_expected_accept)) if accept != expected_accept: raise ClientHandshakeError( 'Invalid %s header: %r (expected: %s)' % (common.SEC_WEBSOCKET_ACCEPT_HEADER, accept, expected_accept)) deflate_frame_accepted = False permessage_deflate_accepted = False extensions_header = fields.get( common.SEC_WEBSOCKET_EXTENSIONS_HEADER.lower()) accepted_extensions = [] if extensions_header is not None and len(extensions_header) != 0: accepted_extensions = common.parse_extensions(extensions_header[0]) # TODO(bashi): Support the new style perframe compression extension. for extension in accepted_extensions: extension_name = extension.name() if (extension_name == common.DEFLATE_FRAME_EXTENSION and self._options.deflate_frame): deflate_frame_accepted = True processor = DeflateFrameExtensionProcessor(extension) unused_extension_response = processor.get_extension_response() self._options.deflate_frame = processor continue elif (extension_name == common.PERMESSAGE_DEFLATE_EXTENSION and self._options.use_permessage_deflate): permessage_deflate_accepted = True framer = _get_permessage_deflate_framer(extension) framer.set_compress_outgoing_enabled(True) self._options.use_permessage_deflate = framer continue raise ClientHandshakeError('Unexpected extension %r' % extension_name) if (self._options.deflate_frame and not deflate_frame_accepted): raise ClientHandshakeError( 'Requested %s, but the server rejected it' % common.DEFLATE_FRAME_EXTENSION) if (self._options.use_permessage_deflate and not permessage_deflate_accepted): raise ClientHandshakeError( 'Requested %s, but the server rejected it' % common.PERMESSAGE_DEFLATE_EXTENSION)