def __init__(self, file_xml): ProxyCoAP.__init__(self) self._mapping = {} root = Resource('root', visible=False, observable=False, allow_children=True) root.path = '/' self.root = Tree(root) self.file_xml = file_xml
def __init__(self, server, forward): self._forward = forward self.received = {} self.sent = {} self.sent_token = {} self.received_token = {} self.call_id = {} self.relation = {} self._currentMID = 1 import socket try: socket.inet_aton(server[0]) self.server = server # legal except socket.error: # Not legal data = socket.gethostbyname(server[0]) self.server = (data, server[1]) # defer = reactor.resolve('coap.me') # defer.addCallback(self.start) # self.server = (None, 5683) root = Resource('root', visible=False, observable=False, allow_children=True) root.path = '/' self.root = Tree(root) self.operations = [] self.l = None self.transport = None
def __init__(self, server_address, multicast=False, starting_mid=None, cache=False, sock=None): """ Initialize the Forward Proxy. :param server_address: Server address for incoming connections :param multicast: if the ip is a multicast address :param starting_mid: used for testing purposes :param cache: if a cache must be used :param sock: if a socket has been created externally, it can be used directly """ self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self.cache_enable = cache self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() if self.cache_enable: self._cacheLayer = CacheLayer(defines.FORWARD_PROXY) else: self._cacheLayer = None self._forwardLayer = ForwardLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=True) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if sock is not None: # Use given socket, could be a DTLS socket self._socket = sock elif self.multicast: # pragma: no cover # Create a socket # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255) # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) # Join group if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(('', self.server_address[1])) mreq = struct.pack("4sl", socket.inet_aton(defines.ALL_COAP_NODES), socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) else: # Bugfix for Python 3.6 for Windows ... missing IPPROTO_IPV6 constant if not hasattr(socket, 'IPPROTO_IPV6'): socket.IPPROTO_IPV6 = 41 self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(('', self.server_address[1])) addrinfo_multicast = socket.getaddrinfo( defines.ALL_COAP_NODES_IPV6, 5683)[0] group_bin = socket.inet_pton(socket.AF_INET6, addrinfo_multicast[4][0]) mreq = group_bin + struct.pack('@I', 0) self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address)
def __init__(self, server_address, multicast=False, starting_mid=None): """ Initialize the server. :param server_address: Server address for incoming connections :param multicast: if the ip is a multicast address :param starting_mid: used for testing purposes """ self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=False) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if self.multicast: # pragma: no cover # Create a socket self._socket = socket.socket(addrinfo[1], socket.SOCK_DGRAM) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255) self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) # Bind it to the port self._socket.bind(('', self.server_address[1])) # Join group if addrinfo[0] == socket.AF_INET: # IPv4 addrinfo_multicast = socket.getaddrinfo( defines.ALL_COAP_NODES, None)[0] group_bin = socket.inet_pton(addrinfo_multicast[1], addrinfo_multicast[4][0]) mreq = group_bin + struct.pack('=I', socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) self._unicast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._unicast_socket.bind(self.server_address) else: addrinfo_multicast = socket.getaddrinfo( defines.ALL_COAP_NODES_IPV6, None)[0] group_bin = socket.inet_pton(addrinfo_multicast[1], addrinfo_multicast[4][0]) mreq = group_bin + struct.pack('@I', 0) self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._unicast_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._unicast_socket.bind(self.server_address) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address)
class CoAP(object): """ Implementation of the Reverse Proxy """ def __init__(self, server_address, xml_file, multicast=False, starting_mid=None, cache=False, sock=None): """ Initialize the Reverse Proxy. :param server_address: Server address for incoming connections :param xml_file: the xml file that describe remote servers :param multicast: if the ip is a multicast address :param starting_mid: used for testing purposes :param cache: if a cache must be used :param sock: if a socket has been created externally, it can be used directly """ self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._forwardLayer = ForwardLayer(self) self.resourceLayer = ResourceLayer(self) self.cache_enable = cache if self.cache_enable: self._cacheLayer = CacheLayer(defines.REVERSE_PROXY) else: self._cacheLayer = None # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=True) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast self.file_xml = xml_file self._mapping = {} addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if sock is not None: # Use given socket, could be a DTLS socket self._socket = sock elif self.multicast: # pragma: no cover # Create a socket # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255) # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) # Join group if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind( (defines.ALL_COAP_NODES, self.server_address[1])) mreq = struct.pack("4sl", socket.inet_aton(defines.ALL_COAP_NODES), socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) self._unicast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._unicast_socket.bind(self.server_address) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind( (defines.ALL_COAP_NODES_IPV6, self.server_address[1])) addrinfo_multicast = socket.getaddrinfo( defines.ALL_COAP_NODES_IPV6, 5683)[0] group_bin = socket.inet_pton(socket.AF_INET6, addrinfo_multicast[4][0]) mreq = group_bin + struct.pack('@I', 0) self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._unicast_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._unicast_socket.bind(self.server_address) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) # self.parse_config() def parse_config(self): """ Parse the xml file with remote servers and discover resources on each found server. """ tree = ElementTree.parse(self.file_xml) root = tree.getroot() for server in root.findall('server'): destination = server.text name = server.get("name") self.discover_remote(destination, name) def discover_remote(self, destination, name): """ Discover resources on remote servers. :param destination: the remote server (ip, port) :type destination: tuple :param name: the name of the remote server :type name: String """ assert (isinstance(destination, str)) if destination.startswith("["): split = destination.split("]", 1) host = split[0][1:] port = int(split[1][1:]) else: split = destination.split(":", 1) host = split[0] port = int(split[1]) server = (host, port) client = HelperClient(server) response = client.discover() client.stop() self.discover_remote_results(response, name) def discover_remote_results(self, response, name): """ Create a new remote server resource for each valid discover response. :param response: the response to the discovery request :param name: the server name """ host, port = response.source if response.code == defines.Codes.CONTENT.number: resource = Resource('server', self, visible=True, observable=False, allow_children=True) self.add_resource(name, resource) self._mapping[name] = (host, port) self.parse_core_link_format(response.payload, name, (host, port)) else: logger.error("Server: " + response.source + " isn't valid.") def parse_core_link_format(self, link_format, base_path, remote_server): """ Parse discovery results. :param link_format: the payload of the response to the discovery request :param base_path: the base path used to create child resources discovered on the remote server :param remote_server: the (ip, port) of the remote server """ while len(link_format) > 0: pattern = "<([^>]*)>;" result = re.match(pattern, link_format) path = result.group(1) path = path.split("/") path = path[1:][0] link_format = link_format[result.end(1) + 2:] pattern = "([^<,])*" result = re.match(pattern, link_format) attributes = result.group(0) dict_att = {} if len(attributes) > 0: attributes = attributes.split(";") for att in attributes: a = att.split("=") if len(a) > 1: dict_att[a[0]] = a[1] else: dict_att[a[0]] = a[0] link_format = link_format[result.end(0) + 1:] # TODO handle observing resource = RemoteResource('server', remote_server, path, coap_server=self, visible=True, observable=False, allow_children=True) resource.attributes = dict_att self.add_resource(base_path + "/" + path, resource) logger.info(self.root.dump()) def purge(self): """ Clean old transactions """ while not self.stopped.isSet(): self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME) self._messageLayer.purge() def listen(self, timeout=10): """ Listen for incoming messages. Timeout is used to check if the server must be switched off. :param timeout: Socket Timeout in seconds """ self._socket.settimeout(float(timeout)) while not self.stopped.isSet(): try: data, client_address = self._socket.recvfrom(4096) except socket.timeout: continue try: self.receive_datagram((data, client_address)) except RuntimeError: logger.exception("Exception with Executor") self._socket.close() def close(self): """ Stop the server. """ logger.info("Stop server") self.stopped.set() for event in self.to_be_stopped: event.set() self._socket.close() def receive_datagram(self, args): """ Handle messages coming from the udp socket. :param args: (data, client_address) """ data, client_address = args serializer = Serializer() message = serializer.deserialize(data, client_address) if isinstance(message, int): logger.error("receive_datagram - BAD REQUEST") rst = Message() rst.destination = client_address rst.type = defines.Types["RST"] rst.code = message self.send_datagram(rst) return logger.debug("receive_datagram - " + str(message)) if isinstance(message, Request): transaction = self._messageLayer.receive_request(message) if transaction.request.duplicated and transaction.completed: logger.debug("message duplicated,transaction completed") transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) transaction = self._messageLayer.send_response(transaction) self.send_datagram(transaction.response) return elif transaction.request.duplicated and not transaction.completed: logger.debug("message duplicated,transaction NOT completed") self._send_ack(transaction) return transaction.separate_timer = self._start_separate_timer( transaction) transaction = self._blockLayer.receive_request(transaction) if transaction.block_transfer: self._stop_separate_timer(transaction.separate_timer) transaction = self._messageLayer.send_response(transaction) self.send_datagram(transaction.response) return transaction = self._observeLayer.receive_request(transaction) """ call to the cache layer to check if there's a cached response for the request if not, call the forward layer """ if self._cacheLayer is not None: transaction = self._cacheLayer.receive_request(transaction) if transaction.cacheHit is False: logger.debug(transaction.request) transaction = self._forwardLayer.receive_request_reverse( transaction) logger.debug(transaction.response) transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) transaction = self._cacheLayer.send_response(transaction) else: transaction = self._forwardLayer.receive_request_reverse( transaction) transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) self._stop_separate_timer(transaction.separate_timer) transaction = self._messageLayer.send_response(transaction) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retrasmission(transaction, transaction.response) self.send_datagram(transaction.response) elif isinstance(message, Message): transaction = self._messageLayer.receive_empty(message) if transaction is not None: transaction = self._blockLayer.receive_empty( message, transaction) self._observeLayer.receive_empty(message, transaction) else: # pragma: no cover logger.error("Received response from %s", message.source) def send_datagram(self, message): """ Send a message through the udp socket. :type message: Message :param message: the message to send """ if not self.stopped.isSet(): host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) self._socket.sendto(message, (host, port)) def add_resource(self, path, resource): """" Helper function to add resources to the resource directory during server initialization. :param path: the path for the new created resource :type resource: Resource :param resource: the resource to be added """ assert isinstance(resource, Resource) path = path.strip("/") paths = path.split("/") actual_path = "" i = 0 for p in paths: i += 1 actual_path += "/" + p try: res = self.root[actual_path] except KeyError: res = None if res is None: if len(paths) != i: return False resource.path = actual_path self.root[actual_path] = resource return True def _start_retrasmission(self, transaction, message): """ Start the retransmission task. :type transaction: Transaction :param transaction: the transaction that owns the message that needs retransmission :type message: Message :param message: the message that needs the retransmission task """ with transaction: if message.type == defines.Types['CON']: future_time = random.uniform( defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR)) transaction.retransmit_thread = threading.Thread( target=self._retransmit, args=(transaction, message, future_time, 0)) transaction.retransmit_stop = threading.Event() self.to_be_stopped.append(transaction.retransmit_stop) transaction.retransmit_thread.start() def _retransmit(self, transaction, message, future_time, retransmit_count): """ Thread function to retransmit the message in the future :param transaction: the transaction that owns the message that needs retransmission :param message: the message that needs the retransmission task :param future_time: the amount of time to wait before a new attempt :param retransmit_count: the number of retransmissions """ with transaction: while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \ and not self.stopped.isSet(): transaction.retransmit_stop.wait(timeout=future_time) if not message.acknowledged and not message.rejected and not self.stopped.isSet( ): retransmit_count += 1 future_time *= 2 self.send_datagram(message) if message.acknowledged or message.rejected: message.timeouted = False else: logger.warning("Give up on message {message}".format( message=message.line_print)) message.timeouted = True if message.observe is not None: self._observeLayer.remove_subscriber(message) try: self.to_be_stopped.remove(transaction.retransmit_stop) except ValueError: pass transaction.retransmit_stop = None transaction.retransmit_thread = None def _start_separate_timer(self, transaction): """ Start a thread to handle separate mode. :type transaction: Transaction :param transaction: the transaction that is in processing :rtype : the Timer object """ t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack, (transaction, )) t.start() return t @staticmethod def _stop_separate_timer(timer): """ Stop the separate Thread if an answer has been already provided to the client. :param timer: The Timer object """ timer.cancel() def _send_ack(self, transaction): """ Sends an ACK message for the request. :param transaction: the transaction that owns the request """ ack = Message() ack.type = defines.Types['ACK'] if not transaction.request.acknowledged: ack = self._messageLayer.send_empty(transaction, transaction.request, ack) self.send_datagram(ack)
def __init__(self, server_address, multicast=False, starting_mid=None, sock=None, cb_ignore_listen_exception=None): """ Initialize the server. :param server_address: Server address for incoming connections :param multicast: if the ip is a multicast address :param starting_mid: used for testing purposes :param sock: if a socket has been created externally, it can be used directly :param cb_ignore_listen_exception: Callback function to handle exception raised during the socket listen operation """ self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=False) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast self._cb_ignore_listen_exception = cb_ignore_listen_exception addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if sock is not None: # Use given socket, could be a DTLS socket self._socket = sock elif self.multicast: # pragma: no cover # Create a socket # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255) # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) # Join group if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(('', self.server_address[1])) mreq = struct.pack("4sl", socket.inet_aton(defines.ALL_COAP_NODES), socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind((defines.ALL_COAP_NODES_IPV6, self.server_address[1])) addrinfo_multicast = socket.getaddrinfo(defines.ALL_COAP_NODES_IPV6, 5683)[0] group_bin = socket.inet_pton(socket.AF_INET6, addrinfo_multicast[4][0]) mreq = group_bin + struct.pack('@I', 0) self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._unicast_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._unicast_socket.bind(self.server_address) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address)
def __init__(self, server_address, xml_file, multicast=False, starting_mid=None): self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._forwardLayer = ForwardLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=True) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast self.file_xml = xml_file self._mapping = {} addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if self.multicast: # pragma: no cover # Create a socket self._socket = socket.socket(addrinfo[1], socket.SOCK_DGRAM) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Bind it to the port self._socket.bind(('', self.server_address[1])) group_bin = socket.inet_pton(addrinfo[1], addrinfo[4][0]) # Join group if addrinfo[0] == socket.AF_INET: # IPv4 mreq = group_bin + struct.pack('=I', socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) else: mreq = group_bin + struct.pack('@I', 0) self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) self.parse_config()
class CoAP(object): def __init__(self, server_address, xml_file, multicast=False, starting_mid=None): self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._forwardLayer = ForwardLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=True) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast self.file_xml = xml_file self._mapping = {} addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if self.multicast: # pragma: no cover # Create a socket self._socket = socket.socket(addrinfo[1], socket.SOCK_DGRAM) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Bind it to the port self._socket.bind(('', self.server_address[1])) group_bin = socket.inet_pton(addrinfo[1], addrinfo[4][0]) # Join group if addrinfo[0] == socket.AF_INET: # IPv4 mreq = group_bin + struct.pack('=I', socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) else: mreq = group_bin + struct.pack('@I', 0) self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) self.parse_config() def parse_config(self): tree = ElementTree.parse(self.file_xml) root = tree.getroot() for server in root.findall('server'): destination = server.text name = server.get("name") self.discover_remote(destination, name) def discover_remote(self, destination, name): assert (isinstance(destination, str)) split = destination.split(":", 1) host = split[0] port = int(split[1]) server = (host, port) client = HelperClient(server) response = client.discover() client.stop() self.discover_remote_results(response, name) def discover_remote_results(self, response, name): host, port = response.source if response.code == defines.Codes.CONTENT.number: resource = Resource('server', self, visible=True, observable=False, allow_children=True) self.add_resource(name, resource) self._mapping[name] = (host, port) self.parse_core_link_format(response.payload, name, (host, port)) else: logger.error("Server: " + response.source + " isn't valid.") def parse_core_link_format(self, link_format, base_path, remote_server): while len(link_format) > 0: pattern = "<([^>]*)>;" result = re.match(pattern, link_format) path = result.group(1) path = path.split("/") path = path[1:][0] link_format = link_format[result.end(1) + 2:] pattern = "([^<,])*" result = re.match(pattern, link_format) attributes = result.group(0) dict_att = {} if len(attributes) > 0: attributes = attributes.split(";") for att in attributes: a = att.split("=") # TODO check correctness dict_att[a[0]] = a[1] link_format = link_format[result.end(0) + 1:] # TODO handle observing resource = RemoteResource('server', remote_server, path, coap_server=self, visible=True, observable=False, allow_children=True) resource.attributes = dict_att self.add_resource(base_path + "/" + path, resource) logger.info(self.root.dump()) def purge(self): while not self.stopped.isSet(): self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME) self._messageLayer.purge() def listen(self, timeout=10): """ Listen for incoming messages. Timeout is used to check if the server must be switched off. :param timeout: Socket Timeout in seconds """ self._socket.settimeout(float(timeout)) while not self.stopped.isSet(): try: data, client_address = self._socket.recvfrom(4096) except socket.timeout: continue try: self.receive_datagram((data, client_address)) except RuntimeError: print "Exception with Executor" self._socket.close() def close(self): """ Stop the server. """ logger.info("Stop server") self.stopped.set() for event in self.to_be_stopped: event.set() self._socket.close() def receive_datagram(self, args): """ Receive datagram from the udp socket. :rtype : Message """ data, client_address = args serializer = Serializer() message = serializer.deserialize(data, client_address) if isinstance(message, int): logger.error("receive_datagram - BAD REQUEST") rst = Message() rst.destination = client_address rst.type = defines.Types["RST"] rst.code = message self.send_datagram(rst) return logger.debug("receive_datagram - " + str(message)) if isinstance(message, Request): transaction = self._messageLayer.receive_request(message) if transaction.request.duplicated and transaction.completed: logger.debug("message duplicated,transaction completed") transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) transaction = self._messageLayer.send_response(transaction) self.send_datagram(transaction.response) return elif transaction.request.duplicated and not transaction.completed: logger.debug("message duplicated,transaction NOT completed") self._send_ack(transaction) return transaction.separate_timer = self._start_separate_timer( transaction) transaction = self._blockLayer.receive_request(transaction) if transaction.block_transfer: self._stop_separate_timer(transaction.separate_timer) transaction = self._messageLayer.send_response(transaction) self.send_datagram(transaction.response) return transaction = self._observeLayer.receive_request(transaction) transaction = self._forwardLayer.receive_request_reverse( transaction) transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) self._stop_separate_timer(transaction.separate_timer) transaction = self._messageLayer.send_response(transaction) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retrasmission(transaction, transaction.response) self.send_datagram(transaction.response) elif isinstance(message, Message): transaction = self._messageLayer.receive_empty(message) if transaction is not None: transaction = self._blockLayer.receive_empty( message, transaction) self._observeLayer.receive_empty(message, transaction) else: # pragma: no cover logger.error("Received response from %s", message.source) def send_datagram(self, message): """ :type message: Message :param message: """ if not self.stopped.isSet(): host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) self._socket.sendto(message, (host, port)) def add_resource(self, path, resource): """ Helper function to add resources to the resource directory during server initialization. :type resource: Resource :param resource: """ assert isinstance(resource, Resource) path = path.strip("/") paths = path.split("/") actual_path = "" i = 0 for p in paths: i += 1 actual_path += "/" + p try: res = self.root[actual_path] except KeyError: res = None if res is None: if len(paths) != i: return False resource.path = actual_path self.root[actual_path] = resource return True def _start_retrasmission(self, transaction, message): """ :type transaction: Transaction :param transaction: :type message: Message :param message: :rtype : Future """ if message.type == defines.Types['CON']: future_time = random.uniform( defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR)) transaction.retransmit_thread = threading.Thread( target=self._retransmit, args=(transaction, message, future_time, 0)) transaction.retransmit_stop = threading.Event() self.to_be_stopped.append(transaction.retransmit_stop) transaction.retransmit_thread.start() def _retransmit(self, transaction, message, future_time, retransmit_count): while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \ and not self.stopped.isSet(): transaction.retransmit_stop.wait(timeout=future_time) if not message.acknowledged and not message.rejected and not self.stopped.isSet( ): retransmit_count += 1 future_time *= 2 self.send_datagram(message) if message.acknowledged or message.rejected: message.timeouted = False else: logger.warning("Give up on message {message}".format( message=message.line_print)) message.timeouted = True if message.observe is not None: self._observeLayer.remove_subscriber(message) try: self.to_be_stopped.remove(transaction.retransmit_stop) except ValueError: pass transaction.retransmit_stop = None transaction.retransmit_thread = None def _start_separate_timer(self, transaction): """ :type transaction: Transaction :param transaction: :type message: Message :param message: :rtype : Future """ t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack, (transaction, )) t.start() return t @staticmethod def _stop_separate_timer(timer): """ :type future: Future :param future: """ timer.cancel() def _send_ack(self, transaction): # Handle separate """ Sends an ACK message for the request. :param request: [request, sleep_time] or request """ ack = Message() ack.type = defines.Types['ACK'] if not transaction.request.acknowledged: ack = self._messageLayer.send_empty(transaction, transaction.request, ack) self.send_datagram(ack)
def __init__(self, server_address, multicast=False, starting_mid=None, sock=None, cb_ignore_listen_exception=None): """ Initialize the server. :param server_address: Server address for incoming connections :param multicast: if the ip is a multicast address :param starting_mid: used for testing purposes :param sock: if a socket has been created externally, it can be used directly :param cb_ignore_listen_exception: Callback function to handle exception raised during the socket listen operation """ self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=False) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast self._cb_ignore_listen_exception = cb_ignore_listen_exception self._socketlist = [] addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if sock is not None: # Use given socket, could be a DTLS socket self._socket = sock self._socketlist.append(sock) elif self.multicast: # pragma: no cover # Join group if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(('', self.server_address[1])) mreq = struct.pack("4sl", socket.inet_aton(defines.ALL_COAP_NODES), socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) else: # IPv6 self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) self._socketlist.append(self._socket) ## multicast on OCF nodes : link local self._socket2 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allows address to be reused self._socket2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allows port to be reused try: self._socket2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except: pass self._socket2.bind(('', 5683)) # Allow messages from this socket to loop back for development try: self._socket2.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True) except: self._socket2.setsockopt(41, socket.IPV6_MULTICAST_LOOP, True) # Construct message for joining multicast group mreq = struct.pack("16s15s".encode('utf-8'), socket.inet_pton(socket.AF_INET6, defines.ALL_OCF_NODES_S3_IPV6), (chr(0) * 16).encode('utf-8')) #self._socket2.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._socket2.setsockopt(41, socket.IPV6_JOIN_GROUP, mreq) self._socketlist.append(self._socket2) ## multicast on OCF nodes: site local self._socket3 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allows address to be reused self._socket3.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allows port to be reused try: self._socket3.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except: pass self._socket3.bind(('', 5683)) # Allow messages from this socket to loop back for development try: self._socket3.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True) except: self._socket3.setsockopt(41, socket.IPV6_MULTICAST_LOOP, True) # Construct message for joining multicast group mreq = struct.pack("16s15s".encode('utf-8'), socket.inet_pton(socket.AF_INET6, defines.ALL_OCF_NODES_S5_IPV6), (chr(0) * 16).encode('utf-8')) try: self._socket3.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) except: self._socket3.setsockopt(41, socket.IPV6_JOIN_GROUP, mreq) self._socketlist.append(self._socket3) ## multicast on ALL COAP nodes self._socket4 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allows address to be reused self._socket4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allows port to be reused try: self._socket4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except: pass self._socket4.bind(('', 5683)) # Allow messages from this socket to loop back for development #self._socket4.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True) self._socket4.setsockopt(41, socket.IPV6_MULTICAST_LOOP, True) # Construct message for joining multicast group mreq = struct.pack("16s15s".encode('utf-8'), socket.inet_pton(socket.AF_INET6, defines.ALL_COAP_NODES_IPV6_LINK), (chr(0) * 16).encode('utf-8')) #self._socket4.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._socket4.setsockopt(41, socket.IPV6_JOIN_GROUP, mreq) self._socketlist.append(self._socket4) self._socket5 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allows address to be reused self._socket5.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allows port to be reused try: self._socket5.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except: pass self._socket5.bind(('', 5683)) # Allow messages from this socket to loop back for development #self._socket4.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_LOOP, True) self._socket5.setsockopt(41, socket.IPV6_MULTICAST_LOOP, True) # Construct message for joining multicast group mreq = struct.pack("16s15s".encode('utf-8'), socket.inet_pton(socket.AF_INET6, defines.ALL_COAP_NODES_IPV6_SITE), (chr(0) * 16).encode('utf-8')) #self._socket4.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._socket5.setsockopt(41, socket.IPV6_JOIN_GROUP, mreq) self._socketlist.append(self._socket5) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) self._socketlist.append(self._socket)
def __init__(self, server_address, xml_file, multicast=False, starting_mid=None, cache=False): self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._forwardLayer = ForwardLayer(self) self.resourceLayer = ResourceLayer(self) self.cache_enable = cache if self.cache_enable: self._cacheLayer = CacheLayer(defines.REVERSE_PROXY) else: self._cacheLayer = None # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=True) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast self.file_xml = xml_file self._mapping = {} addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if self.multicast: # pragma: no cover # Create a socket self._socket = socket.socket(addrinfo[1], socket.SOCK_DGRAM) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Bind it to the port self._socket.bind(('', self.server_address[1])) group_bin = socket.inet_pton(addrinfo[1], addrinfo[4][0]) # Join group if addrinfo[0] == socket.AF_INET: # IPv4 mreq = group_bin + struct.pack('=I', socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) else: mreq = group_bin + struct.pack('@I', 0) self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) self.parse_config()
class CoAP(object): def __init__(self, server_address, xml_file, multicast=False, starting_mid=None, cache=False): self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._forwardLayer = ForwardLayer(self) self.resourceLayer = ResourceLayer(self) self.cache_enable = cache if self.cache_enable: self._cacheLayer = CacheLayer(defines.REVERSE_PROXY) else: self._cacheLayer = None # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=True) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast self.file_xml = xml_file self._mapping = {} addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if self.multicast: # pragma: no cover # Create a socket self._socket = socket.socket(addrinfo[1], socket.SOCK_DGRAM) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Bind it to the port self._socket.bind(('', self.server_address[1])) group_bin = socket.inet_pton(addrinfo[1], addrinfo[4][0]) # Join group if addrinfo[0] == socket.AF_INET: # IPv4 mreq = group_bin + struct.pack('=I', socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) else: mreq = group_bin + struct.pack('@I', 0) self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) self.parse_config() def parse_config(self): tree = ElementTree.parse(self.file_xml) root = tree.getroot() for server in root.findall('server'): destination = server.text name = server.get("name") self.discover_remote(destination, name) def discover_remote(self, destination, name): assert (isinstance(destination, str)) if destination.startswith("["): split = destination.split("]", 1) host = split[0][1:] port = int(split[1][1:]) else: split = destination.split(":", 1) host = split[0] port = int(split[1]) server = (host, port) client = HelperClient(server) response = client.discover() client.stop() self.discover_remote_results(response, name) def discover_remote_results(self, response, name): host, port = response.source if response.code == defines.Codes.CONTENT.number: resource = Resource('server', self, visible=True, observable=False, allow_children=True) self.add_resource(name, resource) self._mapping[name] = (host, port) self.parse_core_link_format(response.payload, name, (host, port)) else: logger.error("Server: " + response.source + " isn't valid.") def parse_core_link_format(self, link_format, base_path, remote_server): while len(link_format) > 0: pattern = "<([^>]*)>;" result = re.match(pattern, link_format) path = result.group(1) path = path.split("/") path = path[1:][0] link_format = link_format[result.end(1) + 2:] pattern = "([^<,])*" result = re.match(pattern, link_format) attributes = result.group(0) dict_att = {} if len(attributes) > 0: attributes = attributes.split(";") for att in attributes: a = att.split("=") # TODO check correctness dict_att[a[0]] = a[1] link_format = link_format[result.end(0) + 1:] # TODO handle observing resource = RemoteResource('server', remote_server, path, coap_server=self, visible=True, observable=False, allow_children=True) resource.attributes = dict_att self.add_resource(base_path + "/" + path, resource) logger.info(self.root.dump()) def purge(self): while not self.stopped.isSet(): self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME) self._messageLayer.purge() def listen(self, timeout=10): """ Listen for incoming messages. Timeout is used to check if the server must be switched off. :param timeout: Socket Timeout in seconds """ self._socket.settimeout(float(timeout)) while not self.stopped.isSet(): try: data, client_address = self._socket.recvfrom(4096) except socket.timeout: continue try: self.receive_datagram((data, client_address)) except RuntimeError: print "Exception with Executor" self._socket.close() def close(self): """ Stop the server. """ logger.info("Stop server") self.stopped.set() for event in self.to_be_stopped: event.set() self._socket.close() def receive_datagram(self, args): """ Receive datagram from the udp socket. :rtype : Message """ data, client_address = args serializer = Serializer() message = serializer.deserialize(data, client_address) if isinstance(message, int): logger.error("receive_datagram - BAD REQUEST") rst = Message() rst.destination = client_address rst.type = defines.Types["RST"] rst.code = message self.send_datagram(rst) return logger.debug("receive_datagram - " + str(message)) if isinstance(message, Request): transaction = self._messageLayer.receive_request(message) if transaction.request.duplicated and transaction.completed: logger.debug("message duplicated,transaction completed") transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) transaction = self._messageLayer.send_response(transaction) self.send_datagram(transaction.response) return elif transaction.request.duplicated and not transaction.completed: logger.debug("message duplicated,transaction NOT completed") self._send_ack(transaction) return transaction.separate_timer = self._start_separate_timer(transaction) transaction = self._blockLayer.receive_request(transaction) if transaction.block_transfer: self._stop_separate_timer(transaction.separate_timer) transaction = self._messageLayer.send_response(transaction) self.send_datagram(transaction.response) return transaction = self._observeLayer.receive_request(transaction) """ call to the cache layer to check if there's a cached response for the request if not, call the forward layer """ if self._cacheLayer is not None: transaction = self._cacheLayer.receive_request(transaction) if transaction.cacheHit is False: print transaction.request transaction = self._forwardLayer.receive_request_reverse(transaction) print transaction.response transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) transaction = self._cacheLayer.send_response(transaction) else: transaction = self._forwardLayer.receive_request_reverse(transaction) transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) self._stop_separate_timer(transaction.separate_timer) transaction = self._messageLayer.send_response(transaction) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retrasmission(transaction, transaction.response) self.send_datagram(transaction.response) elif isinstance(message, Message): transaction = self._messageLayer.receive_empty(message) if transaction is not None: transaction = self._blockLayer.receive_empty(message, transaction) self._observeLayer.receive_empty(message, transaction) else: # pragma: no cover logger.error("Received response from %s", message.source) def send_datagram(self, message): """ :type message: Message :param message: """ if not self.stopped.isSet(): host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) self._socket.sendto(message, (host, port)) def add_resource(self, path, resource): """ Helper function to add resources to the resource directory during server initialization. :type resource: Resource :param resource: """ assert isinstance(resource, Resource) path = path.strip("/") paths = path.split("/") actual_path = "" i = 0 for p in paths: i += 1 actual_path += "/" + p try: res = self.root[actual_path] except KeyError: res = None if res is None: if len(paths) != i: return False resource.path = actual_path self.root[actual_path] = resource return True def _start_retrasmission(self, transaction, message): """ :type transaction: Transaction :param transaction: :type message: Message :param message: :rtype : Future """ if message.type == defines.Types['CON']: future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR)) transaction.retransmit_thread = threading.Thread(target=self._retransmit, args=(transaction, message, future_time, 0)) transaction.retransmit_stop = threading.Event() self.to_be_stopped.append(transaction.retransmit_stop) transaction.retransmit_thread.start() def _retransmit(self, transaction, message, future_time, retransmit_count): while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \ and not self.stopped.isSet(): transaction.retransmit_stop.wait(timeout=future_time) if not message.acknowledged and not message.rejected and not self.stopped.isSet(): retransmit_count += 1 future_time *= 2 self.send_datagram(message) if message.acknowledged or message.rejected: message.timeouted = False else: logger.warning("Give up on message {message}".format(message=message.line_print)) message.timeouted = True if message.observe is not None: self._observeLayer.remove_subscriber(message) try: self.to_be_stopped.remove(transaction.retransmit_stop) except ValueError: pass transaction.retransmit_stop = None transaction.retransmit_thread = None def _start_separate_timer(self, transaction): """ :type transaction: Transaction :param transaction: :type message: Message :param message: :rtype : Future """ t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack, (transaction,)) t.start() return t @staticmethod def _stop_separate_timer(timer): """ :type future: Future :param future: """ timer.cancel() def _send_ack(self, transaction): # Handle separate """ Sends an ACK message for the request. :param request: [request, sleep_time] or request """ ack = Message() ack.type = defines.Types['ACK'] if not transaction.request.acknowledged: ack = self._messageLayer.send_empty(transaction, transaction.request, ack) self.send_datagram(ack)
def __init__(self, server_address, xml_file, multicast=False, starting_mid=None): self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() host, port = server_address ret = socket.getaddrinfo(host, port) family, socktype, proto, canonname, sockaddr = ret[0] self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._forwardLayer = ForwardLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=True) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast self.file_xml = xml_file self._mapping = {} # IPv4 or IPv6 if len(sockaddr) == 4: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if self.multicast: # Set some options to make it multicast-friendly try: self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except AttributeError: pass # Some systems don't support SO_REUSEPORT self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 20) self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) # Bind to the port self._socket.bind(self.server_address) # Set some more multicast options interface = socket.gethostbyname(socket.gethostname()) self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(interface)) self._socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(self.server_address) + socket.inet_aton(interface)) else: self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) self.parse_config()
class CoAP(object): def __init__(self, server_address, xml_file, multicast=False, starting_mid=None): self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() host, port = server_address ret = socket.getaddrinfo(host, port) family, socktype, proto, canonname, sockaddr = ret[0] self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._forwardLayer = ForwardLayer(self) self.resourceLayer = ResourceLayer(self) # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=True) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast self.file_xml = xml_file self._mapping = {} # IPv4 or IPv6 if len(sockaddr) == 4: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) if self.multicast: # Set some options to make it multicast-friendly try: self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except AttributeError: pass # Some systems don't support SO_REUSEPORT self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 20) self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) # Bind to the port self._socket.bind(self.server_address) # Set some more multicast options interface = socket.gethostbyname(socket.gethostname()) self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(interface)) self._socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(self.server_address) + socket.inet_aton(interface)) else: self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) self.parse_config() def parse_config(self): tree = ElementTree.parse(self.file_xml) root = tree.getroot() for server in root.findall('server'): destination = server.text name = server.get("name") self.discover_remote(destination, name) def discover_remote(self, destination, name): assert (isinstance(destination, str)) split = destination.split(":", 1) host = split[0] port = int(split[1]) server = (host, port) client = HelperClient(server) response = client.discover() client.stop() self.discover_remote_results(response, name) def discover_remote_results(self, response, name): host, port = response.source if response.code == defines.Codes.CONTENT.number: resource = Resource('server', self, visible=True, observable=False, allow_children=True) self.add_resource(name, resource) self._mapping[name] = (host, port) self.parse_core_link_format(response.payload, name, (host, port)) else: logger.error("Server: " + response.source + " isn't valid.") def parse_core_link_format(self, link_format, base_path, remote_server): while len(link_format) > 0: pattern = "<([^>]*)>;" result = re.match(pattern, link_format) path = result.group(1) path = path.split("/") path = path[1:][0] link_format = link_format[result.end(1) + 2:] pattern = "([^<,])*" result = re.match(pattern, link_format) attributes = result.group(0) dict_att = {} if len(attributes) > 0: attributes = attributes.split(";") for att in attributes: a = att.split("=") # TODO check correctness dict_att[a[0]] = a[1] link_format = link_format[result.end(0) + 1:] # TODO handle observing resource = RemoteResource('server', remote_server, path, coap_server=self, visible=True, observable=False, allow_children=True) resource.attributes = dict_att self.add_resource(base_path + "/" + path, resource) logger.info(self.root.dump()) def purge(self): while not self.stopped.isSet(): self.stopped.wait(timeout=defines.EXCHANGE_LIFETIME) self._messageLayer.purge() def listen(self, timeout=10): """ Listen for incoming messages. Timeout is used to check if the server must be switched off. :param timeout: Socket Timeout in seconds """ self._socket.settimeout(float(timeout)) while not self.stopped.isSet(): try: data, client_address = self._socket.recvfrom(4096) except socket.timeout: continue try: self.receive_datagram((data, client_address)) except RuntimeError: print "Exception with Executor" self._socket.close() def close(self): """ Stop the server. """ logger.info("Stop server") self.stopped.set() for event in self.to_be_stopped: event.set() self._socket.close() def receive_datagram(self, args): """ Receive datagram from the udp socket. :rtype : Message """ data, client_address = args serializer = Serializer() message = serializer.deserialize(data, client_address) logger.debug("receive_datagram - " + str(message)) if isinstance(message, Request): transaction = self._messageLayer.receive_request(message) if transaction.request.duplicated and transaction.completed: logger.debug("message duplicated,transaction completed") transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) transaction = self._messageLayer.send_response(transaction) self.send_datagram(transaction.response) return elif transaction.request.duplicated and not transaction.completed: logger.debug("message duplicated,transaction NOT completed") self._send_ack(transaction) return transaction.separate_timer = self._start_separate_timer(transaction) transaction = self._blockLayer.receive_request(transaction) if transaction.block_transfer: self._stop_separate_timer(transaction.separate_timer) transaction = self._messageLayer.send_response(transaction) self.send_datagram(transaction.response) return transaction = self._observeLayer.receive_request(transaction) transaction = self._forwardLayer.receive_request_reverse(transaction) if transaction.resource is not None and transaction.resource.changed: self.notify(transaction.resource) transaction.resource.changed = False elif transaction.resource is not None and transaction.resource.deleted: self.notify(transaction.resource) transaction.resource.deleted = False transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) self._stop_separate_timer(transaction.separate_timer) transaction = self._messageLayer.send_response(transaction) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retrasmission(transaction, transaction.response) self.send_datagram(transaction.response) elif isinstance(message, Message): transaction = self._messageLayer.receive_empty(message) if transaction is not None: transaction = self._blockLayer.receive_empty(message, transaction) transaction = self._observeLayer.receive_empty(message, transaction) else: # is Response logger.error("Received response from %s", message.source) def send_datagram(self, message): """ :type message: Message :param message: """ if not self.stopped.isSet(): host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) self._socket.sendto(message, (host, port)) def add_resource(self, path, resource): """ Helper function to add resources to the resource directory during server initialization. :type resource: Resource :param resource: """ assert isinstance(resource, Resource) path = path.strip("/") paths = path.split("/") actual_path = "" i = 0 for p in paths: i += 1 actual_path += "/" + p try: res = self.root[actual_path] except KeyError: res = None if res is None: if len(paths) != i: return False resource.path = actual_path self.root[actual_path] = resource return True def _start_retrasmission(self, transaction, message): """ :type transaction: Transaction :param transaction: :type message: Message :param message: :rtype : Future """ if message.type == defines.Types['CON']: future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR)) transaction.retransmit_thread = threading.Thread(target=self._retransmit, args=(transaction, message, future_time, 0)) transaction.retransmit_stop = threading.Event() self.to_be_stopped.append(transaction.retransmit_stop) transaction.retransmit_thread.start() def _retransmit(self, transaction, message, future_time, retransmit_count): while retransmit_count < defines.MAX_RETRANSMIT and (not message.acknowledged and not message.rejected) \ and not self.stopped.isSet(): transaction.retransmit_stop.wait(timeout=future_time) if not message.acknowledged and not message.rejected and not self.stopped.isSet(): retransmit_count += 1 future_time *= 2 self.send_datagram(message) if message.acknowledged or message.rejected: message.timeouted = False else: logger.warning("Give up on message {message}".format(message=message.line_print)) message.timeouted = True if message.observe is not None: self._observeLayer.remove_subscriber(message) try: self.to_be_stopped.remove(transaction.retransmit_stop) except ValueError: pass transaction.retransmit_stop = None transaction.retransmit_thread = None def _start_separate_timer(self, transaction): """ :type transaction: Transaction :param transaction: :type message: Message :param message: :rtype : Future """ t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack, (transaction,)) t.start() return t @staticmethod def _stop_separate_timer(timer): """ :type future: Future :param future: """ if not timer.finished: timer.cancel() def _send_ack(self, transaction): # Handle separate """ Sends an ACK message for the request. :param request: [request, sleep_time] or request """ ack = Message() ack.type = defines.Types['ACK'] if not transaction.request.acknowledged: ack = self._messageLayer.send_empty(transaction, transaction.request, ack) self.send_datagram(ack) def notify(self, resource): observers = self._observeLayer.notify(resource) logger.debug("Notify") for transaction in observers: transaction.response = None transaction = self._requestLayer.receive_request(transaction) transaction = self._observeLayer.send_response(transaction) transaction = self._blockLayer.send_response(transaction) transaction = self._messageLayer.send_response(transaction) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retrasmission(transaction, transaction.response) self.send_datagram(transaction.response)
class ReverseProxyCoAP(ProxyCoAP): def __init__(self, file_xml): ProxyCoAP.__init__(self) self._mapping = {} root = Resource('root', visible=False, observable=False, allow_children=True) root.path = '/' self.root = Tree(root) self.file_xml = file_xml def parse_config(self): tree = ElementTree.parse(self.file_xml) root = tree.getroot() for server in root.findall('server'): destination = server.text self.discover_remote(destination) @staticmethod def parse_core_link_format(link_format, root): while len(link_format) > 0: pattern = "<([^>]*)>;" result = re.match(pattern, link_format) path = result.group(1) path = path.split("/") path = path[1:] link_format = link_format[result.end(1) + 2:] pattern = "([^<,])*" result = re.match(pattern, link_format) attributes = result.group(0) dict_att = {} if len(attributes) > 0: attributes = attributes.split(";") for att in attributes: a = att.split("=") # TODO check correctness dict_att[a[0]] = a[1] link_format = link_format[result.end(0) + 1:] while True: last, p = root.find_complete_last(path) if p is not None: resource = Resource("/".join(path)) resource.path = p if p == "".join(path): resource.attributes = dict_att last.add_child(resource) else: break log.msg(root.dump()) return root def discover_remote(self, destination): request = Request() assert (isinstance(destination, str)) split = destination.split(":", 1) host = split[0] port = int(split[1]) server = (host, port) request.destination = (host, port) request.type = defines.inv_types["CON"] request._mid = (self._currentMID + 1) % (1 << 16) request.code = defines.inv_codes["GET"] uri = "/" + defines.DISCOVERY_URL request.proxy_uri = uri client = HelperClient(server, True) token = self.generate_token() function = client.protocol.get args = (uri,) kwargs = {"Token": str(token)} callback = self.discover_remote_results err_callback = self.discover_remote_error operations = [(function, args, kwargs, (callback, err_callback))] key = hash(str(host) + str(port) + str(token)) self._forward[key] = request key = hash(str(host) + str(port) + str((client.starting_mid + 1) % (1 << 16))) self._forward_mid[key] = request client.start(operations) def discover_remote_results(self, response): host, port = response.source key = hash(str(host) + str(port) + str(response.token)) request = self._forward.get(key) if request is not None: del self._forward[key] host, port = response.source key = hash(str(host) + str(port) + str(response.mid)) try: del self._forward_mid[key] except KeyError: log.err("MID has not been deleted") if response.code == defines.responses["CONTENT"]: resource = Resource('server', visible=True, observable=False, allow_children=True) resource.path = str(host) + ":" + str(port) resource = self.root.add_child(resource) self._mapping[str(host) + str(port)] = self.parse_core_link_format(response.payload, resource) else: log.err("Server: " + response.source + " isn't valid.") def discover_remote_error(self, mid, host, port): """ Handler of errors. Send an error message to the client. :param mid: the mid of the response that generates the error. :param host: the host of the server. :param port: the port of the server. """ key = hash(str(host) + str(port) + str(mid)) request = self._forward_mid.get(key) if request is not None: del self._forward[key] key = hash(str(host) + str(port) + str(mid)) try: del self._forward_mid[key] except KeyError: log.err("MID has not been deleted") log.err("Server: " + str(host) + ":" + str(port) + " isn't valid.") def datagramReceived(self, data, (host, port)): """ Handler for received dUDP datagram. :param data: the UDP datagram :param host: source host :param port: source port """ log.msg("Datagram received from " + str(host) + ":" + str(port)) serializer = Serializer() message = serializer.deserialize(data, host, port) print "Message received from " + host + ":" + str(port) print "----------------------------------------" print message print "----------------------------------------" if isinstance(message, Request): log.msg("Received request") message = self.map(message) if message is not None: ret = self._request_layer.handle_request(message) if isinstance(ret, Request): self.forward_request(ret) else: return elif isinstance(message, Response): log.err("Received response") rst = Message.new_rst(message) rst = self._message_layer.matcher_response(rst) log.msg("Send RST") self.send(rst, host, port) elif isinstance(message, tuple): message, error = message response = Response() response.destination = (host, port) response.code = defines.responses[error] response = self.reliability_response(message, response) response = self._message_layer.matcher_response(response) log.msg("Send Error") self.send(response, host, port) elif message is not None: # ACK or RST log.msg("Received ACK or RST") self._message_layer.handle_message(message)
def __init__(self, server_address, xml_file, multicast=False, starting_mid=None, cache=False, sock=None): """ Initialize the Reverse Proxy. :param server_address: Server address for incoming connections :param xml_file: the xml file that describe remote servers :param multicast: if the ip is a multicast address :param starting_mid: used for testing purposes :param cache: if a cache must be used :param sock: if a socket has been created externally, it can be used directly """ self.stopped = threading.Event() self.stopped.clear() self.to_be_stopped = [] self.purge = threading.Thread(target=self.purge) self.purge.start() self._messageLayer = MessageLayer(starting_mid) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._forwardLayer = ForwardLayer(self) self.resourceLayer = ResourceLayer(self) self.cache_enable = cache if self.cache_enable: self._cacheLayer = CacheLayer(defines.REVERSE_PROXY) else: self._cacheLayer = None # Resource directory root = Resource('root', self, visible=False, observable=False, allow_children=True) root.path = '/' self.root = Tree() self.root["/"] = root self._serializer = None self.server_address = server_address self.multicast = multicast self.file_xml = xml_file self._mapping = {} addrinfo = socket.getaddrinfo(self.server_address[0], None)[0] if sock is not None: # Use given socket, could be a DTLS socket self._socket = sock elif self.multicast: # pragma: no cover # Create a socket # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255) # self._socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) # Join group if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind((defines.ALL_COAP_NODES, self.server_address[1])) mreq = struct.pack("4sl", socket.inet_aton(defines.ALL_COAP_NODES), socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) self._unicast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._unicast_socket.bind(self.server_address) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # Allow multiple copies of this program on one machine # (not strictly needed) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind((defines.ALL_COAP_NODES_IPV6, self.server_address[1])) addrinfo_multicast = socket.getaddrinfo(defines.ALL_COAP_NODES_IPV6, 5683)[0] group_bin = socket.inet_pton(socket.AF_INET6, addrinfo_multicast[4][0]) mreq = group_bin + struct.pack('@I', 0) self._socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) self._unicast_socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._unicast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._unicast_socket.bind(self.server_address) else: if addrinfo[0] == socket.AF_INET: # IPv4 self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket.bind(self.server_address) self.parse_config()
class CoAP(DatagramProtocol): def __init__(self, server, forward): self._forward = forward self.received = {} self.sent = {} self.sent_token = {} self.received_token = {} self.call_id = {} self.relation = {} self._currentMID = 1 import socket try: socket.inet_aton(server[0]) self.server = server # legal except socket.error: # Not legal data = socket.gethostbyname(server[0]) self.server = (data, server[1]) # defer = reactor.resolve('coap.me') # defer.addCallback(self.start) # self.server = (None, 5683) root = Resource('root', visible=False, observable=False, allow_children=True) root.path = '/' self.root = Tree(root) self.operations = [] self.l = None self.transport = None @property def current_mid(self): return self._currentMID @current_mid.setter def current_mid(self, c): self._currentMID = c def set_operations(self, operations): for op in operations: function, args, kwargs, client_callback = op self.operations.append((function, args, kwargs, client_callback)) def startProtocol(self): if self.server is None: log.err("Server address for the client is not initialized") exit() host, port = self.server if host is not None: self.start(host) self.l = task.LoopingCall(self.purge_mids) self.l.start(defines.EXCHANGE_LIFETIME) def stopProtocol(self): self.l.stop() def purge_mids(self): log.msg("Purge mids") now = time.time() sent_key_to_delete = [] for key in self.sent.keys(): message, timestamp, callback, client_callback = self.sent.get(key) if timestamp + defines.EXCHANGE_LIFETIME <= now: sent_key_to_delete.append(key) for key in sent_key_to_delete: message, timestamp, callback, client_callback = self.sent.get(key) key_token = hash(str(self.server[0]) + str(self.server[1]) + str(message.token)) try: del self.sent[key] except KeyError: pass try: del self.received[key] except KeyError: pass try: del self.sent_token[key_token] except KeyError: pass try: del self.received_token[key_token] except KeyError: pass def start(self, host): # self.transport.connect(host, self.server[1]) function, args, kwargs, client_callback = self.get_operation() function(client_callback, *args, **kwargs) def start_test(self, transport): self.transport = transport function, args, kwargs, client_callback = self.get_operation() function(client_callback, *args, **kwargs) def get_operation(self): try: to_exec = self.operations.pop(0) args = [] kwargs = {} if len(to_exec) == 4: function, args, kwargs, client_callback = to_exec elif len(to_exec) == 3: function, args, client_callback = to_exec elif len(to_exec) == 2: function, client_callback = to_exec[0] else: return None, None, None, None return function, args, kwargs, client_callback except IndexError: return None, None, None, None def send(self, message): serializer = Serializer() message.destination = self.server host, port = message.destination print "Message sent to " + host + ":" + str(port) print "----------------------------------------" print message print "----------------------------------------" datagram = serializer.serialize(message) log.msg("Send datagram") self.transport.write(datagram, self.server) def send_callback(self, req, callback, client_callback): self._currentMID += 1 req.mid = self._currentMID key = hash(str(self.server[0]) + str(self.server[1]) + str(req.mid)) key_token = hash(str(self.server[0]) + str(self.server[1]) + str(req.token)) self.sent[key] = (req, time.time(), callback, client_callback) self.sent_token[key_token] = (req, time.time(), callback, client_callback) if isinstance(client_callback, tuple) and len(client_callback) > 1: client_callback, err_callback = client_callback else: err_callback = None self.schedule_retrasmission(req, err_callback) self.send(req) def datagramReceived(self, datagram, host): serializer = Serializer() host, port = host message = serializer.deserialize(datagram, host, port) print "Message received from " + host + ":" + str(port) print "----------------------------------------" print message print "----------------------------------------" if isinstance(message, Response): self.handle_response(message) elif isinstance(message, Request): log.err("Received request") else: self.handle_message(message) key = hash(str(host) + str(port) + str(message.mid)) if message.type == defines.inv_types["ACK"] and message.code == defines.inv_codes["EMPTY"] \ and key in self.sent.keys(): # Separate Response print "Separate Response" else: function, args, kwargs, client_callback = self.get_operation() key = hash(str(host) + str(port) + str(message.token)) if function is None and len(self.relation) == 0: if not self._forward: reactor.stop() elif key in self.relation: response, timestamp, client_callback = self.relation.get(key) self.handle_notification(message, client_callback) else: function(client_callback, *args, **kwargs) def handle_message(self, message): key = hash(str(self.server[0]) + str(self.server[1]) + str(message.mid)) if message.type == defines.inv_types["ACK"] and message.code == defines.inv_codes["EMPTY"] \ and key in self.sent.keys(): return None if key in self.sent.keys(): self.received[key] = message if message.type == defines.inv_types["RST"]: print message else: req, timestamp, callback, client_callback = self.sent[key] callback(message.mid, client_callback) def handle_response(self, response): if response.type == defines.inv_types["CON"]: ack = Message.new_ack(response) self.send(ack) key_token = hash(str(self.server[0]) + str(self.server[1]) + str(response.token)) if key_token in self.sent_token.keys(): self.received_token[key_token] = response req, timestamp, callback, client_callback = self.sent_token[key_token] key = hash(str(self.server[0]) + str(self.server[1]) + str(req.mid)) self.received[key] = response callback(req.mid, client_callback) def discover(self, client_callback, *args, **kwargs): req = Request() if "Token" in kwargs.keys(): req.token = kwargs.get("Token") req.code = defines.inv_codes['GET'] req.uri_path = ".well-known/core" req.type = defines.inv_types["CON"] self.send_callback(req, self.discover_results, client_callback) def discover_results(self, mid, client_callback): key = hash(str(self.server[0]) + str(self.server[1]) + str(mid)) response = self.received.get(key) if key in self.call_id.keys(): handler, retransmit_count = self.call_id.get(key) if handler is not None: try: log.msg("Cancel retrasmission") handler.cancel() except AlreadyCancelled: pass err_callback = None if isinstance(client_callback, tuple) and len(client_callback) > 1: client_callback, err_callback = client_callback elif isinstance(client_callback, tuple): client_callback = client_callback[0] if response is not None: # self.parse_core_link_format(response.payload) client_callback(response) elif err_callback is not None: err_callback(mid, self.server[0], self.server[1]) def parse_core_link_format(self, link_format): while len(link_format) > 0: pattern = "<([^>]*)>;" result = re.match(pattern, link_format) path = result.group(1) path = path.split("/") path = path[1:] link_format = link_format[result.end(1) + 2:] pattern = "([^<,])*" result = re.match(pattern, link_format) attributes = result.group(0) dict_att = {} if len(attributes) > 0: attributes = attributes.split(";") for att in attributes: a = att.split("=") # TODO check correctness dict_att[a[0]] = a[1] link_format = link_format[result.end(0) + 1:] while True: last, p = self.root.find_complete_last(path) if p is not None: resource = Resource("/".join(path)) resource.path = p if p == "".join(path): resource.attributes = dict_att last.add_child(resource) else: break log.msg(self.root.dump()) def get(self, client_callback, *args, **kwargs): path = args[0] req = Request() if "Token" in kwargs.keys(): req.token = kwargs.get("Token") del kwargs["Token"] for key in kwargs: o = Option() o.number = defines.inv_options[key] o.value = kwargs[key] req.add_option(o) req.code = defines.inv_codes['GET'] req.uri_path = path req.type = defines.inv_types["CON"] self.send_callback(req, self.get_results, client_callback) def get_results(self, mid, client_callback): key = hash(str(self.server[0]) + str(self.server[1]) + str(mid)) response = self.received.get(key) if key in self.call_id.keys(): handler, retransmit_count = self.call_id.get(key) if handler is not None: try: log.msg("Cancel retrasmission") handler.cancel() except AlreadyCancelled: pass err_callback = None if isinstance(client_callback, tuple) and len(client_callback) > 1: client_callback, err_callback = client_callback elif isinstance(client_callback, tuple): client_callback = client_callback[0] if response is not None: # self.parse_core_link_format(response.payload) client_callback(response) elif err_callback is not None: err_callback(mid, self.server[0], self.server[1]) def observe(self, client_callback, *args, **kwargs): path = args[0] req = Request() if "Token" in kwargs.keys(): req.token = kwargs.get("Token") del kwargs["Token"] for key in kwargs: o = Option() o.number = defines.inv_options[key] o.value = kwargs[key] req.add_option(o) req.code = defines.inv_codes['GET'] req.uri_path = path req.observe = 0 req.type = defines.inv_types["CON"] self.send_callback(req, self.observe_results, client_callback) def observe_results(self, mid, client_callback): key = hash(str(self.server[0]) + str(self.server[1]) + str(mid)) response = self.received.get(key) if key in self.call_id.keys(): handler, retransmit_count = self.call_id.get(key) if handler is not None: try: log.msg("Cancel retrasmission") handler.cancel() except AlreadyCancelled: pass err_callback = None if isinstance(client_callback, tuple) and len(client_callback) > 1: client_callback, err_callback = client_callback elif isinstance(client_callback, tuple): client_callback = client_callback[0] if response is not None: if response.observe != 0: # TODO add observing results host, port = response.source key = hash(str(host) + str(port) + str(response.token)) self.relation[key] = (response, time.time(), client_callback) client_callback(response) elif err_callback is not None: err_callback(mid, self.server[0], self.server[1]) def handle_notification(self, response, client_callback): host, port = response.source key = hash(str(host) + str(port) + str(response.token)) self.relation[key] = (response, time.time(), client_callback) if response.type == defines.inv_types["CON"]: ack = Message.new_ack(response) self.send(ack) def cancel_observing(self, response, send_rst): host, port = response.source key = hash(str(host) + str(port) + str(response.token)) del self.relation[key] if send_rst: rst = Message.new_rst(response) self.send(rst) def post(self, client_callback, *args, **kwargs): path, payload = args req = Request() if "Token" in kwargs.keys(): req.token = kwargs.get("Token") del kwargs["Token"] for key in kwargs: o = Option() o.number = defines.inv_options[key] o.value = kwargs[key] req.add_option(o) req.code = defines.inv_codes['POST'] req.uri_path = path req.type = defines.inv_types["CON"] req.payload = payload self.send_callback(req, self.post_results, client_callback) def post_results(self, mid, client_callback): key = hash(str(self.server[0]) + str(self.server[1]) + str(mid)) response = self.received.get(key) if key in self.call_id.keys(): handler, retransmit_count = self.call_id.get(key) if handler is not None: try: log.msg("Cancel retrasmission") handler.cancel() except AlreadyCancelled: pass err_callback = None if isinstance(client_callback, tuple) and len(client_callback) > 1: client_callback, err_callback = client_callback elif isinstance(client_callback, tuple): client_callback = client_callback[0] if response is not None: # self.parse_core_link_format(response.payload) client_callback(response) elif err_callback is not None: err_callback(mid, self.server[0], self.server[1]) def put(self, client_callback, *args, **kwargs): path, payload = args req = Request() if "Token" in kwargs.keys(): req.token = kwargs.get("Token") del kwargs["Token"] for key in kwargs: o = Option() o.number = defines.inv_options[key] o.value = kwargs[key] req.add_option(o) req.code = defines.inv_codes['PUT'] req.uri_path = path req.type = defines.inv_types["CON"] req.payload = payload self.send_callback(req, self.put_results, client_callback) def put_results(self, mid, client_callback): key = hash(str(self.server[0]) + str(self.server[1]) + str(mid)) response = self.received.get(key) if key in self.call_id.keys(): handler, retransmit_count = self.call_id.get(key) if handler is not None: try: log.msg("Cancel retrasmission") handler.cancel() except AlreadyCancelled: pass err_callback = None if isinstance(client_callback, tuple) and len(client_callback) > 1: client_callback, err_callback = client_callback elif isinstance(client_callback, tuple): client_callback = client_callback[0] if response is not None: # self.parse_core_link_format(response.payload) client_callback(response) elif err_callback is not None: err_callback(mid, self.server[0], self.server[1]) def delete(self, client_callback, *args, **kwargs): path = args[0] req = Request() if "Token" in kwargs.keys(): req.token = kwargs.get("Token") del kwargs["Token"] for key in kwargs: o = Option() o.number = defines.inv_options[key] o.value = kwargs[key] req.add_option(o) req.code = defines.inv_codes['DELETE'] req.uri_path = path req.type = defines.inv_types["CON"] self.send_callback(req, self.delete_results, client_callback) def delete_results(self, mid, client_callback): key = hash(str(self.server[0]) + str(self.server[1]) + str(mid)) response = self.received.get(key) if key in self.call_id.keys(): handler, retransmit_count = self.call_id.get(key) if handler is not None: try: log.msg("Cancel retrasmission") handler.cancel() except AlreadyCancelled: pass err_callback = None if isinstance(client_callback, tuple) and len(client_callback) > 1: client_callback, err_callback = client_callback elif isinstance(client_callback, tuple): client_callback = client_callback[0] if response is not None: # self.parse_core_link_format(response.payload) client_callback(response) elif err_callback is not None: err_callback(mid, self.server[0], self.server[1]) def schedule_retrasmission(self, request, err_callback): host, port = self.server if request.type == defines.inv_types['CON']: future_time = random.uniform(defines.ACK_TIMEOUT, (defines.ACK_TIMEOUT * defines.ACK_RANDOM_FACTOR)) key = hash(str(host) + str(port) + str(request.mid)) self.call_id[key] = (reactor.callLater(future_time, self.retransmit, (request, host, port, future_time, err_callback)), 0) def retransmit(self, t): log.msg("Retransmit") request, host, port, future_time, err_callback = t key = hash(str(host) + str(port) + str(request.mid)) call_id, retransmit_count = self.call_id[key] if retransmit_count < defines.MAX_RETRANSMIT and (not request.acknowledged and not request.rejected): retransmit_count += 1 req, timestamp, callback, client_callback = self.sent[key] self.sent[key] = (request, time.time(), callback, client_callback) self.send(request) future_time *= 2 self.call_id[key] = (reactor.callLater(future_time, self.retransmit, (request, host, port, future_time, err_callback)), retransmit_count) elif request.acknowledged or request.rejected: request.timeouted = False del self.call_id[key] else: request.timeouted = True log.err("Request timeouted") del self.call_id[key] if err_callback is not None: err_callback(request.mid, host, port)