Ejemplo n.º 1
0
    def read_response(self):
        if not self._reader:
            raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)

        # _next_response might be cached from a can_read() call
        if self._next_response is not False:
            response = self._next_response
            self._next_response = False
            return response

        response = self._reader.gets()
        socket_read_size = self.socket_read_size
        while response is False:
            try:
                if HIREDIS_USE_BYTE_BUFFER:
                    bufflen = self._sock.recv_into(self._buffer)
                    if bufflen == 0:
                        raise socket.error(SERVER_CLOSED_CONNECTION_ERROR)
                else:
                    buffer = self._sock.recv(socket_read_size)
                    # an empty string indicates the server shutdown the socket
                    if not isinstance(buffer, bytes) or len(buffer) == 0:
                        raise socket.error(SERVER_CLOSED_CONNECTION_ERROR)
            except socket.timeout:
                raise TimeoutError("Timeout reading from socket")
            except socket.error:
                e = sys.exc_info()[1]
                raise ConnectionError("Error while reading from socket: %s" %
                                      (e.args,))
            if HIREDIS_USE_BYTE_BUFFER:
                self._reader.feed(self._buffer, 0, bufflen)
            else:
                self._reader.feed(buffer)
            # proactively, but not conclusively, check if more data is in the
            # buffer. if the data received doesn't end with \r\n, there's more.
            if HIREDIS_USE_BYTE_BUFFER:
                if bufflen > 2 and self._buffer[bufflen - 2:bufflen] != SYM_CRLF:
                    continue
            else:
                if not buffer.endswith(SYM_CRLF):
                    continue
            response = self._reader.gets()
        # if an older version of hiredis is installed, we need to attempt
        # to convert ResponseErrors to their appropriate types.
        if not HIREDIS_SUPPORTS_CALLABLE_ERRORS:
            if isinstance(response, ResponseError):
                response = self.parse_error(response.args[0])
            elif isinstance(response, list) and response and \
                    isinstance(response[0], ResponseError):
                response[0] = self.parse_error(response[0].args[0])
        # if the response is a ConnectionError or the response is a list and
        # the first item is a ConnectionError, raise it as something bad
        # happened
        if isinstance(response, ConnectionError):
            raise response
        elif isinstance(response, list) and response and \
                isinstance(response[0], ConnectionError):
            raise response[0]
        return response
Ejemplo n.º 2
0
 def connect_to(self, address):
     self.host, self.port = address
     super(SentinelManagedConnection, self).connect()
     if self.connection_pool.check_connection:
         self.send_command('PING')
         if nativestr(self.read_response()) != 'PONG':
             raise ConnectionError('PING failed')
Ejemplo n.º 3
0
    def get_connection(self, command_name, *keys, **options):
        """
        Get a connection, blocking for ``self.timeout`` until a connection
        is available from the pool.

        If the connection returned is ``None`` then creates a new connection.
        Because we use a last-in first-out queue, the existing connections
        (having been returned to the pool after the initial ``None`` values
        were added) will be returned before ``None`` values. This means we only
        create new connections when we need to, i.e.: the actual number of
        connections will only increase in response to demand.
        """
        # Make sure we haven't changed process.
        self._checkpid()

        # Try and get a connection from the pool. If one isn't available within
        # self.timeout then raise a ``ConnectionError``.
        connection = None
        try:
            connection = self.pool.get(block=True, timeout=self.timeout)
        except Empty:
            # Note that this is not caught by the redis client and will be
            # raised unless handled by application code. If you want never to
            raise ConnectionError("No connection available.")

        # If the ``connection`` is actually ``None`` then that's a cue to make
        # a new connection to add to the pool.
        if connection is None:
            connection = self.make_connection()

        return connection
Ejemplo n.º 4
0
    def can_read(self):
        if not self._reader:
            raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)

        if self._next_response is False:
            self._next_response = self._reader.gets()
        return self._next_response is not False
Ejemplo n.º 5
0
    def _read_from_socket(self, length=None):
        socket_read_size = self.socket_read_size
        buf = self._buffer
        buf.seek(self.bytes_written)
        marker = 0

        try:
            while True:
                data = self._sock.recv(socket_read_size)
                # an empty string indicates the server shutdown the socket
                if isinstance(data, bytes) and len(data) == 0:
                    raise socket.error(SERVER_CLOSED_CONNECTION_ERROR)
                buf.write(data)
                data_length = len(data)
                self.bytes_written += data_length
                marker += data_length

                if length is not None and length > marker:
                    continue
                break
        except socket.timeout:
            raise TimeoutError("Timeout reading from socket")
        except socket.error:
            e = sys.exc_info()[1]
            raise ConnectionError("Error while reading from socket: %s" %
                                  (e.args,))
Ejemplo n.º 6
0
 def read_response(self):
     try:
         return super(SentinelManagedConnection, self).read_response()
     except ReadOnlyError:
         if self.connection_pool.is_master:
             # When talking to a master, a ReadOnlyError when likely
             # indicates that the previous master that we're still connected
             # to has been demoted to a slave and there's a new master.
             # calling disconnect will force the connection to re-query
             # sentinel during the next connect() attempt.
             self.disconnect()
             raise ConnectionError('The previous master is now a slave')
         raise
Ejemplo n.º 7
0
    def on_connect(self):
        "Initialize the connection, authenticate and select a database"
        self._parser.on_connect(self)

        # if a password is specified, authenticate
        if self.password:
            self.send_command('AUTH', self.password)
            if nativestr(self.read_response()) != 'OK':
                raise AuthenticationError('Invalid Password')

        # if a database is specified, switch to it
        if self.db:
            self.send_command('SELECT', self.db)
            if nativestr(self.read_response()) != 'OK':
                raise ConnectionError('Invalid Database')
Ejemplo n.º 8
0
    def read_response(self):
        response = self._buffer.readline()
        if not response:
            raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)

        byte, response = byte_to_chr(response[0]), response[1:]

        if byte not in ('-', '+', ':', '$', '*'):
            raise InvalidResponse("Protocol Error: %s, %s" %
                                  (str(byte), str(response)))

        # server returned an error
        if byte == '-':
            response = nativestr(response)
            error = self.parse_error(response)
            # if the error is a ConnectionError, raise immediately so the user
            # is notified
            if isinstance(error, ConnectionError):
                raise error
            # otherwise, we're dealing with a ResponseError that might belong
            # inside a pipeline response. the connection's read_response()
            # and/or the pipeline's execute() will raise this error if
            # necessary, so just return the exception instance here.
            return error
        # single value
        elif byte == '+':
            pass
        # int value
        elif byte == ':':
            response = long(response)
        # bulk response
        elif byte == '$':
            length = int(response)
            if length == -1:
                return None
            response = self._buffer.read(length)
        # multi-bulk response
        elif byte == '*':
            length = int(response)
            if length == -1:
                return None
            response = [self.read_response() for i in xrange(length)]
        if isinstance(response, bytes) and self.encoding:
            response = response.decode(self.encoding)
        return response
Ejemplo n.º 9
0
    def connect(self):
        "Connects to the Redis server if not already connected"
        if self._sock:
            return
        try:
            sock = self._connect()
        except socket.error:
            e = sys.exc_info()[1]
            raise ConnectionError(self._error_message(e))

        self._sock = sock
        try:
            self.on_connect()
        except RedisError:
            # clean up after any error in on_connect
            self.disconnect()
            raise

        # run any user callbacks. right now the only internal callback
        # is for pubsub channel/pattern resubscription
        for callback in self._connect_callbacks:
            callback(self)
Ejemplo n.º 10
0
 def send_packed_command(self, command):
     "Send an already packed command to the Redis server"
     if not self._sock:
         self.connect()
     try:
         if isinstance(command, str):
             command = [command]
         for item in command:
             self._sock.sendall(item)
     except socket.timeout:
         self.disconnect()
         raise TimeoutError("Timeout writing to socket")
     except socket.error:
         e = sys.exc_info()[1]
         self.disconnect()
         if len(e.args) == 1:
             _errno, errmsg = 'UNKNOWN', e.args[0]
         else:
             _errno, errmsg = e.args
         raise ConnectionError("Error %s while writing to socket. %s." %
                               (_errno, errmsg))
     except:
         self.disconnect()
         raise
Ejemplo n.º 11
0
 def make_connection(self):
     "Create a new connection"
     if self._created_connections >= self.max_connections:
         raise ConnectionError("Too many connections")
     self._created_connections += 1
     return self.connection_class(**self.connection_kwargs)