Example #1
0
    def _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')
    def handle_message(self, response, ignore_subscribe_messages=False):
        """
        Parses a pub/sub message. If the channel or pattern was subscribed to
        with a message handler_or_queue, the handler_or_queue is invoked instead of a parsed
        message being returned.
        """

        message_type = nativestr(response[0])
        if message_type == 'pmessage':
            message = {
                'type': message_type,
                'pattern': response[1],
                'channel': response[2],
                'data': response[3]
            }
        else:
            message = {
                'type': message_type,
                'pattern': None,
                'channel': response[1],
                'data': response[2]
            }

        if message_type in self.PUBLISH_MESSAGE_TYPES:

            # Incoming message.
            # Find the queue or callable, and context that are associated with
            # the incoming pattern/channel-name. Create a BusMessage, and either
            # call the callable with that instance, or place the instance in the
            # queue:

            handler_or_queue = None

            if message_type == 'pmessage':
                try:
                    (handler_or_queue,
                     context) = self.patterns.get(message['pattern'], None)
                except TypeError:
                    # No callable or queue associated with this pattern:
                    return None
            else:
                try:
                    (handler_or_queue,
                     context) = self.channels.get(message['channel'], None)
                except TypeError:
                    # No callable or queue associated with this channel name:
                    return None

            # Make a bus object, setting isJsonContent to True. This will
            # have the BusMessage __init__() method try to parse the data
            # as a JSON message that has content/id/time fields. If the message
            # is not proper JSON, the BusMessage init function will just put
            # the data itself into the content field:

            busMsg = BusMessage(content=message['data'],
                                topicName=message['channel'],
                                isJsonContent=True,
                                context=context)

            if isinstance(handler_or_queue, Queue.Queue):
                handler_or_queue.put(busMsg)
            else:
                try:
                    handler_or_queue(busMsg)
                except TypeError:
                    raise TypeError("Message delivery method for %s was neither a queue nor a callable: %s" %\
                                     (busMsg.topicName, handler_or_queue))

            return None

        elif message_type in self.UNSUBSCRIBE_MESSAGE_TYPES:
            # if this is an unsubscribe message, indicate that
            # (p)unsubscribe() method(s) may now remove the
            # subscription from memory:

            if message_type == 'punsubscribe':
                self.patterns.setUnsubscribeAckArrived()
            else:
                self.channels.setUnsubscribeAckArrived()
            if ignore_subscribe_messages or self.ignore_subscribe_messages:
                return None

        elif message_type in self.SUBSCRIBE_MESSAGE_TYPES:
            # this is a (p)subscribe message. ignore if we don't
            # want them, but let (p)subscribe() method(s) know that
            # the ack arrived:
            if message_type == 'psubscribe':
                self.patterns.setSubscribeAckArrived()
            else:
                self.channels.setSubscribeAckArrived()
            if ignore_subscribe_messages or self.ignore_subscribe_messages:
                return None

        return message
Example #3
0
    def parse_response(self, response=None, socket_buffer=None, encoding=None, block=True, timeout=None):
        '''
        Given a full line of Redis wire protocol,
        parse that line, requesting additional lines if
        needed by requesting them from the passed-in
        socket_buffer. That object must provide a 
        readline(block, timeout) method, else error.
        
        Examples for a response are:
            - *2
            - subscribe
            - $9
            
        This method is called recursively.
        
        :param response: one line of the wire protocol 
        :type response: string
        :param socket_buffer: object that provides a readline(block, timeout) method
        :type socket_buffer: {SocketLineReader | OneShotConnection | ...}
        :returns: parsed response
        :rtype: [string]
        :raise TimeoutError
        '''

        if encoding is None:
            encoding = self.connection.encoding

        if response is None:
            response = socket_buffer.readline(block=block, timeout=timeout)
            
        # Guard against closed or semi-closed sockets having
        # returned bad data:
        try:
            byte, response = byte_to_chr(response[0]), response[1:]
        except TypeError:
            # Ignore the badly formatted response:
            return None

        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
        # simple-string: response holds result:
        elif byte == '+':
            pass
        # int value
        elif byte == ':':
            response = long(response)
            
        # bulk response
        elif byte == '$':
            length = int(response)
            if length == -1:
                # Null string:
                return None
            response = socket_buffer.read(length)
                        
        # multi-bulk response
        elif byte == '*':
            length = int(response)
            if length == -1:
                return None
            response = [self.parse_response(response=None, 
                                            socket_buffer=socket_buffer, 
                                            block=block, 
                                            timeout=timeout,
                                            encoding=encoding) for _ in xrange(length)]
        if isinstance(response, bytes) and encoding:
            response = response.decode(encoding)
        #***********
        #print('Response: %s' % byte + '|' + str(response))
        #***********
                
        return response
    def handle_message(self, response, ignore_subscribe_messages=False):
        """
        Parses a pub/sub message. If the channel or pattern was subscribed to
        with a message handler_or_queue, the handler_or_queue is invoked instead of a parsed
        message being returned.
        """
        
        message_type = nativestr(response[0])
        if message_type == 'pmessage':
            message = {
                'type': message_type,
                'pattern': response[1],
                'channel': response[2],
                'data': response[3]
            }
        else:
            message = {
                'type': message_type,
                'pattern': None,
                'channel': response[1],
                'data': response[2]
            }

            
        if message_type in self.PUBLISH_MESSAGE_TYPES:

            # Incoming message.
            # Find the queue or callable, and context that are associated with
            # the incoming pattern/channel-name. Create a BusMessage, and either
            # call the callable with that instance, or place the instance in the
            # queue:
             
            handler_or_queue = None
            
            if message_type == 'pmessage':
                try:
                    (handler_or_queue, context) = self.patterns.get(message['pattern'], None)
                except TypeError:
                    # No callable or queue associated with this pattern:
                    return None
            else:
                try:
                    (handler_or_queue, context) = self.channels.get(message['channel'], None)
                except TypeError:
                    # No callable or queue associated with this channel name:
                    return None

            # Make a bus object, setting isJsonContent to True. This will
            # have the BusMessage __init__() method try to parse the data 
            # as a JSON message that has content/id/time fields. If the message
            # is not proper JSON, the BusMessage init function will just put
            # the data itself into the content field:
            
            busMsg = BusMessage(content=message['data'], topicName=message['channel'], isJsonContent=True, context=context)
            
            if isinstance(handler_or_queue, Queue.Queue):
                handler_or_queue.put(busMsg)
            else:
                try:
                    handler_or_queue(busMsg)
                except TypeError:
                    raise TypeError("Message delivery method for %s was neither a queue nor a callable: %s" %\
                                     (busMsg.topicName, handler_or_queue))
            
            return None
            
        elif message_type in self.UNSUBSCRIBE_MESSAGE_TYPES:
            # if this is an unsubscribe message, indicate that
            # (p)unsubscribe() method(s) may now remove the
            # subscription from memory:
            
            if message_type == 'punsubscribe':
                self.patterns.setUnsubscribeAckArrived()
            else:
                self.channels.setUnsubscribeAckArrived()
            if ignore_subscribe_messages or self.ignore_subscribe_messages:
                return None
            
        elif message_type in self.SUBSCRIBE_MESSAGE_TYPES:
            # this is a (p)subscribe message. ignore if we don't
            # want them, but let (p)subscribe() method(s) know that
            # the ack arrived:
            if message_type == 'psubscribe':
                self.patterns.setSubscribeAckArrived()
            else:
                self.channels.setSubscribeAckArrived()
            if ignore_subscribe_messages or self.ignore_subscribe_messages:
                return None

        return message