Example #1
0
class Server():
    Logger = logger.get(__name__)

    def __init__(self, port, conn_limit, blocking=False):
        self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._sock.setsockopt(socket.SOL_SOCKET,
                              socket.SO_REUSEADDR,
                              conn_limit)
        self._sock.bind(('', port))
        self._sock.listen(conn_limit)
        self._clients = {}
        self.on_data = Event('tcp.server.on_data')
        self.on_cleanup = Event('tcp.server.on_cleanup')
        if blocking:
            self.listen()
        else:
            self._thread = threading.Thread(target=self.listen)
            self._thread.daemon = True
            self._thread.start()

    def listen(self):
        while True:
            clientsock, addr = self._sock.accept()
            address = Address(addr[0], addr[1])
            self._clients[str(address)] = clientsock
            self.Logger.info('Connected from:', addr)
            listenThread = threading.Thread(target=self.handler,
                                            args=(clientsock, address))
            listenThread.start()

    def handler(self, clientsock, address):
        buf = 1024
        while True:
            try:
                data = clientsock.recv(buf)
                if not data:
                    self.Logger.info("Connection Broken", address)
                    break
                data = json.loads(data.decode('UTF-8'))
                self.Logger.debug("%s -> %s" % (address, data))
                self.on_data(address, data)
            except Exception as e:
                self.Logger.error(traceback.format_exc())
        clientsock.close()
        self.on_cleanup(address)

    def send_data(self, address, data):
        data = json.dumps(data)
        self.Logger.debug("%s <- %s" % (address, data))
        self._clients[str(address)].send(bytes(data, 'UTF-8'))
Example #2
0
#!/usr/bin/python3
from collections import namedtuple

from common import Contact, Address, Hash, Event
from common import List as list
from common import btlxlogger as logger
from .exceptions import NoContactsError

Logger = logger.get(__name__)


class SearchContact():
    """
    Simple struct for search contacts.
    Must be a class because namedtuples can not be modified.

    .. attribute:: contacted

         If a contact has been contacted yet
    .. attribute:: contact

        The contact
    """
    def __init__(self, contacted, contact):
        self.contacted = contacted
        self.contact = contact

    def __str__(self):
        return "<%s: %s>" % (self.contacted, self.contact)

Example #3
0
from datetime import datetime

from common import Event, List as list
from common import btlxlogger as logger

Logger = logger.get(__name__)


class Bucket:
    """
    .. attribute:: contacts

        :class:`list` of len :attr:`kademlia.K`
    .. attribute:: waitlist

        :class:`list` of len :attr:`kademlia.K`
    .. attribute:: on_added

        Event(:class:`common.Event`)
    .. attribute:: on_removed

        Event(:class:`common.Event`)
    """
    def __init__(self, K):
        self.K = K
        self.contacts = list()
        self.waitlist = list()
        self.on_added = Event('Bucket.on_added')
        self.on_removed = Event('Bucket.on_removed')

    def update(self, contact, report=True):
Example #4
0
class UIServer():
    Logger = logger.get(__name__)

    def __init__(self, port, max_conns):
        self.server = Server(port, max_conns)
        self.server.on_data += self._data_handler
        self.server.on_cleanup += self._clean_contact
        self.subscribers = {}
        self.properties = {}
        self._properties_lock = Lock()
        self._subscriber_lock = Lock()
        self._updated_properties = set()
        # Update Vars
        self._last_update = datetime.now()
        self._updating = False
        self._update_lock = Lock()
        # Handlers
        self.proto = protocol.get()
        for x in filter(lambda y: y.mtype == protocol.client,
                        self.proto.values()):
            x.handler = self.__getattribute__('_%s_handler' % x.name)

    def add_property(self, name, path, object_root):
        obj = object_root
        for item in path.split('.'):
            obj = obj.__getattribute__(item)
        with self._properties_lock:
            if name not in self.properties:
                self.properties[name] = obj
                obj.on_changed += self.on_property_update

    def send_data(self, sub,  message_name, data):
        message = self.proto[message_name].pack(data)
        self.server.send_data(sub.address, message)

    #: Min time between updates
    UPDATE_DELTA = timedelta(seconds=2)

    def try_update(self):
        """
        Function that checks to see if an update needs to be started.
        This is locked for multithreading,
        and spawns another thread to do the update.
        """
        with self._update_lock:
            if (datetime.now() - self.UPDATE_DELTA > self._last_update
                    and not self._updating):
                self._updating = True
                self._last_update = datetime.now()
                # Get the party started
                t = Thread(target=self._update)
                t.daemon = True
                t.start()

    def _update(self):
        try:
            with self._properties_lock:
                props = self._updated_properties
                self._updated_properties = set()
            # Used because transforming some of these items is expensive.
            # This could be done in one huge ass expression,
            # but that may be unreadable.
            self.Logger.debug("Subcribers: %s", self.subscribers)
            with self._subscriber_lock:
                subbed_props = reduce(lambda x, y: x.union(y),
                                      (x.subscriptions for x
                                       in self.subscribers.values()),
                                      set()).intersection(props)
                cached_vals = {x: self.properties[x].flatten()
                               for x in subbed_props}
                # Check if anything changed
                if len(cached_vals) > 0:
                    for sub in self.subscribers.values():
                        # Collect all data the client is subscribed to.
                        data = reduce(lambda x, y: dict(x, **y),
                                      (cached_vals[x] for x in props
                                       if x in sub.subscriptions),
                                      {})
                        # Send data to the clients
                        if len(data) > 0:
                            self.send_data(sub, 'update', data)
        finally:
            self._updating = False

    def on_property_update(self, name, value):
        """
        Handler for when a watched property is updated.
        We only store the name, since the value may change a lot.
        The value is also of unknown type, not the json-compatible
        types needed.
        """
        with self._properties_lock:
            self.Logger.debug("updating %s (%s)", name, value)
            self._updated_properties.add(name)
        self.try_update()

    def get_subscriber(self, address):
        """
        Gets the subscriber at the given address.
        If none exists, one is created.
        """
        try:
            client = self.subscribers[str(address)]
        except KeyError:
            client = Client(address)
            with self._subscriber_lock:
                self.subscribers[str(address)] = client
        return client

    def _clean_contact(self, address):
        """
        Handler for cleaning up a contact once they disconnect.
        This is processed from the quitting listener's thread.
        """
        with self._subscriber_lock:
            del(self.subscribers[str(address)])

    def _data_handler(self, address, payload):
        """
        Handler for when data is received.
        """
        subscriber = self.get_subscriber(address)
        try:
            cmd_name = payload['command']
            data = payload['values']
            self.proto[cmd_name].handler(subscriber, data)
        except Exception as e:
            self.Logger.error(traceback.format_exc())

    def _get_events_handler(self, subscriber, data):
        names = [x for x in self.properties.keys()]
        self.Logger.debug("Events: %s", names)
        self.send_data(subscriber, 'events', names)

    def _subscribe_handler(self, subscriber, data):
        for value in (x for x in data if x in self.properties):
            # Check only to look for a data update
            if value not in subscriber.subscriptions:
                subscriber.subscriptions.add(value)
                self.send_data(subscriber, 'update',
                               self.properties[value].flatten())

    def _unsubscribe_handler(self, subscriber, data):
        for value in (x for x in data if x in self.properties):
            try:
                subscriber.subscriptions.remove(value)
            except KeyError:
                pass
Example #5
0
#!/usr/bin/python3
from .bucket import Buckets
from .shortlist import Shortlists
from common import dbinterface
from common import btlxlogger as logger

Logger = logger.get('kademlia')


class Kademlia():
    """
    The main interface to the kademlia DHT.
    Because the DHT has to stay updated with all the
    packets transferred, this also must be the hub
    of all network traffic.

    .. attribute:: shortlists

        The kademlia :class:`~kademlia.Shortlists`
    .. attribute:: buckets

        The kademlia :class:`~kademlia.Buckets`
    .. db_conn

        Database handle :class:`common.dbinterface`
    """
    def __init__(self, net, own_contact, dir_, K, B, A):
        """
        :param net:
        :type net: :class:`~net.BytelynxStack`
        :param own_contact:
Example #6
0
#!/usr/bin/python3
from .bucket import Buckets
from .shortlist import Shortlists
from common import dbinterface
from common import btlxlogger as logger

Logger = logger.get('kademlia')


class Kademlia():
    """
    The main interface to the kademlia DHT.
    Because the DHT has to stay updated with all the
    packets transferred, this also must be the hub
    of all network traffic.

    .. attribute:: shortlists

        The kademlia :class:`~kademlia.Shortlists`
    .. attribute:: buckets

        The kademlia :class:`~kademlia.Buckets`
    .. db_conn

        Database handle :class:`common.dbinterface`
    """

    def __init__(self, net, own_contact, dir_, K, B, A):
        """
        :param net:
        :type net: :class:`~net.BytelynxStack`