Skip to content
simple.py 3.99 KiB
Newer Older
Antoine Millet's avatar
Antoine Millet committed
#!/usr/bin/env python
#coding=utf8

import ssl
import select
import socket
import logging
Antoine Millet's avatar
Antoine Millet committed
from sjrpc.core import RpcConnection, ConnectionManager

class SimpleRpcClient(ConnectionManager):
    '''
    Create a new simple RPC client.
    
    :param connect: the :class:`RpcConnection` object to bind the client manager
    :param default_handler: the default handler to bind to the client connection
    :param on_disconnect: method on the handler to call when the client
        disconnects.
    '''

    def __init__(self, connection, default_handler=None, on_disconnect=None):
        super(SimpleRpcClient, self).__init__()
        
        self._on_disconnect = on_disconnect
        self._connection = connection
        self._connection.set_handler(default_handler)
Antoine Millet's avatar
Antoine Millet committed
        self.register(self._connection)

    @classmethod
    def from_addr(cls, addr, port, enable_ssl=False, cert=None, timeout=30.0,
Antoine Millet's avatar
Antoine Millet committed
                  default_handler=None, on_disconnect=None):
        '''
        Construct the instance of :class:`SimpleRpcClient` without providing
        the :class:`RpcConnection` object. The :class:`RpcConnection` is
        automatically created and passed to the standard constructor before to
        return the new instance.

        :param addr: the target ip address
        :param port: the target port
        :param ssl: enable SSL
        :param cert: is SSL is enabled, profile the filename of certificate to
            check. If None, don't check certificate.
        :param default_handler: the default handler to bind to the
            client connection
        :param on_disconnect: method on the handler to call when the client
            disconnects.
        '''

        connection = RpcConnection.from_addr(addr, port, None, timeout=timeout,
Antoine Millet's avatar
Antoine Millet committed
                                             enable_ssl=enable_ssl, cert=cert)
        client = cls(connection, default_handler=default_handler,
                     on_disconnect=on_disconnect)
Antoine Millet's avatar
Antoine Millet committed
        connection._manager = client
        return client

    @classmethod
    def from_sock(cls, sock, default_handler=None, on_disconnect=None):
        '''
        Construct the instance of :class:`SimpleRpcClient` without providing
        the :class:`RpcConnection` object. The :class:`RpcConnection` is
        automatically created and passed to the standard constructor before to
        return the new instance

        :param sock: the socket object to wrap with :class:`RpcConnection`
            object.
        :param default_handler: the default handler to bind to the
            client connection
        :param on_disconnect: method on the handler to call when the client
            disconnects.
        '''
        
        connection = RpcConnection(sock, None)
        client = cls(connection, default_handler, on_disconnect)
        connection._manager = client
        return client

    def shutdown(self):
        super(SimpleRpcClient, self).shutdown()
        self._connection.shutdown(self._on_disconnect)

    def handle_event(self, fd, event):
        if event & select.EPOLLIN:
            # Data are ready to be readed on socket
            try:
                self._connection.receive()
            except socket.error as err:
                logging.error('Socket error while receiving from the client '
                              'fd/%s: %s' % (fd, err))
Antoine Millet's avatar
Antoine Millet committed
                self.shutdown()

        if event & select.EPOLLOUT:
            # Data are ready to be written on socket
            try:
                self._connection.send()
            except socket.error as err:
                logging.error('Socket error while sending to the client '
                              'fd/%s: %s' % (fd, err))
Antoine Millet's avatar
Antoine Millet committed
                self.shutdown()

        if event & select.EPOLLHUP:
            logging.error('Socket HUP fd/%s' % fd)
Antoine Millet's avatar
Antoine Millet committed
            self.shutdown()                                                      #TODO

    def all_connections(self):
        return set((self._connection,))

    def call(self, *args, **kwargs):
        return self._connection.call(*args, **kwargs)