예제 #1
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
예제 #2
0
    async def _handler(self, data, addr):
        try:
            transaction, msg_type = await self._handle_datagram(data, addr)
            await self.handle_message(transaction, msg_type)
        except errors.PongException as e:
            if e.message is not None:
                # check if it is ping
                if e.message.type == defines.Type.CON:
                    rst = Message()
                    rst.destination = addr
                    rst.type = defines.Type.RST
                    rst.mid = e.message.mid
                    await self._send_datagram(rst)
        except errors.ProtocolError as e:
            '''
               From RFC 7252, Section 4.2
               reject the message if the recipient
               lacks context to process the message properly, including situations
               where the message is Empty, uses a code with a reserved class (1, 6,
               or 7), or has a message format error.  Rejecting a Confirmable
               message is effected by sending a matching Reset message and otherwise
               ignoring it.

               From RFC 7252, Section 4.3
               A recipient MUST reject the message
               if it lacks context to process the message properly, including the
               case where the message is Empty, uses a code with a reserved class
               (1, 6, or 7), or has a message format error.  Rejecting a Non-
               confirmable message MAY involve sending a matching Reset message
            '''
            rst = Message()
            rst.destination = addr
            rst.type = defines.Type.RST
            rst.mid = e.mid
            rst.payload = e.msg
            await self._send_datagram(rst)
        except errors.InternalError as e:
            if e.transaction.separate_task is not None:
                e.transaction.separate_task.cancel()
                del e.transaction.separate_task

            e.transaction.response = Response()
            e.transaction.response.destination = addr
            e.transaction.response.code = e.response_code
            e.transaction.response.payload = e.msg
            transaction = await self._messageLayer.send_response(e.transaction)
            await self._send_datagram(transaction.response)
            logger.error(e.msg)
        except errors.ObserveError as e:
            if e.transaction is not None:
                if e.transaction.separate_task is not None:
                    e.transaction.separate_task.cancel()
                    del e.transaction.separate_task
                e.transaction.response.payload = e.msg
                e.transaction.response.clear_options()
                e.transaction.response.type = defines.Type.CON
                e.transaction.response.code = e.response_code
                e.transaction = await self._messageLayer.send_response(e.transaction)
                await self._send_datagram(e.transaction.response)
                logger.error("Observe Error")
        except errors.CoAPException as e:
            logger.error(e.msg)
        except Exception as e:
            logger.exception(e)