Example #1
0
    async def receive_request(self, request):
        """
        Handle duplicates and store received messages.

        :type request: Request
        :param request: the incoming request
        :rtype : Transaction
        :return: the edited transaction
        """
        logger.info("receive_request - " + str(request))
        try:
            host, port = request.source
        except AttributeError:
            return
        if request.multicast:
            key_mid = request.mid
            key_token = request.token  # skip duplicated from net interfaces
            if key_token in list(self._transactions_token.keys()):
                # Duplicated multicast request
                self._transactions_token[key_token].request.duplicated = True
                return self._transactions_token[key_token]
        else:
            key_mid = utils.str_append_hash(host, port, request.mid)
            key_token = utils.str_append_hash(host, port, request.token)

            if key_mid in list(self._transactions.keys()):
                # Duplicated
                self._transactions[key_mid].request.duplicated = True
                return self._transactions[key_mid]
        request.timestamp = time.time()
        transaction = Transaction(request=request, timestamp=request.timestamp)
        async with transaction.lock:
            self._transactions[key_mid] = transaction
            self._transactions_token[key_token] = transaction
        return transaction
Example #2
0
    async def receive_request(self, transaction):
        """
        Manage the observe option in the request end eventually initialize the client for adding to
        the list of observers or remove from the list.

        :type transaction: Transaction
        :param transaction: the transaction that owns the request
        :rtype : Transaction
        :return: the modified transaction
        """
        if transaction.request.observe == 0:
            # Observe request
            host, port = transaction.request.source
            key_token = utils.str_append_hash(host, port,
                                              transaction.request.token)
            non_counter = 0
            if key_token in self._relations:
                # Renew registration
                allowed = True
            else:
                allowed = False
            self._relations[key_token] = ObserveItem(time.time(), non_counter,
                                                     allowed, transaction)
        elif transaction.request.observe == 1:
            host, port = transaction.request.source
            key_token = utils.str_append_hash(host, port,
                                              transaction.request.token)
            logger.info("Remove Subscriber")
            try:
                del self._relations[key_token]
            except KeyError:
                pass

        return transaction
Example #3
0
    def send_request(self, request):
        """
        Handles the Blocks option in a outgoing request.

        :type request: Request
        :param request: the outgoing request
        :return: the edited request
        """
        assert isinstance(request, Request)
        if request.block1 or (request.payload is not None and len(request.payload) > defines.MAX_PAYLOAD):
            host, port = request.destination
            key_token = utils.str_append_hash(host, port, request.token)
            if request.block1:
                num, m, size = request.block1
            else:
                num = 0
                m = 1
                size = defines.MAX_PAYLOAD
            # correct m
            m = 0 if ((num * size) + size) > len(request.payload) else 1
            del request.size1
            request.size1 = len(request.payload)
            self._block1_sent[key_token] = BlockItem(size, num, m, size, request.payload, request.content_type)
            request.payload = request.payload[0:size]
            del request.block1
            request.block1 = (num, m, size)
        elif request.block2:
            host, port = request.destination
            key_token = utils.str_append_hash(host, port, request.token)
            num, m, size = request.block2
            item = BlockItem(size, num, m, size, "", None)
            self._block2_sent[key_token] = item
            return request
        return request
Example #4
0
    def receive_empty(self, message):
        """
        Pair ACKs with requests.

        :type message: Message
        :param message: the received message
        :rtype : Transaction
        :return: the transaction to which the message belongs to
        """
        logger.info("receive_empty - " + str(message))
        try:
            host, port = message.source
        except AttributeError:
            return
        all_coap_nodes = defines.ALL_COAP_NODES_IPV6 if socket.getaddrinfo(host, None)[0][0] == socket.AF_INET6 else defines.ALL_COAP_NODES
        key_mid = utils.str_append_hash(host, port, message.mid)
        key_mid_multicast = utils.str_append_hash(all_coap_nodes, port, message.mid)
        key_token = utils.str_append_hash(host, port, message.token)
        key_token_multicast = utils.str_append_hash(all_coap_nodes, port, message.token)
        if key_mid in list(self._transactions.keys()):
            transaction = self._transactions[key_mid]
        elif key_token in self._transactions_token:
            transaction = self._transactions_token[key_token]
        elif key_mid_multicast in list(self._transactions.keys()):
            transaction = self._transactions[key_mid_multicast]
        elif key_token_multicast in self._transactions_token:
            transaction = self._transactions_token[key_token_multicast]
        else:
            logger.warning("Un-Matched incoming empty message " + str(host) + ":" + str(port))
            return None

        if message.type == defines.Types["ACK"]:
            if not transaction.request.acknowledged:
                transaction.request.acknowledged = True
            elif (transaction.response is not None) and (not transaction.response.acknowledged):
                transaction.response.acknowledged = True
        elif message.type == defines.Types["RST"]:
            if not transaction.request.acknowledged:
                transaction.request.rejected = True
            elif not transaction.response.acknowledged:
                transaction.response.rejected = True
        elif message.type == defines.Types["CON"]:
            #implicit ACK (might have been lost)
            logger.debug("Implicit ACK on received CON for waiting transaction")
            transaction.request.acknowledged = True
        else:
            logger.warning("Unhandled message type...")

        if transaction.retransmit_stop is not None:
            transaction.retransmit_stop.set()

        return transaction
Example #5
0
    def send_response(self, transaction):
        """
        Set the type, the token and eventually the MID for the outgoing response

        :type transaction: Transaction
        :param transaction: the transaction that owns the response
        :rtype : Transaction
        :return: the edited transaction
        """
        logger.info("send_response - " + str(transaction.response))
        if transaction.response.type is None:
            if transaction.request.type == defines.Types["CON"] and not transaction.request.acknowledged:
                transaction.response.type = defines.Types["ACK"]
                transaction.response.mid = transaction.request.mid
                transaction.response.acknowledged = True
                transaction.completed = True
            elif transaction.request.type == defines.Types["NON"]:
                transaction.response.type = defines.Types["NON"]
            else:
                transaction.response.type = defines.Types["CON"]
                transaction.response.token = transaction.request.token

        if transaction.response.mid is None:
            transaction.response.mid = self.fetch_mid()
            try:
                host, port = transaction.response.destination
            except AttributeError:
                return
            key_mid = utils.str_append_hash(host, port, transaction.response.mid)
            self._transactions[key_mid] = transaction

        transaction.request.acknowledged = True
        return transaction
Example #6
0
    def receive_response(self, response):
        """
        Pair responses with requests.

        :type response: Response
        :param response: the received response
        :rtype : Transaction
        :return: the transaction to which the response belongs to
        """
        logger.info("receive_response - " + str(response))
        try:
            host, port = response.source
        except AttributeError:
            return
        # all_coap_nodes = defines.ALL_COAP_NODES_IPV6 if socket.getaddrinfo(host, None)[0][0] == socket.AF_INET6 else defines.ALL_COAP_NODES
        key_mid = utils.str_append_hash(host, port, response.mid)
        # key_mid_multicast = utils.str_append_hash(all_coap_nodes, port, response.mid)
        key_token = utils.str_append_hash(host, port, response.token)
        key_token_multicast = utils.str_append_hash(response.destination[0], response.destination[1], response.token)
        if key_mid in list(self._transactions.keys()):
            transaction = self._transactions[key_mid]
            if response.token != transaction.request.token:
                logger.warning("Tokens does not match -  response message " + str(host) + ":" + str(port))
                return None, False
        elif key_token in self._transactions_token:
            transaction = self._transactions_token[key_token]
        # elif key_mid_multicast in list(self._transactions.keys()):
        #     transaction = self._transactions[key_mid_multicast]
        elif key_token_multicast in self._transactions_token:
            transaction = self._transactions_token[key_token_multicast]
            if response.token != transaction.request.token:
                logger.warning("Tokens does not match -  response message " + str(host) + ":" + str(port))
                return None, False
        else:
            logger.warning("Un-Matched incoming response message " + str(host) + ":" + str(port))
            return None, False
        send_ack = False
        if response.type == defines.Types["CON"]:
            send_ack = True

        transaction.request.acknowledged = True
        transaction.completed = True
        transaction.response = response
        if transaction.retransmit_stop is not None:
            transaction.retransmit_stop.set()
        return transaction, send_ack
Example #7
0
    def send_request(self, request):
        """
        Create the transaction and fill it with the outgoing request.

        :type request: Request
        :param request: the request to send
        :rtype : Transaction
        :return: the created transaction
        """
        logger.info("send_request - " + str(request))
        assert isinstance(request, Request)
        try:
            host, port = request.destination
        except AttributeError:
            return

        request.timestamp = time.time()
        transaction = Transaction(request=request, timestamp=request.timestamp)
        if transaction.request.type is None:
            transaction.request.type = defines.Types["CON"]

        if transaction.request.mid is None:
            transaction.request.mid = self.fetch_mid()

        if transaction.request.token is None:
            transaction.request.token = self.fetch_token()

        if request.multicast:
            key_token = utils.str_append_hash(request.source[0], request.source[1], request.token)
            self._transactions_token[key_token] = transaction
            return self._transactions_token[key_token]
        else:
            key_mid = utils.str_append_hash(host, port, request.mid)
            self._transactions[key_mid] = transaction

            key_token = utils.str_append_hash(host, port, request.token)
            self._transactions_token[key_token] = transaction

            return self._transactions[key_mid]
Example #8
0
    def remove_subscriber(self, message):
        """
        Remove a subscriber based on token.

        :param message: the message
        """
        logger.info("Remove Subcriber")
        host, port = message.destination
        key_token = utils.str_append_hash(host, port, message.token)
        try:
            self._relations[key_token].transaction.completed = True
            del self._relations[key_token]
        except KeyError:
            logger.warning("No Subscriber")
Example #9
0
    def send_empty(self, message):
        """
        Eventually remove from the observer list in case of a RST message.

        :type message: Message
        :param message: the message
        :return: the message unmodified
        """
        host, port = message.destination
        key_token = utils.str_append_hash(host, port, message.token)
        if key_token in self._relations and message.type == defines.Types[
                "RST"]:
            del self._relations[key_token]
        return message
Example #10
0
    def receive_response(self, transaction):
        """
        Sets notification's parameters.

        :type transaction: Transaction
        :param transaction: the transaction
        :rtype : Transaction
        :return: the modified transaction
        """
        host, port = transaction.response.source
        key_token = utils.str_append_hash(host, port,
                                          transaction.response.token)
        if key_token in self._relations and transaction.response.type == defines.Types[
                "CON"]:
            transaction.notification = True
        return transaction
Example #11
0
    def send_request(self, request):
        """
        Add itself to the observing list

        :param request: the request
        :return: the request unmodified
        """
        if request.observe == 0:
            # Observe request
            host, port = request.destination
            key_token = utils.str_append_hash(host, port, request.token)

            self._relations[key_token] = ObserveItem(time.time(), None, True,
                                                     None)

        return request
Example #12
0
    def send_response(self, transaction):
        """
        Handles the Blocks option in a outgoing response.

        :type transaction: Transaction
        :param transaction: the transaction that owns the response
        :rtype : Transaction
        :return: the edited transaction
        """
        host, port = transaction.request.source
        key_token = utils.str_append_hash(host, port, transaction.request.token)
        if (key_token in self._block2_receive and transaction.response.payload is not None) or \
                (transaction.response.payload is not None and len(transaction.response.payload) > defines.MAX_PAYLOAD):
            if key_token in self._block2_receive:

                byte = self._block2_receive[key_token].byte
                size = self._block2_receive[key_token].size
                num = self._block2_receive[key_token].num

            else:
                byte = 0
                num = 0
                size = defines.MAX_PAYLOAD
                m = 1

                self._block2_receive[key_token] = BlockItem(byte, num, m, size)

                
            # correct m
            m = 0 if ((num * size) + size) > len(transaction.response.payload) else 1
            # add size2 if requested or if payload is bigger than one datagram
            del transaction.response.size2
            if (transaction.request.size2 is not None and transaction.request.size2 == 0) or \
               (transaction.response.payload is not None and len(transaction.response.payload) > defines.MAX_PAYLOAD):
                transaction.response.size2 = len(transaction.response.payload)

            transaction.response.payload = transaction.response.payload[byte:byte + size]
            del transaction.response.block2
            transaction.response.block2 = (num, m, size)

            self._block2_receive[key_token].byte += size
            self._block2_receive[key_token].num += 1
            if m == 0:
                del self._block2_receive[key_token]

        return transaction
Example #13
0
    def receive_empty(self, empty, transaction):
        """
        Manage the observe feature to remove a client in case of a RST message receveide in reply to a notification.

        :type empty: Message
        :param empty: the received message
        :type transaction: Transaction
        :param transaction: the transaction that owns the notification message
        :rtype : Transaction
        :return: the modified transaction
        """
        if empty.type == defines.Types["RST"]:
            host, port = transaction.request.source
            key_token = utils.str_append_hash(host, port,
                                              transaction.request.token)
            logger.info("Remove Subscriber")
            try:
                del self._relations[key_token]
            except KeyError:
                pass
            transaction.completed = True
        return transaction
Example #14
0
    def send_response(self, transaction):
        """
        Finalize to add the client to the list of observer.

        :type transaction: Transaction
        :param transaction: the transaction that owns the response
        :return: the transaction unmodified
        """
        host, port = transaction.request.source
        key_token = utils.str_append_hash(host, port,
                                          transaction.request.token)
        if key_token in self._relations:
            if transaction.response.code == defines.Codes.CONTENT.number:
                if transaction.resource is not None and transaction.resource.observable:

                    transaction.response.observe = transaction.resource.observe_count
                    self._relations[key_token].allowed = True
                    self._relations[key_token].transaction = transaction
                    self._relations[key_token].timestamp = time.time()
                else:
                    del self._relations[key_token]
            elif transaction.response.code >= defines.Codes.ERROR_LOWER_BOUND:
                del self._relations[key_token]
        return transaction
Example #15
0
    def receive_request(self, transaction):
        """
        Handles the Blocks option in a incoming request.

        :type transaction: Transaction
        :param transaction: the transaction that owns the request
        :rtype : Transaction
        :return: the edited transaction
        """
        if transaction.request.block2 is not None:
            host, port = transaction.request.source
            key_token = utils.str_append_hash(host, port, transaction.request.token)
            num, m, size = transaction.request.block2
            if key_token in self._block2_receive:
                self._block2_receive[key_token].num = num
                self._block2_receive[key_token].size = size
                self._block2_receive[key_token].m = m
                del transaction.request.block2
            else:
                # early negotiation
                byte = num * size
                self._block2_receive[key_token] = BlockItem(byte, num, m, size)
                del transaction.request.block2

        elif transaction.request.block1 is not None:
            # POST or PUT
            host, port = transaction.request.source
            key_token = utils.str_append_hash(host, port, transaction.request.token)
            num, m, size = transaction.request.block1
            if transaction.request.size1 is not None:
                # What to do if the size1 is larger than the maximum resource size or the maxium server buffer
                pass
            if key_token in self._block1_receive:
                # n-th block
                content_type = transaction.request.content_type
                if num != self._block1_receive[key_token].num \
                        or content_type != self._block1_receive[key_token].content_type:
                    # Error Incomplete
                    return self.incomplete(transaction)
                self._block1_receive[key_token].payload += transaction.request.payload
            else:
                # first block
                if num != 0:
                    # Error Incomplete
                    return self.incomplete(transaction)
                content_type = transaction.request.content_type
                self._block1_receive[key_token] = BlockItem(size, num, m, size, transaction.request.payload,
                                                            content_type)

            if m == 0:
                transaction.request.payload = self._block1_receive[key_token].payload
                # end of blockwise
                del transaction.request.block1
                transaction.block_transfer = False
                del self._block1_receive[key_token]
                return transaction
            else:
                # Continue
                transaction.block_transfer = True
                transaction.response = Response()
                transaction.response.destination = transaction.request.source
                transaction.response.token = transaction.request.token
                transaction.response.code = defines.Codes.CONTINUE.number
                transaction.response.block1 = (num, m, size)

            num += 1
            byte = size
            self._block1_receive[key_token].byte = byte
            self._block1_receive[key_token].num = num
            self._block1_receive[key_token].size = size
            self._block1_receive[key_token].m = m

        return transaction
Example #16
0
    def send_empty(self, transaction, related, message):
        """
        Manage ACK or RST related to a transaction. Sets if the transaction has been acknowledged or rejected.

        :param transaction: the transaction
        :param related: if the ACK/RST message is related to the request or the response. Must be equal to
        transaction.request or to transaction.response or None
        :type message: Message
        :param message: the ACK or RST message to send
        """
        logger.info("send_empty - " + str(message))
        if transaction is None:
            try:
                host, port = message.destination
            except AttributeError:
                return
            key_mid = utils.str_append_hash(host, port, message.mid)
            key_token = utils.str_append_hash(host, port, message.token)
            if key_mid in self._transactions:
                transaction = self._transactions[key_mid]
                related = transaction.response
            elif key_token in self._transactions_token:
                transaction = self._transactions_token[key_token]
                related = transaction.response
            else:
                return message

        if message.type == defines.Types["ACK"]:
            if transaction.request == related:
                transaction.request.acknowledged = True
                transaction.completed = True
                message.mid = transaction.request.mid
                message.code = 0
                message.destination = transaction.request.source
            elif transaction.response == related:
                transaction.response.acknowledged = True
                transaction.completed = True
                message.mid = transaction.response.mid
                message.code = 0
                message.token = transaction.response.token
                message.destination = transaction.response.source

        elif message.type == defines.Types["RST"]:
            if transaction.request == related:
                transaction.request.rejected = True
                message._mid = transaction.request.mid
                if message.mid is None:
                    message.mid = self.fetch_mid()
                message.code = 0
                message.token = transaction.request.token
                message.destination = transaction.request.source
            elif transaction.response == related:
                transaction.response.rejected = True
                transaction.completed = True
                message._mid = transaction.response.mid
                if message.mid is None:
                    message.mid = self.fetch_mid()
                message.code = 0
                message.token = transaction.response.token
                message.destination = transaction.response.source
        return message
Example #17
0
    def receive_response(self, transaction):
        """
        Handles the Blocks option in a incoming response.

        :type transaction: Transaction
        :param transaction: the transaction that owns the response
        :rtype : Transaction
        :return: the edited transaction
        """
        host, port = transaction.response.source
        key_token = utils.str_append_hash(host, port, transaction.response.token)
        if key_token in self._block1_sent and transaction.response.block1 is not None:
            item = self._block1_sent[key_token]
            transaction.block_transfer = True
            if item.m == 0:
                transaction.block_transfer = False
                del transaction.request.block1
                return transaction
            n_num, n_m, n_size = transaction.response.block1
            if n_num != item.num:  # pragma: no cover
                logger.warning("Blockwise num acknowledged error, expected " + str(item.num) + " received " +
                               str(n_num))
                return None
            if n_size < item.size:
                logger.debug("Scale down size, was " + str(item.size) + " become " + str(n_size))
                item.size = n_size
            request = transaction.request
            del request.mid
            del request.block1
            request.payload = item.payload[item.byte: item.byte+item.size]
            item.num += 1
            item.byte += item.size
            if len(item.payload) <= item.byte:
                item.m = 0
            else:
                item.m = 1
            request.block1 = (item.num, item.m, item.size)
            # The original request already has this option set
            # request.size1 = len(item.payload)
        elif transaction.response.block2 is not None:

            num, m, size = transaction.response.block2
            logger.error(f"response block2 num:{num} m:{m} token:{key_token}")
            if m == 1:
                transaction.block_transfer = True
                if key_token in self._block2_sent:
                    item = self._block2_sent[key_token]
                    if num != item.num:  # pragma: no cover
                        logger.error("Receive unwanted block")
                        return self.error(transaction, defines.Codes.REQUEST_ENTITY_INCOMPLETE.number)
                    if item.content_type is None:
                        item.content_type = transaction.response.content_type
                    if item.content_type != transaction.response.content_type:  # pragma: no cover
                        logger.error("Content-type Error")
                        return self.error(transaction, defines.Codes.UNSUPPORTED_CONTENT_FORMAT.number)
                    item.byte += size
                    item.num = num + 1
                    item.size = size
                    item.m = m
                    item.payload += transaction.response.payload
                else:
                    item = BlockItem(size, num + 1, m, size, transaction.response.payload,
                                     transaction.response.content_type)
                    self._block2_sent[key_token] = item
                request = transaction.request
                del request.mid
                del request.block2
                request.block2 = (item.num, 0, item.size)
            else:
                transaction.block_transfer = False
                if key_token in self._block2_sent:
                    if self._block2_sent[key_token].content_type != transaction.response.content_type:  # pragma: no cover
                        logger.error("Content-type Error")
                        return self.error(transaction, defines.Codes.UNSUPPORTED_CONTENT_FORMAT.number)
                    transaction.response.payload = self._block2_sent[key_token].payload + transaction.response.payload
                    del self._block2_sent[key_token]
        else:
            transaction.block_transfer = False
        return transaction