Ejemplo n.º 1
0
 def __init__(self, socket):
     self.socket = socket
     self.interfaces = []
     self.listeners = [] # List of tuples, each of which contains an
     # interface name and an event name.
     self.watches = [] # List of tuples, each of which contains an
     # interface name and an object name.
     self.message_queue = Queue()
     self.id = get_next_id()
     self.pending_responses = {} # Maps message ids of commands sent to this
     # connection to tuples consisting of the id of a connection that the
     # response should be forwarded to and the message id that the response
     # should have
     self.input_thread = InputThread(socket, self.message_arrived,
                                     self.input_closed)
     self.output_thread = OutputThread(socket, self.read_next_message)
Ejemplo n.º 2
0
class Connection(object):
    """
    Represents a connection. Connections are created when someone connects to
    autobus. They are destroyed when autobus exits or the connection is closed.
    Connections have an id, a list of interfaces they have registered, a queue
    containing protobuf Message objects to be sent to the corresponding
    socket as soon as possible, and an input and output thread.
    """
    def __init__(self, socket, address):
        self.socket = socket
        self.socket_address = address
        self.interfaces = []
        self.listeners = [] # List of tuples, each of which contains an
        # interface name and an event name.
        self.watches = [] # List of tuples, each of which contains an
        # interface name and an object name.
        self.message_queue = Queue()
        self.id = get_next_id()
        self.pending_responses = {} # Maps message ids of commands sent to this
        # connection to tuples consisting of the id of a connection that the
        # response should be forwarded to and the message id that the response
        # should have
        self.input_thread = InputThread(socket, self.message_arrived,
                                        self.input_closed)
        self.output_thread = OutputThread(socket, self.read_next_message)
    
    def register(self):
        """
        Registers this connection with the global connection map. This must
        only be called on the event thread.
        """
        print "New connection " + str(self.id) + " from " + str(self.socket_address)
        connection_map[self.id] = self
    
    def message_arrived(self, message):
        try:
            function = find_function_for_message(message)
        except:
            print "Client sent an invalid command (they will be disconnected):"
            print_exc()
            raise
        event_queue.put((self.id, partial(function, message)), block=True)
    
    def input_closed(self):
        event_queue.put((self.id, discard_args(self.shutdown_and_deregister)),
                        block=True)
        self.message_queue.put(None, block=True)
    
    def read_next_message(self):
        return self.message_queue.get(block=True)
    
    def close(self):
        """
        Closes the underlying socket for this connection. This, in turn, causes
        the input and output threads to shut down. This can be called from any
        thread.
        """
        self.socket.close()
    
    def send(self, message):
        """
        Adds the specified message to this connection's message queue. It will
        be sent to the underlying socket as soon as possible by the
        connection's output thread. This can be called from any thread.
        
        If the message is None, this function returns without doing anything.
        This allows for code that constructs a message pair with
        create_message_pair and then sends the message without worrying about
        whether or not create_message_pair returned an empty message (which it
        would if it was used to "reply" to a notification instead of to a
        command).
        """
        if message is not None:
            if isinstance(message, dict) and message["message_type"] is None:
                return
            self.message_queue.put(message, block=True)
    
    def send_new(self, *args, **kwargs):
        """
        Exactly the same as
        self.send(libautobus.create_message(*args, **kwargs)).
        """
        self.send(create_message(*args, **kwargs))
    
    def send_error(self, in_reply_to=None, **kwargs):
        """
        Sends an error back to the client. If in_reply_to is itself a
        notification message, no message will be sent back. Otherwise, an
        ErrorResponse to the specified message will be sent. The error's
        fields will be initialized to have any additional keyword arugments
        specified. You'll typically do:
        
        send_error(some_message, text="A random problem happened")
        """
        if in_reply_to == None:
            in_reply_to = NOTIFICATION
        message = create_message(ErrorResponse, in_reply_to, **kwargs)
        self.send(message)
    
    def start(self):
        """
        Starts this connection's input and output threads.
        """
        self.input_thread.start()
        self.output_thread.start()
    
    def shutdown_and_deregister(self):
        """
        Shuts this connection down and deregisters it. This also pushes error
        messages onto the message queues of any connections that were waiting
        for a response from this connection. This must only be called from the
        event thread.
        """
        print "Connection " + str(self.id) + " from " + str(self.socket_address) + " shutting down"
        try:
            self.socket.shutdown(SHUT_RDWR)
        except:
            pass
        self.socket.close()
        if self.id in connection_map:
            del connection_map[self.id]
        for connection_id, message_id in self.pending_responses.values():
            if connection_id in connection_map:
                connection_map[connection_id].send(create_error_response(
                        message_id, "The connection this response was waiting "
                        "on closed unexpectedly"))
        for interface in self.interfaces:
            interface.deregister()
        for interface_name, event_name in self.listeners:
            deregister_event_listener(interface_name, event_name, self.id)
        for interface_name, object_name in self.watches:
            deregister_object_watch(interface_name, object_name, self.id)