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
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)