def _skip_spaces(self): # 4.1 35. read a byte from the server while True: ch = _receive_bytes(self._socket, 1) if ch == ' ': # 0x20 continue return ch
def _read_fields(self): # 4.1 32. let /fields/ be a list of name-value pairs, initially empty. fields = {} while True: # "Field" # 4.1 33. let /name/ and /value/ be empty byte arrays name = '' value = '' # 4.1 34. read /name/ name = self._read_name() if name is None: break # 4.1 35. read spaces # TODO(tyoshino): Skip only one space as described in the spec. ch = self._skip_spaces() # 4.1 36. read /value/ value = self._read_value(ch) # 4.1 37. read a byte from the server ch = _receive_bytes(self._socket, 1) if ch != '\n': # 0x0A raise ClientHandshakeError( 'Expected LF but found %r while reading value %r for ' 'header %r' % (ch, value, name)) self._logger.debug('Received %r header', name) # 4.1 38. append an entry to the /fields/ list that has the name # given by the string obtained by interpreting the /name/ byte # array as a UTF-8 stream and the value given by the string # obtained by interpreting the /value/ byte array as a UTF-8 byte # stream. fields.setdefault(name, []).append(value) # 4.1 39. return to the "Field" step above return fields
def _read_value(self, ch): # 4.1 33. let /value/ be empty byte arrays value = '' # 4.1 36. read a byte from server. while True: if ch == '\r': # 0x0D return value elif ch == '\n': # 0x0A raise ClientHandshakeError( 'Unexpected LF when reading header value %r' % value) else: value += ch ch = _receive_bytes(self._socket, 1)
def _read_name(self): # 4.1 33. let /name/ be empty byte arrays name = '' while True: # 4.1 34. read a byte from the server ch = _receive_bytes(self._socket, 1) if ch == '\r': # 0x0D return None elif ch == '\n': # 0x0A raise ClientHandshakeError( 'Unexpected LF when reading header name %r' % name) elif ch == ':': # 0x3A return name elif ch >= 'A' and ch <= 'Z': # Range 0x31 to 0x5A ch = chr(ord(ch) + 0x20) name += ch else: name += ch
def handshake(self): """Performs opening handshake on the specified socket. Raises: ClientHandshakeError: handshake failed. """ request_line = _build_method_line(self._options.get('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.get('server_host'), int(self._options.get('server_port')), self._options.get('use_tls')=='True')) fields.append(_UPGRADE_HEADER) fields.append(_CONNECTION_HEADER) if self._options.get('origin') is not None: if self._options.get('protocol_version') == _PROTOCOL_VERSION_HYBI08: fields.append(_origin_header( common.SEC_WEBSOCKET_ORIGIN_HEADER, self._options.get('origin'))) else: fields.append(_origin_header(common.ORIGIN_HEADER, self._options.get('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 int(self._options.get('version_header')) > 0: fields.append('%s: %d\r\n' % (common.SEC_WEBSOCKET_VERSION_HEADER, int(self._options.get('version_header')))) else: fields.append('%s: %d\r\n' % (common.SEC_WEBSOCKET_VERSION_HEADER, common.VERSION_HYBI_LATEST)) extensions_to_request = [] if self._options.get('deflate_stream') == 'True': extensions_to_request.append( common.ExtensionParameter( common.DEFLATE_STREAM_EXTENSION)) if self._options.get('deflate_frame') == 'True': 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, "test", "test")) 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))