Exemplo n.º 1
0
    async def send_request(self, request: 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
        """

        try:
            host, port = request.destination
        except TypeError or AttributeError:  # pragma: no cover
            raise errors.CoAPException("Request destination cannot be computed")

        request.timestamp = time.time()
        transaction = Transaction(request=request, timestamp=request.timestamp)
        if transaction.request.type is None:  # pragma: no cover
            raise errors.CoAPException("Request type is not set")

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

        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
        logger.debug("send_request - {0}".format(request))
        return transaction
Exemplo n.º 2
0
    async def receive_request(self, request: Request) -> Transaction:
        """
        Handle duplicates and store received messages.

        :type request: Request
        :param request: the incoming request
        :rtype : Transaction
        :return: the edited transaction
        """
        logger.debug("receive_request - {0}".format(request))
        try:
            host, port = request.source
        except TypeError or AttributeError:  # pragma: no cover
            raise errors.CoAPException("Request Source cannot be computed")
        key_mid = utils.str_append_hash(host, port, request.mid)
        key_token = utils.str_append_hash(host, port, request.token)

        transaction = self._transactions.get(key_mid, None)
        if transaction is not None:
            from_token = self._transactions_token.get(key_token, None)
            if from_token is None:
                logger.warning("Duplicated message with different Token")
                raise errors.ProtocolError(msg="Tokens does not match",
                                           mid=transaction.request.mid)
            transaction.request.duplicated = True
        else:
            request.timestamp = time.time()
            transaction = Transaction(request=request, timestamp=request.timestamp)
            self._transactions[key_mid] = transaction
            self._transactions_token[key_token] = transaction
        return transaction
Exemplo n.º 3
0
    async def receive_empty(self, empty, transaction):
        """
        Manage the observe feature to remove a client in case of a RST message received 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.Type.RST:
            try:
                host, port = transaction.request.source
            except AttributeError as e:  # pragma: no cover
                raise errors.CoAPException("Request Source cannot be computed")

            key_token = utils.str_append_hash(host, port,
                                              transaction.request.token)
            logger.info("Remove Subscriber")
            try:
                del self._relations[key_token]
            except KeyError:  # pragma: no cover
                logger.exception("Subscriber was not registered")
            transaction.completed = True
        return transaction
Exemplo n.º 4
0
    async def send_response(self, transaction: Transaction) -> 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
        """
        if transaction.response.type is None:
            if transaction.request.type == defines.Type.CON and not transaction.request.acknowledged:
                transaction.response.type = defines.Type.ACK
                transaction.response.mid = transaction.request.mid
                transaction.response.acknowledged = True
                # transaction.completed = True
            elif transaction.request.type == defines.Type.NON:
                transaction.response.type = defines.Type.NON
                transaction.response.acknowledged = True
            else:
                transaction.response.type = defines.Type.CON

        transaction.response.token = transaction.request.token
        transaction.response.timestamp = time.time()

        if transaction.response.mid is None:
            transaction.response.mid = self.fetch_mid()
        try:
            host, port = transaction.response.destination
        except TypeError or AttributeError:  # pragma: no cover
            raise errors.CoAPException("Response destination cannot be computed")

        logger.debug("send_response - {0}".format(transaction.response))

        key_mid = utils.str_append_hash(host, port, transaction.response.mid)
        key_token = utils.str_append_hash(host, port, transaction.response.token)
        self._transactions[key_mid] = transaction
        self._transactions_token[key_token] = transaction
        request_host, request_port = transaction.request.source
        if request_host.is_multicast:
            key_mid_multicast = utils.str_append_hash(request_host, request_port, transaction.response.mid)
            key_token_multicast = utils.str_append_hash(request_host, request_port, transaction.response.token)
            self._transactions[key_mid_multicast] = transaction
            self._transactions_token[key_token_multicast] = transaction

        transaction.request.acknowledged = True
        return transaction
Exemplo n.º 5
0
    async def send_response(self, transaction: Transaction) -> 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
        """
        try:
            host, port = transaction.request.source
        except AttributeError:  # pragma: no cover
            raise errors.CoAPException("Request Source cannot be computed")

        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)

            if num != 0:
                del transaction.response.observe
            m = 0
            if len(transaction.response.payload) > (byte + size):
                m = 1

            transaction.response.payload = transaction.response.payload[
                byte:byte + size]
            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]
        elif key_token in self._block1_receive:
            num = self._block1_receive[key_token].num
            size = self._block1_receive[key_token].size
            m = self._block1_receive[key_token].m
            transaction.response.block1 = (num - 1, m, size)
            if m == 1:
                transaction.response.code = defines.Code.CONTINUE
            else:
                del self._block1_receive[key_token]

        return transaction
Exemplo n.º 6
0
    async def receive_response(self, response: 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.debug("receive_response - {0}".format(response))
        try:
            host, port = response.source
        except TypeError or AttributeError:  # pragma: no cover
            raise errors.CoAPException("Response Source cannot be computed")

        key_mid = utils.str_append_hash(host, port, response.mid)
        key_mid_multicast = utils.str_append_hash(defines.ALL_COAP_NODES, port, response.mid)
        key_token = utils.str_append_hash(host, port, response.token)
        key_token_multicast = utils.str_append_hash(defines.ALL_COAP_NODES, port, response.token)
        if key_mid in list(self._transactions.keys()):
            transaction = self._transactions[key_mid]
            if response.token != transaction.request.token:
                logger.warning(f"Tokens does not match -  response message {host}:{port}")
                raise errors.CoAPException(msg=f"Tokens does not match -  response message {host}:{port}")
        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(f"Tokens does not match -  response message {host}:{port}")
                raise errors.CoAPException(msg=f"Tokens does not match -  response message {host}:{port}")
        else:
            raise errors.CoAPException("Un-Matched incoming response message " + str(host) + ":" + str(port))

        transaction.request.acknowledged = True
        transaction.response = response
        if response.type != defines.Type.CON:
            transaction.response.acknowledged = True
        transaction.retransmit_stop = True
        if transaction.retransmit_task is not None:
            transaction.retransmit_task.cancel()
        return transaction
Exemplo n.º 7
0
    async def send_request(self, request: Request):
        """
        Handles the Blocks option in a outgoing request.

        :type request: Request
        :param request: the outgoing request
        :return: the edited request
        """
        if request.block1 or (request.payload is not None
                              and len(request.payload) > defines.MAX_PAYLOAD):
            try:
                host, port = request.destination
            except AttributeError:  # pragma: no cover
                raise errors.CoAPException(
                    "Request destination cannot be computed")
            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
                request.block1 = num, m, size
            self._block1_sent[key_token] = BlockItem(size, num, m, size,
                                                     request.payload,
                                                     request.content_type)
            request.payload = request.payload[0:size]

        elif request.block2:
            try:
                host, port = request.destination
            except AttributeError:  # pragma: no cover
                raise errors.CoAPException(
                    "Request destination cannot be computed")
            key_token = utils.str_append_hash(host, port, request.token)
            num, m, size = request.block2
            item = BlockItem(size, num, m, size)
            self._block2_sent[key_token] = item
            return request
        return request
Exemplo n.º 8
0
    async def remove_subscriber(self, message):
        """
        Remove a subscriber based on token.

        :param message: the message
        """
        logger.debug("Remove Subcriber")
        try:
            host, port = message.destination
        except AttributeError:  # pragma: no cover
            raise errors.CoAPException(
                "Message destination cannot be computed")
        key_token = utils.str_append_hash(host, port, message.token)
        try:
            del self._relations[key_token]
        except KeyError:  # pragma: no cover
            logger.exception("Subscriber was not registered")
Exemplo n.º 9
0
    async 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
        """

        try:
            host, port = message.destination
        except AttributeError as e:  # pragma: no cover
            raise errors.CoAPException(
                "Message destination cannot be computed")
        key_token = utils.str_append_hash(host, port, message.token)
        if key_token in self._relations and message.type == defines.Type.RST:
            del self._relations[key_token]
        return message
Exemplo n.º 10
0
    async def receive_response(self, transaction):
        """
        Sets notification's parameters.

        :type transaction: Transaction
        :param transaction: the transaction
        :rtype : Transaction
        :return: the modified transaction
        """
        try:
            host, port = transaction.response.source
        except AttributeError as e:  # pragma: no cover
            raise errors.CoAPException("Message source cannot be computed")
        key_token = utils.str_append_hash(host, port,
                                          transaction.response.token)

        if key_token in self._relations and transaction.response.type == defines.Type.CON:
            transaction.notification = True
        return transaction
Exemplo n.º 11
0
    async 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
            try:
                host, port = request.destination
            except AttributeError as e:  # pragma: no cover
                raise errors.CoAPException(
                    "Request destination cannot be computed")

            key_token = utils.str_append_hash(host, port, request.token)

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

        return request
Exemplo n.º 12
0
    async 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
        """
        try:
            host, port = transaction.request.source
        except AttributeError as e:  # pragma: no cover
            raise errors.CoAPException("Request source cannot be computed")

        key_token = utils.str_append_hash(host, port,
                                          transaction.request.token)
        if key_token in self._relations:
            if transaction.response.code == defines.Code.CONTENT:
                if transaction.resource is not None and transaction.resource.observable:
                    if transaction.resource.content_type == self._relations[key_token].content_type or \
                                    self._relations[key_token].content_type == -1:
                        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()
                        self._relations[
                            key_token].content_type = transaction.resource.content_type
                        del transaction.request.observe
                        if transaction.response.max_age is not None:
                            self._relations[
                                key_token].pmin = transaction.response.max_age
                    else:
                        del self._relations[key_token]
                        raise errors.ObserveError("Content-Type changed",
                                                  defines.Code.NOT_ACCEPTABLE,
                                                  transaction=transaction)
                else:
                    del self._relations[key_token]
            elif transaction.response.code.is_error():
                del self._relations[key_token]
        return transaction
Exemplo n.º 13
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 or transaction.request.observe == 1:
            # Observe request
            try:
                host, port = transaction.request.source
            except AttributeError as e:  # pragma: no cover
                raise errors.CoAPException("Request Source cannot be computed")

            key_token = utils.str_append_hash(host, port,
                                              transaction.request.token)

            if transaction.request.observe == 0:
                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, -1)
            elif transaction.request.observe == 1:
                logger.info("Remove Subscriber")
                try:
                    del self._relations[key_token]
                except KeyError:  # pragma: no cover
                    logger.exception("Subscriber was not registered")

        return transaction
Exemplo n.º 14
0
    async def receive_response(self, transaction: 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
        """
        try:
            host, port = transaction.response.source
        except AttributeError:  # pragma: no cover
            raise errors.CoAPException("Response source cannot be computed")
        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]
            n_num, n_m, n_size = transaction.response.block1
            if n_num != item.num:  # pragma: no cover
                if transaction.response.type == defines.Type.CON or transaction.response.type == defines.Type.NON:
                    raise errors.ProtocolError(
                        msg=f"Block num acknowledged error, expected {item.num} "
                        f"received {n_num}",
                        mid=transaction.response.mid)
                else:
                    raise errors.CoAPException(
                        msg=f"Block num acknowledged error, expected {item.num} "
                        f"received {n_num}")
            if n_size < item.size:
                logger.debug("Scale down size, was " + str(item.size) +
                             " become " + str(n_size))
                item.size = n_size

        elif transaction.response.block2 is not None:
            num, m, size = transaction.response.block2
            if m == 1:
                if key_token in self._block2_sent:
                    item = self._block2_sent[key_token]
                    if num != item.num:  # pragma: no cover
                        if transaction.response.type == defines.Type.CON or transaction.response.type == defines.Type.NON:
                            raise errors.ProtocolError(
                                msg=
                                f"Receive unwanted block, expected {item.num} "
                                f"received {num}",
                                mid=transaction.response.mid)
                        else:
                            raise errors.CoAPException(
                                msg=
                                f"Receive unwanted block, expected {item.num} "
                                f"received {num}")

                    if item.content_type is None:
                        item.content_type = transaction.response.content_type
                    if item.content_type != transaction.response.content_type:
                        if transaction.response.type == defines.Type.CON or transaction.response.type == defines.Type.NON:
                            raise errors.ProtocolError(
                                msg=f"Content-type Error",
                                mid=transaction.response.mid)
                        else:
                            raise errors.CoAPException(
                                msg=f"Content-type Error")
                    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

            else:
                if key_token in self._block2_sent:
                    if self._block2_sent[key_token].content_type is None:
                        self._block2_sent[
                            key_token].content_type = transaction.response.content_type
                    if self._block2_sent[
                            key_token].content_type != transaction.response.content_type:  # pragma: no cover
                        if transaction.response.type == defines.Type.CON or transaction.response.type == defines.Type.NON:
                            raise errors.ProtocolError(
                                msg=f"Content-type Error",
                                mid=transaction.response.mid)
                        else:
                            raise errors.CoAPException(
                                msg=f"Content-type Error")
                    del self._block2_sent[key_token]

        return transaction
Exemplo n.º 15
0
    async def send_empty(self, transaction: Optional[Transaction] = None,
                         related: Optional[defines.MessageRelated] = None,
                         message: Message = None) -> Tuple[Transaction, Message]:
        """
        Manage ACK or RST related to a transaction. Sets if the transaction has been acknowledged or rejected.

        :param message: 
        :param msg_type:
        :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
        """
        if message is None:
            message = Message()
        if related == defines.MessageRelated.REQUEST:
            if transaction.request.type == defines.Type.CON:
                transaction.request.acknowledged = True
                # transaction.completed = True
                message.type = defines.Type.ACK
                message.mid = transaction.request.mid
                message.code = defines.Code.EMPTY
                message.destination = transaction.request.source
            else:  # pragma: no cover
                raise errors.CoAPException("NON messages cannot be replied with ACKs")

            try:
                host, port = transaction.request.source
            except TypeError or AttributeError:  # pragma: no cover
                raise errors.CoAPException("Response destination cannot be computed")
            key_mid = utils.str_append_hash(host, port, transaction.request.mid)
            key_token = utils.str_append_hash(host, port, transaction.request.token)
            self._transactions[key_mid] = transaction
            self._transactions_token[key_token] = transaction

        elif related == defines.MessageRelated.RESPONSE:
            if transaction.response.type == defines.Type.CON:
                transaction.response.acknowledged = True
                # transaction.completed = True
                message.type = defines.Type.ACK
                message.mid = transaction.response.mid
                message.code = defines.Code.EMPTY
                message.destination = transaction.response.source
            else:  # pragma: no cover
                raise errors.CoAPException("NON messages cannot be replied with ACKs")

            try:
                host, port = transaction.response.source
            except TypeError or AttributeError:  # pragma: no cover
                raise errors.CoAPException("Response destination cannot be computed")
            key_mid = utils.str_append_hash(host, port, transaction.response.mid)
            key_token = utils.str_append_hash(host, port, transaction.response.token)
            self._transactions[key_mid] = transaction
            self._transactions_token[key_token] = transaction
            request_host, request_port = transaction.request.destination
            if request_host.is_multicast:
                key_mid_multicast = utils.str_append_hash(request_host, request_port, transaction.response.mid)
                key_token_multicast = utils.str_append_hash(request_host, request_port, transaction.response.token)
                self._transactions[key_mid_multicast] = transaction
                self._transactions_token[key_token_multicast] = transaction
        else:
            # for clients
            try:
                host, port = message.destination
            except TypeError or AttributeError:  # pragma: no cover
                raise errors.CoAPException("Message destination cannot be computed")

            key_mid = utils.str_append_hash(host, port, message.mid)
            key_token = utils.str_append_hash(host, port, message.token)

            message.timestamp = time.time()
            transaction = Transaction(request=message, timestamp=message.timestamp)
            self._transactions[key_mid] = transaction
            self._transactions_token[key_token] = transaction
        logger.debug("send_empty -  {0}".format(message))
        return transaction, message
Exemplo n.º 16
0
    async def receive_request(self, transaction: Transaction) -> 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
        """
        try:
            host, port = transaction.request.source
        except AttributeError:  # pragma: no cover
            raise errors.CoAPException("Request Source cannot be computed")

        key_token = utils.str_append_hash(host, port,
                                          transaction.request.token)

        if transaction.request.block2 is not None:

            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
            else:
                # early negotiation
                byte = size * num
                self._block2_receive[key_token] = BlockItem(byte, num, m, size)

        elif transaction.request.block1 is not None or len(
                transaction.request.payload) > defines.MAX_PAYLOAD:
            # POST or PUT
            if len(transaction.request.payload) > defines.MAX_PAYLOAD:
                num, m, size = 0, 1, defines.MAX_PAYLOAD
                transaction.request.payload = transaction.request.payload[
                    0:size]
            else:
                num, m, size = transaction.request.block1
            if key_token in self._block1_receive:
                content_type = transaction.request.content_type
                if num != self._block1_receive[key_token].num \
                        or content_type != self._block1_receive[key_token].content_type \
                        or transaction.request.payload is None:
                    # Error Incomplete
                    raise errors.InternalError(
                        msg="Entity incomplete",
                        response_code=defines.Code.REQUEST_ENTITY_INCOMPLETE,
                        transaction=transaction)
                self._block1_receive[
                    key_token].payload += transaction.request.payload
            else:
                # first block
                if num != 0:
                    # Error Incomplete
                    raise errors.InternalError(
                        msg="Entity incomplete",
                        response_code=defines.Code.REQUEST_ENTITY_INCOMPLETE,
                        transaction=transaction)
                content_type = transaction.request.content_type
                self._block1_receive[key_token] = BlockItem(
                    size, num, m, size, transaction.request.payload,
                    content_type)
            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

            if m == 0:
                transaction.request.payload = self._block1_receive[
                    key_token].payload
                # end of blockwise
                transaction.block_transfer = False
                #
                return transaction
            else:
                # Continue
                transaction.block_transfer = True
                transaction.response = Response()
                transaction.response.destination = transaction.request.source
                transaction.response.token = transaction.request.token

        return transaction
Exemplo n.º 17
0
    async def receive_empty(self, message: Message) -> Transaction:
        """
        Pair ACKs with requests.

        :type message: Message
        :param message: the received message
        :rtype : Transaction
        :return: the transaction to which the message belongs to
        """
        logger.debug("receive_empty - {0}".format(message))
        try:
            host, port = message.source
        except TypeError or AttributeError:  # pragma: no cover
            raise errors.CoAPException("Request Source cannot be computed")

        key_mid = utils.str_append_hash(host, port, message.mid)
        key_mid_multicast = utils.str_append_hash(defines.ALL_COAP_NODES, port, message.mid)
        key_token = utils.str_append_hash(host, port, message.token)
        key_token_multicast = utils.str_append_hash(defines.ALL_COAP_NODES, port, message.token)
        transaction = self._transactions.get(key_mid, None)
        in_memory = [(transaction, key_mid)]
        transaction = self._transactions_token.get(key_token, None)
        in_memory.append((transaction, key_token))
        transaction = self._transactions.get(key_mid_multicast, None)
        in_memory.append((transaction, key_mid_multicast))
        transaction = self._transactions_token.get(key_token_multicast, None)
        in_memory.append((transaction, key_token_multicast))
        valid = list(filter(lambda x: x[0] is not None, in_memory))
        if len(valid) == 0:  # pragma: no cover
            logger.warning("Un-Matched incoming empty message fom {0}:{1} with MID {2}".format(host, port,
                                                                                               message.mid))
            raise errors.PongException("Un-Matched incoming empty message fom {0}:{1} with MID {2}"
                                       .format(host, port, message.mid), message=message)
        else:
            transaction, key = valid[0]

        if message.type == defines.Type.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.Type.RST:
            if not transaction.request.acknowledged:
                transaction.request.rejected = True
            elif not transaction.response.acknowledged:
                transaction.response.rejected = True
        elif message.type == defines.Type.CON:  # pragma: no cover
            # implicit ACK (might have been lost)
            logger.debug("Implicit ACK on received CON for waiting transaction")
            transaction.request.acknowledged = True
        else:  # pragma: no cover
            logger.warning("Unhandled message type...")
            raise errors.CoAPException("Unhandled message type...")

        transaction.retransmit_stop = True
        if transaction.retransmit_task is not None:
            transaction.retransmit_task.cancel()

        for t, k in valid:

            if k == key_mid:
                self._transactions[key_mid] = transaction
            elif k == key_token:
                self._transactions_token[key_token] = transaction
            elif k == key_mid_multicast:
                self._transactions[key_mid_multicast] = transaction
            elif k == key_token_multicast:
                self._transactions_token[key_token_multicast] = transaction

        return transaction