def __init__(self, server, starting_mid, callback): self._currentMID = starting_mid self._server = server self._callback = callback self.stopped = threading.Event() self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) # try: # # legal # socket.inet_aton(server[0]) # except socket.error: # # Not legal # data = socket.getaddrinfo(server[0], server[1]) # self._server = (data[0], data[1]) host, port = self._server addrinfo = socket.getaddrinfo(host, None)[0] if addrinfo[0] == socket.AF_INET: 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 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._receiver_thread = threading.Thread(target=self.receive_datagram) self._receiver_thread.daemon = True self._receiver_thread.start()
def __init__(self, server_address, 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 # 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)
def __init__(self, server_address, 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 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)
def __init__(self, server, starting_mid, callback): self._currentMID = starting_mid self._server = server self._callback = callback self.stopped = threading.Event() self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) try: # legal socket.inet_aton(server[0]) except socket.error: # Not legal data = socket.getaddrinfo(server[0], server[1]) self._server = (data[0], data[1]) self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._receiver_thread = threading.Thread(target=self.receive_datagram) self._receiver_thread.start()
def __init__(self, server, starting_mid, callback, sock=None, cb_ignore_read_exception=None, cb_ignore_write_exception=None): """ Initialize the client. :param server: Server address for incoming connections :param callback:the callback function to be invoked when a response is received :param starting_mid: used for testing purposes :param sock: if a socket has been created externally, it can be used directly :param cb_ignore_read_exception: Callback function to handle exception raised during the socket read operation :param cb_ignore_write_exception: Callback function to handle exception raised during the socket write operation """ self._currentMID = starting_mid self._server = server self._callback = callback self._cb_ignore_read_exception = cb_ignore_read_exception self._cb_ignore_write_exception = cb_ignore_write_exception self.stopped = threading.Event() self.to_be_stopped = [] self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) addrinfo = socket.getaddrinfo(self._server[0], None)[0] if sock is not None: self._socket = sock elif addrinfo[0] == socket.AF_INET: 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._receiver_thread = None
class CoAP(object): """ Implementation of the Forward Proxy """ 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((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) 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: #Start a new thread not to block other requests args = ((data, client_address), ) t = threading.Thread(target=self.receive_datagram, args=args) t.daemon = True t.start() except RuntimeError: logging.exception("Exception with Executor") logging.debug("closing socket") 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 logging.debug("receiving datagram") try: host, port = client_address except ValueError: host, port, tmp1, tmp2 = client_address client_address = (host, port) 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: logging.debug(transaction.request) transaction = self._forwardLayer.receive_request(transaction) logging.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(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_retransmission(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: # is Response 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 _start_retransmission(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): """ 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() 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._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 # 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: self._socket.bind(("", self.server_address[1])) try: group = socket.inet_aton(self.server_address[0]) except: group = self.server_address[0] mreq = struct.pack("4sL", group, socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) # # 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[0]) # + socket.inet_aton(interface)) else: 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): 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)
class CoAP(object): def __init__(self, server_address, 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.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 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) 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" print "closing socket" 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 print "receiving datagram" 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(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(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: # 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 _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: :rtype : Timer """ t = threading.Timer(defines.ACK_TIMEOUT, self._send_ack, (transaction,)) t.start() return t @staticmethod def _stop_separate_timer(timer): """ :type timer: Timer :param timer: The timer object """ timer.cancel() def _send_ack(self, transaction): # Handle separate """ Sends an ACK message for the request. :param transaction: The transaction to be acknowledged """ 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)
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)
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) # 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)
class CoAP(object): 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=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 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) 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) if len(client_address) > 2: client_address = (client_address[0], client_address[1]) except socket.timeout: continue try: 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 rst.mid = self._messageLayer._current_mid self._messageLayer._current_mid += 1 % 65535 self.send_datagram(rst) continue logger.debug("receive_datagram - " + str(message)) if isinstance(message, Request): #logger.debug("send_datagram - " + str(message)) #print("ddddddddddddddd: " + str(message)) transaction = self._messageLayer.receive_request(message) if transaction.request.duplicated and transaction.completed: logger.debug( "message duplicated, transaction completed") if transaction.response is not None: self.send_datagram(transaction.response) continue elif transaction.request.duplicated and not transaction.completed: logger.debug( "message duplicated, transaction NOT completed") self._send_ack(transaction) continue args = (transaction, ) t = threading.Thread(target=self.receive_request, args=args) t.start() # self.receive_datagram(data, client_address) elif isinstance(message, Response): logger.error("Received response from %s", message.source) else: # is Message transaction = self._messageLayer.receive_empty(message) if transaction is not None: with transaction: self._blockLayer.receive_empty( message, transaction) self._observeLayer.receive_empty( message, transaction) 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_request(self, transaction): """ Receive datagram from the udp socket. :param data: the udp message :param client_address: the ip and port of the client """ with transaction: transaction.separate_timer = self._start_separate_timer( transaction) self._blockLayer.receive_request(transaction) if transaction.block_transfer: self._stop_separate_timer(transaction.separate_timer) self._messageLayer.send_response(transaction) self.send_datagram(transaction.response) return self._observeLayer.receive_request(transaction) self._requestLayer.receive_request(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 self._observeLayer.send_response(transaction) self._blockLayer.send_response(transaction) self._stop_separate_timer(transaction.separate_timer) self._messageLayer.send_response(transaction) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.response) self.send_datagram(transaction.response) 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. :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_retransmission(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'] # TODO handle mutex on transaction if not transaction.request.acknowledged and transaction.request.type == defines.Types[ "CON"]: ack = self._messageLayer.send_empty(transaction, transaction.request, ack) self.send_datagram(ack) def notify(self, resource): """ Notifies the observers of a certain resource. :param resource: the resource """ observers = self._observeLayer.notify(resource) logger.debug("Notify") # print resource.observe_client_list # print "[[[[" for transaction in observers: with transaction: 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) print resource.token_address if ("introspection" not in resource.__dict__) \ or ("introspection" in resource.__dict__ and transaction.request.source in resource.observe_client_list): print "OTL" print "OTL" print "OTL" print "OTL" print "OTL" print "OTL" if "introspection" in resource.__dict__ \ and transaction.request.source in resource.observe_client_list: resource.observe_client_list.remove( transaction.request.source) print "OTT" print "OTT" print "OTT" print "OTT" print transaction.response.active print transaction.response.active == str(False) if transaction.response.active == str(False): self._observeLayer.remove_subscriber( transaction.response) transaction.response.type = defines.Types["NON"] # option = Option() # option.number = defines.OptionRegistry.BEARER.number # option.value = transaction.request.bearer # transaction.response.add_option(option) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.response) self.send_datagram(transaction.response)
class CoAP(object): def __init__(self, server, starting_mid, callback): self._currentMID = starting_mid self._server = server self._callback = callback self.stopped = threading.Event() self.to_be_stopped = [] self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) # try: # # legal # socket.inet_aton(server[0]) # except socket.error: # # Not legal # data = socket.getaddrinfo(server[0], server[1]) # self._server = (data[0], data[1]) host, port = self._server addrinfo = socket.getaddrinfo(host, None)[0] if addrinfo[0] == socket.AF_INET: 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 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._receiver_thread = threading.Thread(target=self.receive_datagram) self._receiver_thread.daemon = True self._receiver_thread.start() @property def current_mid(self): return self._currentMID @current_mid.setter def current_mid(self, c): assert isinstance(c, int) self._currentMID = c def send_message(self, message): if isinstance(message, Request): request = self._requestLayer.send_request(message) request = self._observeLayer.send_request(request) request = self._blockLayer.send_request(request) transaction = self._messageLayer.send_request(request) if transaction.request.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.request) self.send_datagram(transaction.request) elif isinstance(message, Message): message = self._observeLayer.send_empty(message) message = self._messageLayer.send_empty(None, None, message) self.send_datagram(message) def send_datagram(self, message): host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) self._socket.sendto(message, (host, port)) def _start_retransmission(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(): logger.debug("retransmit Request") 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 try: self.to_be_stopped.remove(transaction.retransmit_stop) except ValueError: pass transaction.retransmit_stop = None transaction.retransmit_thread = None def receive_datagram(self): logger.debug("Start receiver Thread") while not self.stopped.isSet(): self._socket.settimeout(1) try: datagram, addr = self._socket.recvfrom(1152) except socket.timeout: # pragma: no cover continue except socket.error: # pragma: no cover return else: # pragma: no cover if len(datagram) == 0: print 'orderly shutdown on server end' return serializer = Serializer() try: host, port = addr except ValueError: host, port, tmp1, tmp2 = addr source = (host, port) message = serializer.deserialize(datagram, source) if isinstance(message, Response): transaction, send_ack = self._messageLayer.receive_response(message) if transaction is None: # pragma: no cover continue if send_ack: self._send_ack(transaction) self._blockLayer.receive_response(transaction) if transaction.block_transfer: transaction = self._messageLayer.send_request(transaction.request) self.send_datagram(transaction.request) continue elif transaction is None: # pragma: no cover self._send_rst(transaction) return self._observeLayer.receive_response(transaction) if transaction.notification: # pragma: no cover ack = Message() ack.type = defines.Types['ACK'] ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) self._callback(transaction.response) else: self._callback(transaction.response) elif isinstance(message, Message): self._messageLayer.receive_empty(message) def _send_ack(self, transaction): # Handle separate """ Sends an ACK message for the response. :param transaction: transaction that holds the response """ ack = Message() ack.type = defines.Types['ACK'] if not transaction.response.acknowledged: ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) def _send_rst(self, transaction): # pragma: no cover # Handle separate """ Sends an RST message for the response. :param transaction: transaction that holds the response """ rst = Message() rst.type = defines.Types['RST'] if not transaction.response.acknowledged: rst = self._messageLayer.send_empty(transaction, transaction.response, rst) self.send_datagram(rst)
class CoAP(object): def __init__(self, server, starting_mid, callback): self._currentMID = starting_mid self._server = server self._callback = callback self.stopped = threading.Event() self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) try: # legal socket.inet_aton(server[0]) except socket.error: # Not legal data = socket.getaddrinfo(server[0], server[1]) self._server = (data[0], data[1]) self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._receiver_thread = threading.Thread(target=self.receive_datagram) self._receiver_thread.start() @property def current_mid(self): return self._currentMID @current_mid.setter def current_mid(self, c): assert isinstance(c, int) self._currentMID = c def send_message(self, message): if isinstance(message, Request): request = self._requestLayer.send_request(message) request = self._observeLayer.send_request(request) request = self._blockLayer.send_request(request) transaction = self._messageLayer.send_request(request) self.send_datagram(transaction.request) elif isinstance(message, Message): message = self._observeLayer.send_empty(message) message = self._blockLayer.send_empty(message) message = self._messageLayer.send_empty(None, None, message) self.send_datagram(message) def send_datagram(self, message): host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) self._socket.sendto(message, (host, port)) def receive_datagram(self): logger.debug("Start receiver Thread") while not self.stopped.isSet(): self._socket.settimeout(1) try: datagram, addr = self._socket.recvfrom(1152) except socket.timeout, e: err = e.args[0] # this next if/else is a bit redundant, but illustrates how the # timeout exception is setup if err == 'timed out': continue else: print e return except socket.error, e: # Something else happened, handle error, exit, etc. print e return else:
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=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 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)
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(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: 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): """ 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: 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): """ 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, 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((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)
class CoAP(object): def __init__(self, server, starting_mid, callback): self._currentMID = starting_mid self._server = server self._callback = callback self.stopped = threading.Event() self.to_be_stopped = [] self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) # try: # # legal # socket.inet_aton(server[0]) # except socket.error: # # Not legal # data = socket.getaddrinfo(server[0], server[1]) # self._server = (data[0], data[1]) host, port = self._server addrinfo = socket.getaddrinfo(host, None)[0] if addrinfo[0] == socket.AF_INET: 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 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._receiver_thread = threading.Thread(target=self.receive_datagram) self._receiver_thread.daemon = True self._receiver_thread.start() @property def current_mid(self): return self._currentMID @current_mid.setter def current_mid(self, c): assert isinstance(c, int) self._currentMID = c def send_message(self, message): if isinstance(message, Request): request = self._requestLayer.send_request(message) request = self._observeLayer.send_request(request) request = self._blockLayer.send_request(request) transaction = self._messageLayer.send_request(request) if transaction.request.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.request) self.send_datagram(transaction.request) elif isinstance(message, Message): message = self._observeLayer.send_empty(message) message = self._messageLayer.send_empty(None, None, message) self.send_datagram(message) def send_datagram(self, message): host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) self._socket.sendto(message, (host, port)) def _start_retransmission(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( ): logger.debug("retransmit Request") 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 try: self.to_be_stopped.remove(transaction.retransmit_stop) except ValueError: pass transaction.retransmit_stop = None transaction.retransmit_thread = None def receive_datagram(self): logger.debug("Start receiver Thread") while not self.stopped.isSet(): self._socket.settimeout(1) try: datagram, addr = self._socket.recvfrom(1152) except socket.timeout: # pragma: no cover continue except socket.error: # pragma: no cover return else: # pragma: no cover if len(datagram) == 0: print 'orderly shutdown on server end' return serializer = Serializer() try: host, port = addr except ValueError: host, port, tmp1, tmp2 = addr source = (host, port) message = serializer.deserialize(datagram, source) if isinstance(message, Response): transaction, send_ack = self._messageLayer.receive_response( message) if transaction is None: # pragma: no cover continue if send_ack: self._send_ack(transaction) self._blockLayer.receive_response(transaction) if transaction.block_transfer: transaction = self._messageLayer.send_request( transaction.request) self.send_datagram(transaction.request) continue elif transaction is None: # pragma: no cover self._send_rst(transaction) return self._observeLayer.receive_response(transaction) if transaction.notification: # pragma: no cover ack = Message() ack.type = defines.Types['ACK'] ack = self._messageLayer.send_empty( transaction, transaction.response, ack) self.send_datagram(ack) self._callback(transaction.response) else: self._callback(transaction.response) elif isinstance(message, Message): self._messageLayer.receive_empty(message) def _send_ack(self, transaction): # Handle separate """ Sends an ACK message for the response. :param transaction: transaction that holds the response """ ack = Message() ack.type = defines.Types['ACK'] if not transaction.response.acknowledged: ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) def _send_rst(self, transaction): # pragma: no cover # Handle separate """ Sends an RST message for the response. :param transaction: transaction that holds the response """ rst = Message() rst.type = defines.Types['RST'] if not transaction.response.acknowledged: rst = self._messageLayer.send_empty(transaction, transaction.response, rst) self.send_datagram(rst)
class CoAP(object): """ Client class to perform requests to remote servers. """ def __init__(self, server, starting_mid, callback, sock=None, cb_ignore_read_exception=None, cb_ignore_write_exception=None): """ Initialize the client. :param server: Server address for incoming connections :param callback:the callback function to be invoked when a response is received :param starting_mid: used for testing purposes :param sock: if a socket has been created externally, it can be used directly :param cb_ignore_read_exception: Callback function to handle exception raised during the socket read operation :param cb_ignore_write_exception: Callback function to handle exception raised during the socket write operation """ self._currentMID = starting_mid self._server = server self._callback = callback self._cb_ignore_read_exception = cb_ignore_read_exception self._cb_ignore_write_exception = cb_ignore_write_exception self.stopped = threading.Event() self.to_be_stopped = [] self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) addrinfo = socket.getaddrinfo(self._server[0], None)[0] if sock is not None: self._socket = sock elif addrinfo[0] == socket.AF_INET: 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._receiver_thread = None def close(self): """ Stop the client. """ self.stopped.set() for event in self.to_be_stopped: event.set() if self._receiver_thread is not None: self._receiver_thread.join() try: # Python does not close the OS FD on socket.close() # Ensure OS socket is closed with shutdown to prevent FD leak self._socket.shutdown(socket.SHUT_RDWR) except socket.error: pass self._socket.close() @property def current_mid(self): """ Return the current MID. :return: the current mid """ return self._currentMID @current_mid.setter def current_mid(self, c): """ Set the current MID. :param c: the mid to set """ assert isinstance(c, int) self._currentMID = c def send_message(self, message): """ Prepare a message to send on the UDP socket. Eventually set retransmissions. :param message: the message to send """ if isinstance(message, Request): request = self._requestLayer.send_request(message) request = self._observeLayer.send_request(request) request = self._blockLayer.send_request(request) transaction = self._messageLayer.send_request(request) self.send_datagram(transaction.request) if transaction.request.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.request) elif isinstance(message, Message): message = self._observeLayer.send_empty(message) message = self._messageLayer.send_empty(None, None, message) self.send_datagram(message) def end_observation(self, token): """ Remove an observation token from our records. :param token: the token for the observation """ dummy = Message() dummy.token = token dummy.destination = self._server self._observeLayer.remove_subscriber(dummy) @staticmethod def _wait_for_retransmit_thread(transaction): """ Only one retransmit thread at a time, wait for other to finish """ if hasattr(transaction, 'retransmit_thread'): while transaction.retransmit_thread is not None: logger.debug("Waiting for retransmit thread to finish ...") time.sleep(0.01) continue def _send_block_request(self, transaction): """ A former request resulted in a block wise transfer. With this method, the block wise transfer will be continued, including triggering of the retry mechanism. :param transaction: The former transaction including the request which should be continued. """ transaction = self._messageLayer.send_request(transaction.request) # ... but don't forget to reset the acknowledge flag transaction.request.acknowledged = False self.send_datagram(transaction.request) if transaction.request.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.request) def send_datagram(self, message): """ Send a message over the UDP socket. :param message: the message to send """ host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() raw_message = serializer.serialize(message) try: self._socket.sendto(raw_message, (host, port)) except Exception as e: if self._cb_ignore_write_exception is not None and callable(self._cb_ignore_write_exception): if not self._cb_ignore_write_exception(e, self): raise if self._receiver_thread is None or not self._receiver_thread.isAlive(): self._receiver_thread = threading.Thread(target=self.receive_datagram) self._receiver_thread.start() def _start_retransmission(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_stop = threading.Event() self.to_be_stopped.append(transaction.retransmit_stop) transaction.retransmit_thread = threading.Thread(target=self._retransmit, name=str('%s-Retry-%d' % (threading.current_thread().name, message.mid)), args=(transaction, message, future_time, 0)) 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: logger.debug("retransmit loop ... enter") while retransmit_count <= defines.MAX_RETRANSMIT \ and (not message.acknowledged and not message.rejected) \ and not transaction.retransmit_stop.isSet(): transaction.retransmit_stop.wait(timeout=future_time) if not message.acknowledged and not message.rejected and not transaction.retransmit_stop.isSet(): retransmit_count += 1 future_time *= 2 if retransmit_count < defines.MAX_RETRANSMIT: logger.debug("retransmit loop ... retransmit Request") 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 # Inform the user, that nothing was received self._callback(message) try: self.to_be_stopped.remove(transaction.retransmit_stop) except ValueError: pass transaction.retransmit_stop = None transaction.retransmit_thread = None logger.debug("retransmit loop ... exit") def receive_datagram(self): """ Receive datagram from the UDP socket and invoke the callback function. """ logger.debug("Start receiver Thread") while not self.stopped.isSet(): self._socket.settimeout(0.1) try: datagram, addr = self._socket.recvfrom(1152) except socket.timeout: # pragma: no cover continue except Exception as e: # pragma: no cover if self._cb_ignore_read_exception is not None and callable(self._cb_ignore_read_exception): if self._cb_ignore_read_exception(e, self): continue return else: # pragma: no cover if len(datagram) == 0: logger.debug("Exiting receiver Thread due to orderly shutdown on server end") return serializer = Serializer() try: host, port = addr except ValueError: host, port, tmp1, tmp2 = addr source = (host, port) message = serializer.deserialize(datagram, source) if isinstance(message, Response): logger.debug("receive_datagram - " + str(message)) transaction, send_ack = self._messageLayer.receive_response(message) if transaction is None: # pragma: no cover continue self._wait_for_retransmit_thread(transaction) if send_ack: self._send_ack(transaction) self._blockLayer.receive_response(transaction) if transaction.block_transfer: self._send_block_request(transaction) continue elif transaction is None: # pragma: no cover self._send_rst(transaction) return self._observeLayer.receive_response(transaction) if transaction.notification: # pragma: no cover ack = Message() ack.type = defines.Types['ACK'] ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) self._callback(transaction.response) else: self._callback(transaction.response) elif isinstance(message, Message): self._messageLayer.receive_empty(message) logger.debug("Exiting receiver Thread due to request") def _send_ack(self, transaction): """ Sends an ACK message for the response. :param transaction: transaction that holds the response """ ack = Message() ack.type = defines.Types['ACK'] if not transaction.response.acknowledged: ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) def _send_rst(self, transaction): # pragma: no cover """ Sends an RST message for the response. :param transaction: transaction that holds the response """ rst = Message() rst.type = defines.Types['RST'] if not transaction.response.acknowledged: rst = self._messageLayer.send_empty(transaction, transaction.response, rst) self.send_datagram(rst)
class CoAP(object): """ Implementation of the CoAP server """ 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) # 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) 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) if len(client_address) > 2: client_address = (client_address[0], client_address[1]) except socket.timeout: continue except Exception as e: if self._cb_ignore_listen_exception is not None and callable(self._cb_ignore_listen_exception): if self._cb_ignore_listen_exception(e, self): continue raise try: 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 rst.mid = self._messageLayer.fetch_mid() self.send_datagram(rst) continue 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") if transaction.response is not None: self.send_datagram(transaction.response) continue elif transaction.request.duplicated and not transaction.completed: logger.debug("message duplicated, transaction NOT completed") self._send_ack(transaction) continue args = (transaction, ) t = threading.Thread(target=self.receive_request, args=args) t.start() # self.receive_datagram(data, client_address) elif isinstance(message, Response): logger.error("Received response from %s", message.source) else: # is Message transaction = self._messageLayer.receive_empty(message) if transaction is not None: with transaction: self._blockLayer.receive_empty(message, transaction) self._observeLayer.receive_empty(message, transaction) except Exception as e: logger.error("Exception received in listen") logger.exception(traceback.format_exc()) continue except RuntimeError: logger.exception("Exception with Executor") try: # Python does not close the OS FD on socket.close() # Ensure OS socket is closed with shutdown to prevent FD leak self._socket.shutdown(socket.SHUT_RDWR) except socket.error: pass 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() def receive_request(self, transaction): """ Handle requests coming from the udp socket. :param transaction: the transaction created to manage the request """ with transaction: transaction.separate_timer = self._start_separate_timer(transaction) self._blockLayer.receive_request(transaction) if transaction.block_transfer: self._stop_separate_timer(transaction.separate_timer) self._messageLayer.send_response(transaction) self.send_datagram(transaction.response) return self._observeLayer.receive_request(transaction) self._requestLayer.receive_request(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 self._observeLayer.send_response(transaction) self._blockLayer.send_response(transaction) self._stop_separate_timer(transaction.separate_timer) self._messageLayer.send_response(transaction) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.response) self.send_datagram(transaction.response) 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() try: message = serializer.serialize(message) except Exception as e: logger.error("Exception received in send_datagram") logger.exception(traceback.format_exc()) if self.multicast: self._unicast_socket.sendto(message, (host, port)) else: 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 remove_resource(self, path): """ Helper function to remove resources. :param path: the path for the unwanted resource :rtype : the removed object """ 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 not None: del(self.root[actual_path]) return res def _start_retransmission(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(): if transaction.retransmit_stop is not None: 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'] # TODO handle mutex on transaction if not transaction.request.acknowledged and transaction.request.type == defines.Types["CON"]: ack = self._messageLayer.send_empty(transaction, transaction.request, ack) self.send_datagram(ack) def notify(self, resource): """ Notifies the observers of a certain resource. :param resource: the resource """ observers = self._observeLayer.notify(resource) logger.debug("Notify") for transaction in observers: with transaction: 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_retransmission(transaction, transaction.response) self.send_datagram(transaction.response)
class CoAP(object): def __init__(self, server, starting_mid, callback): self._currentMID = starting_mid self._server = server self._callback = callback self.stopped = threading.Event() self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) # try: # # legal # socket.inet_aton(server[0]) # except socket.error: # # Not legal # data = socket.getaddrinfo(server[0], server[1]) # self._server = (data[0], data[1]) host, port = self._server addrinfo = socket.getaddrinfo(host, None)[0] if addrinfo[0] == socket.AF_INET: 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 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._receiver_thread = threading.Thread(target=self.receive_datagram) self._receiver_thread.daemon = True self._receiver_thread.start() @property def current_mid(self): return self._currentMID @current_mid.setter def current_mid(self, c): assert isinstance(c, int) self._currentMID = c def send_message(self, message): if isinstance(message, Request): request = self._requestLayer.send_request(message) request = self._observeLayer.send_request(request) request = self._blockLayer.send_request(request) transaction = self._messageLayer.send_request(request) self.send_datagram(transaction.request) elif isinstance(message, Message): message = self._observeLayer.send_empty(message) message = self._messageLayer.send_empty(None, None, message) self.send_datagram(message) def send_datagram(self, message): host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() message = serializer.serialize(message) self._socket.sendto(message, (host, port)) def receive_datagram(self): logger.debug("Start receiver Thread") while not self.stopped.isSet(): self._socket.settimeout(1) try: datagram, addr = self._socket.recvfrom(1152) except socket.timeout: # pragma: no cover continue except socket.error: # pragma: no cover return else: # pragma: no cover if len(datagram) == 0: print 'orderly shutdown on server end' return serializer = Serializer() try: host, port = addr except ValueError: host, port, tmp1, tmp2 = addr source = (host, port) message = serializer.deserialize(datagram, source) if isinstance(message, Response): transaction, send_ack = self._messageLayer.receive_response(message) if transaction is None: # pragma: no cover continue if send_ack: self._send_ack(transaction) self._blockLayer.receive_response(transaction) if transaction.block_transfer: transaction = self._messageLayer.send_request(transaction.request) self.send_datagram(transaction.request) continue elif transaction is None: # pragma: no cover self._send_rst(transaction) return self._observeLayer.receive_response(transaction) if transaction.notification: # pragma: no cover ack = Message() ack.type = defines.Types['ACK'] ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) self._callback(transaction.response) else: self._callback(transaction.response) elif isinstance(message, Message): self._messageLayer.receive_empty(message) def _send_ack(self, transaction): # Handle separate """ Sends an ACK message for the response. :param transaction: transaction that holds the response """ ack = Message() ack.type = defines.Types['ACK'] if not transaction.response.acknowledged: ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) def _send_rst(self, transaction): # pragma: no cover # Handle separate """ Sends an RST message for the response. :param transaction: transaction that holds the response """ rst = Message() rst.type = defines.Types['RST'] if not transaction.response.acknowledged: rst = self._messageLayer.send_empty(transaction, transaction.response, rst) self.send_datagram(rst)
class CoAP(object): def __init__(self, server_address, 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 # 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) 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(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) 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 _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)
class CoAP(object): 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) # 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) self._socket.bind(self.server_address) else: self._socket = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) print "Yes Socket IPV6 is " print self.server_address self._socket.bind(self.server_address) 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) if len(client_address) > 2: client_address = (client_address[0], client_address[1]) except socket.timeout: continue try: 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 rst.mid = self._messageLayer._current_mid self._messageLayer._current_mid += 1 % 65535 self.send_datagram(rst) continue 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") if transaction.response is not None: 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 args = (transaction, ) t = threading.Thread(target=self.receive_request, args=args) t.start() # self.receive_datagram(data, client_address) elif isinstance(message, Response): logger.error("Received response from %s", message.source) else: # is Message transaction = self._messageLayer.receive_empty(message) if transaction is not None: with transaction: self._blockLayer.receive_empty(message, transaction) self._observeLayer.receive_empty(message, transaction) 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_request(self, transaction): """ Receive datagram from the udp socket. :param data: the udp message :param client_address: the ip and port of the client """ with transaction: transaction.separate_timer = self._start_separate_timer(transaction) self._blockLayer.receive_request(transaction) if transaction.block_transfer: self._stop_separate_timer(transaction.separate_timer) self._messageLayer.send_response(transaction) self.send_datagram(transaction.response) return self._observeLayer.receive_request(transaction) self._requestLayer.receive_request(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 self._observeLayer.send_response(transaction) self._blockLayer.send_response(transaction) self._stop_separate_timer(transaction.separate_timer) self._messageLayer.send_response(transaction) if transaction.response is not None: if transaction.response.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.response) self.send_datagram(transaction.response) 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. :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_retransmission(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'] # TODO handle mutex on transaction if not transaction.request.acknowledged and transaction.request.type == defines.Types["CON"]: ack = self._messageLayer.send_empty(transaction, transaction.request, ack) self.send_datagram(ack) def notify(self, resource): """ Notifies the observers of a certain resource. :param resource: the resource """ observers = self._observeLayer.notify(resource) logger.debug("Notify") for transaction in observers: with transaction: 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_retransmission(transaction, transaction.response) self.send_datagram(transaction.response)
class CoAP(object): """ Client class to perform requests to remote servers. """ def __init__(self, server, starting_mid, callback, sock=None, cb_ignore_read_exception=None, cb_ignore_write_exception=None): """ Initialize the client. :param server: Server address for incoming connections :param callback:the callback function to be invoked when a response is received :param starting_mid: used for testing purposes :param sock: if a socket has been created externally, it can be used directly :param cb_ignore_read_exception: Callback function to handle exception raised during the socket read operation :param cb_ignore_write_exception: Callback function to handle exception raised during the socket write operation """ self._currentMID = starting_mid self._server = server self._callback = callback self._cb_ignore_read_exception = cb_ignore_read_exception self._cb_ignore_write_exception = cb_ignore_write_exception self.stopped = threading.Event() self.to_be_stopped = [] self._messageLayer = MessageLayer(self._currentMID) self._blockLayer = BlockLayer() self._observeLayer = ObserveLayer() self._requestLayer = RequestLayer(self) addrinfo = socket.getaddrinfo(self._server[0], None)[0] if sock is not None: self._socket = sock elif addrinfo[0] == socket.AF_INET: 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._receiver_thread = None def close(self): """ Stop the client. """ self.stopped.set() for event in self.to_be_stopped: event.set() if self._receiver_thread is not None: self._receiver_thread.join() self._socket.close() @property def current_mid(self): """ Return the current MID. :return: the current mid """ return self._currentMID @current_mid.setter def current_mid(self, c): """ Set the current MID. :param c: the mid to set """ assert isinstance(c, int) self._currentMID = c def send_message(self, message): """ Prepare a message to send on the UDP socket. Eventually set retransmissions. :param message: the message to send """ if isinstance(message, Request): request = self._requestLayer.send_request(message) request = self._observeLayer.send_request(request) request = self._blockLayer.send_request(request) transaction = self._messageLayer.send_request(request) self.send_datagram(transaction.request) if transaction.request.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.request) elif isinstance(message, Message): message = self._observeLayer.send_empty(message) message = self._messageLayer.send_empty(None, None, message) self.send_datagram(message) @staticmethod def _wait_for_retransmit_thread(transaction): """ Only one retransmit thread at a time, wait for other to finish """ if hasattr(transaction, 'retransmit_thread'): while transaction.retransmit_thread is not None: logger.debug("Waiting for retransmit thread to finish ...") time.sleep(0.01) continue def _send_block_request(self, transaction): """ A former request resulted in a block wise transfer. With this method, the block wise transfer will be continued, including triggering of the retry mechanism. :param transaction: The former transaction including the request which should be continued. """ transaction = self._messageLayer.send_request(transaction.request) # ... but don't forget to reset the acknowledge flag transaction.request.acknowledged = False self.send_datagram(transaction.request) if transaction.request.type == defines.Types["CON"]: self._start_retransmission(transaction, transaction.request) def send_datagram(self, message): """ Send a message over the UDP socket. :param message: the message to send """ host, port = message.destination logger.debug("send_datagram - " + str(message)) serializer = Serializer() raw_message = serializer.serialize(message) try: self._socket.sendto(raw_message, (host, port)) except Exception as e: if self._cb_ignore_write_exception is not None and callable( self._cb_ignore_write_exception): if not self._cb_ignore_write_exception(e, self): raise if self._receiver_thread is None or not self._receiver_thread.isAlive( ): self._receiver_thread = threading.Thread( target=self.receive_datagram) self._receiver_thread.start() def _start_retransmission(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_stop = threading.Event() self.to_be_stopped.append(transaction.retransmit_stop) transaction.retransmit_thread = threading.Thread( target=self._retransmit, name=str('%s-Retry-%d' % (threading.current_thread().name, message.mid)), args=(transaction, message, future_time, 0)) 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: logger.debug("retransmit loop ... enter") while retransmit_count <= defines.MAX_RETRANSMIT \ and (not message.acknowledged and not message.rejected) \ and not transaction.retransmit_stop.isSet(): transaction.retransmit_stop.wait(timeout=future_time) if not message.acknowledged and not message.rejected and not transaction.retransmit_stop.isSet( ): retransmit_count += 1 future_time *= 2 if retransmit_count < defines.MAX_RETRANSMIT: logger.debug("retransmit loop ... retransmit Request") 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 # Inform the user, that nothing was received self._callback(None) try: self.to_be_stopped.remove(transaction.retransmit_stop) except ValueError: pass transaction.retransmit_stop = None transaction.retransmit_thread = None logger.debug("retransmit loop ... exit") def receive_datagram(self): """ Receive datagram from the UDP socket and invoke the callback function. """ logger.debug("Start receiver Thread") while not self.stopped.isSet(): self._socket.settimeout(0.1) try: datagram, addr = self._socket.recvfrom(1152) except socket.timeout: # pragma: no cover continue except Exception as e: # pragma: no cover if self._cb_ignore_read_exception is not None and callable( self._cb_ignore_read_exception): if self._cb_ignore_read_exception(e, self): continue return else: # pragma: no cover if len(datagram) == 0: logger.debug( "Exiting receiver Thread due to orderly shutdown on server end" ) return serializer = Serializer() try: host, port = addr except ValueError: host, port, tmp1, tmp2 = addr source = (host, port) message = serializer.deserialize(datagram, source) if isinstance(message, Response): logger.debug("receive_datagram - " + str(message)) transaction, send_ack = self._messageLayer.receive_response( message) if transaction is None: # pragma: no cover continue self._wait_for_retransmit_thread(transaction) if send_ack: self._send_ack(transaction) self._blockLayer.receive_response(transaction) if transaction.block_transfer: self._send_block_request(transaction) continue elif transaction is None: # pragma: no cover self._send_rst(transaction) return self._observeLayer.receive_response(transaction) if transaction.notification: # pragma: no cover ack = Message() ack.type = defines.Types['ACK'] ack = self._messageLayer.send_empty( transaction, transaction.response, ack) self.send_datagram(ack) self._callback(transaction.response) else: self._callback(transaction.response) elif isinstance(message, Message): self._messageLayer.receive_empty(message) logger.debug("Exiting receiver Thread due to request") def _send_ack(self, transaction): """ Sends an ACK message for the response. :param transaction: transaction that holds the response """ ack = Message() ack.type = defines.Types['ACK'] if not transaction.response.acknowledged: ack = self._messageLayer.send_empty(transaction, transaction.response, ack) self.send_datagram(ack) def _send_rst(self, transaction): # pragma: no cover """ Sends an RST message for the response. :param transaction: transaction that holds the response """ rst = Message() rst.type = defines.Types['RST'] if not transaction.response.acknowledged: rst = self._messageLayer.send_empty(transaction, transaction.response, rst) self.send_datagram(rst)