Spaces Data

Minimal test - lines (330, 489)

path: .spaces[10].spaces[1].metrics.loc.sloc
old: 157.0
new: 160.0

path: .spaces[10].spaces[1].metrics.loc.blank
old: 30.0
new: 31.0

path: .spaces[10].spaces[1].metrics.loc.cloc
old: 8.0
new: 10.0

path: .spaces[10].spaces[1].metrics.mi.mi_sei
old: 3.1961097727804564
new: 4.5071485357971

path: .spaces[10].spaces[1].metrics.mi.mi_visual_studio
old: 24.21133533528953
new: 24.032017346900403

path: .spaces[10].spaces[1].metrics.mi.mi_original
old: 41.401383423345095
new: 41.09474966319969

Code

    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.encode('UTF-8'))

        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:
            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.decode('UTF-8')))

        fields.append(
            '%s: %d\r\n' %
            (common.SEC_WEBSOCKET_VERSION_HEADER, common.VERSION_HYBI_LATEST))

        extensions_to_request = []

        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.encode('UTF-8'))

        self._socket.sendall(b'\r\n')

        self._logger.debug('Sent client\'s opening handshake headers: %r',
                           fields)
        self._logger.debug('Start reading Status-Line')

        status_line = b''
        while True:
            ch = _receive_bytes(self._socket, 1)
            status_line += ch
            if ch == b'\n':
                break

        m = re.match(b'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 != b'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 != b'\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 = sha1(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.decode('UTF-8'):
            raise ClientHandshakeError(
                'Invalid %s header: %r (expected: %s)' %
                (common.SEC_WEBSOCKET_ACCEPT_HEADER, accept, expected_accept))

        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])

        for extension in accepted_extensions:
            extension_name = extension.name()
            if (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.use_permessage_deflate
                and not permessage_deflate_accepted):
            raise ClientHandshakeError(
                'Requested %s, but the server rejected it' %
                common.PERMESSAGE_DEFLATE_EXTENSION)

        # TODO(tyoshino): Handle Sec-WebSocket-Protocol
        # TODO(tyoshino): Handle Cookie, etc.

Minimal test - lines (319, 489)

path: .spaces[10].metrics.mi.mi_sei
old: 1.9681006297045336
new: 3.0513733337096767

path: .spaces[10].metrics.mi.mi_original
old: 39.493756533503785
new: 39.20702338449348

path: .spaces[10].metrics.mi.mi_visual_studio
old: 23.095764054680576
new: 22.928083850580983

path: .spaces[10].metrics.loc.cloc
old: 10.0
new: 12.0

path: .spaces[10].metrics.loc.sloc
old: 168.0
new: 171.0

path: .spaces[10].metrics.loc.blank
old: 33.0
new: 34.0

Code

class ClientHandshakeProcessor(ClientHandshakeBase):
    """WebSocket opening handshake processor
    """
    def __init__(self, socket, options):
        super(ClientHandshakeProcessor, self).__init__()

        self._socket = socket
        self._options = options

        self._logger = util.get_class_logger(self)

    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.encode('UTF-8'))

        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:
            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.decode('UTF-8')))

        fields.append(
            '%s: %d\r\n' %
            (common.SEC_WEBSOCKET_VERSION_HEADER, common.VERSION_HYBI_LATEST))

        extensions_to_request = []

        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.encode('UTF-8'))

        self._socket.sendall(b'\r\n')

        self._logger.debug('Sent client\'s opening handshake headers: %r',
                           fields)
        self._logger.debug('Start reading Status-Line')

        status_line = b''
        while True:
            ch = _receive_bytes(self._socket, 1)
            status_line += ch
            if ch == b'\n':
                break

        m = re.match(b'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 != b'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 != b'\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 = sha1(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.decode('UTF-8'):
            raise ClientHandshakeError(
                'Invalid %s header: %r (expected: %s)' %
                (common.SEC_WEBSOCKET_ACCEPT_HEADER, accept, expected_accept))

        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])

        for extension in accepted_extensions:
            extension_name = extension.name()
            if (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.use_permessage_deflate
                and not permessage_deflate_accepted):
            raise ClientHandshakeError(
                'Requested %s, but the server rejected it' %
                common.PERMESSAGE_DEFLATE_EXTENSION)

        # TODO(tyoshino): Handle Sec-WebSocket-Protocol
        # TODO(tyoshino): Handle Cookie, etc.