Пример #1
0
    async def _retransmit(self, transaction: Transaction, message: Message,
                          future_time: float, retransmit_count: int):
        try:
            if message.type == defines.Type.CON:
                while retransmit_count < defines.MAX_RETRANSMIT and \
                        (not message.acknowledged and not message.rejected) \
                        and not transaction.retransmit_stop:
                    await asyncio.sleep(future_time)
                    if not message.acknowledged and not message.rejected:
                        retransmit_count += 1
                        future_time *= 2
                        logger.error(f"Retransmit message #{retransmit_count}, next attempt in {future_time}")
                        await self._send_datagram(message)

                if message.acknowledged or message.rejected:
                    message.timeouts = False
                else:
                    logger.error("Give up on message {message}".format(message=message.line_print))
                    message.timeouts = True
                    if message.observe is not None:
                        await self._observeLayer.remove_subscriber(message)
                transaction.retransmit_stop = False
        except asyncio.CancelledError:
            logger.debug("_retransmit cancelled")
Пример #2
0
    async def deserialize(
        cls,
        datagram: bytes,
        source: Optional[Tuple[str, int]] = None,
        destination: Optional[Tuple[str, int]] = None
    ) -> Union[Message, Request, Response]:
        """
        De-serialize a stream of byte to a message.

        :param destination:
        :param datagram: the incoming udp message
        :param source: the source address and port (ip, port)
        :return: the message
        :rtype: Message
        """

        try:
            (vttkl, code, mid) = struct.unpack('!BBH', datagram[:4])
        except struct.error:  # pragma: no cover
            raise errors.CoAPException("Message too short for CoAP")

        datagram = datagram[4:]
        version = int((vttkl & 0xC0) >> 6)
        message_type = (vttkl & 0x30) >> 4
        tkl = int(vttkl & 0x0F)

        if version != defines.VERSION:  # pragma: no cover
            raise errors.ProtocolError("Unsupported protocol version", mid)

        if 9 <= tkl <= 15:  # pragma: no cover
            raise errors.ProtocolError("Token Length 9-15 are reserved", mid)

        code_class = (code & 0b11100000) >> 5
        code_details = (code & 0b00011111)
        try:
            cl = MessageCodeClass(code_class)
        except ValueError:  # pragma: no cover
            raise errors.ProtocolError(
                "Unknown code class {0}".format(code_class), mid)
        try:
            if cl == MessageCodeClass.RESPONSE or cl == MessageCodeClass.CLIENT_ERROR or \
                    cl == MessageCodeClass.SERVER_ERROR:
                message = Response()
                message.code = code
            elif cl == MessageCodeClass.REQUEST and code_details != 0:
                message = Request()
                message.code = code
            else:  # Empty message
                message = Message()
                message.code = defines.Code.EMPTY
        except ValueError:  # pragma: no cover
            raise errors.ProtocolError("Unknown code {0}".format(code), mid)

        if source is not None:
            message.source = source
        if destination is not None:
            message.destination = destination
        message.version = version
        message.type = message_type
        message.mid = mid

        if tkl > 0:
            message.token = datagram[:tkl]
        else:
            message.token = None
        try:
            datagram, options = cls._deserialize_options(datagram[tkl:])
        except errors.CoAPException as e:
            raise errors.ProtocolError(e.msg, mid)
        message.add_options(options)
        if len(datagram) == 1:  # pragma: no cover
            raise errors.ProtocolError("Payload Marker with no payload", mid)
        elif len(datagram) == 0:
            message.payload = None
        else:
            message.payload = datagram[1:]
        return message
Пример #3
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
Пример #4
0
    async def test_td_coap_obs_13(self):
        client, server = await self.start_client_server()
        print("TD_COAP_OBS_13")
        path = "/obs-large"

        token = utils.generate_random_hex(2)
        req = Request()
        req.code = defines.Code.GET
        req.uri_path = path
        req.type = defines.Type.CON
        req.mid = random.randint(1, 1000)
        req.destination = self.server_address
        req.token = token
        req.observe = 0

        expected = Response()
        expected.type = defines.Type.ACK
        expected.mid = req.mid
        expected.code = defines.Code.CONTENT
        expected.token = token
        expected.observe = 2
        expected.source = "127.0.0.1", 5683
        expected.payload = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sollicitudin fermentum ornare. " \
                           "Cras accumsan tellus quis dui lacinia eleifend. Proin ultrices rutrum orci vitae luctus. " \
                           "Nullam malesuada pretium elit, at aliquam odio vehicula in. Etiam nec maximus elit. " \
                           "Etiam at erat ac ex ornare feugiat. Curabitur sed malesuada orci, id aliquet nunc. Phasellus " \
                           "nec leo luctus, blandit lorem sit amet, interdum metus. Duis efficitur volutpat magna, ac " \
                           "ultricies nibh aliquet sit amet. Etiam tempor egestas augue in hendrerit. Nunc eget augue " \
                           "ultricies, dignissim lacus et, vulputate dolor. Nulla eros odio, fringilla vel massa ut, " \
                           "facilisis cursus quam. Fusce faucibus lobortis congue. Fusce consectetur porta neque, id " \
                           "sollicitudin velit maximus eu. Sed pharetra leo quam, vel finibus turpis cursus ac. " \
                           "Aenean ac nisi massa. Cras commodo arcu nec ante tristique ullamcorper. Quisque eu hendrerit" \
                           " urna. Cras fringilla eros ut nunc maximus, non porta nisl mollis. Aliquam in rutrum massa." \
                           " Praesent tristique turpis dui, at ultri"
        expected.block2 = (0, 1, 1024)

        transaction = await client.send_request(req)
        ret = await client.receive_response(transaction, 10)

        if ret != expected:
            print("Received: {0}".format(ret))
            print("Expected: {0}".format(expected))
            self.assertEqual(ret, expected)

        req = Request()
        req.code = defines.Code.GET
        req.uri_path = path
        req.type = defines.Type.CON
        req.mid = random.randint(1, 1000)
        req.destination = self.server_address
        req.token = token
        req.block2 = (1, 0, 1024)

        expected = Response()
        expected.type = defines.Type.ACK
        expected.mid = req.mid
        expected.code = defines.Code.CONTENT
        expected.payload = "cies lorem fermentum at. Vivamus sit amet ornare neque, " \
                           "a imperdiet nisl. Quisque a iaculis libero, id tempus lacus. " \
                           "Aenean convallis est non justo consectetur, a hendrerit enim consequat. In accumsan ante " \
                           "a egestas luctus. Etiam quis neque nec eros vestibulum faucibus. Nunc viverra ipsum " \
                           "lectus, vel scelerisque dui dictum a. Ut orci enim, ultrices a ultrices nec, pharetra " \
                           "in quam. Donec accumsan sit amet eros eget fermentum." \
                           "Vivamus ut odio ac odio malesuada accumsan. Aenean vehicula diam at tempus ornare. " \
                           "Phasellus dictum mauris a mi consequat, vitae mattis nulla fringilla. Ut laoreet " \
                           "tellus in nisl efficitur, a luctus justo tempus. Fusce finibus libero eget velit " \
                           "finibus iaculis. Morbi rhoncus purus vel vestibulum ullamcorper. Sed ac metus in urna " \
                           "fermentum feugiat. Nulla nunc diam, sodales aliquam mi id, varius porta nisl. Praesent " \
                           "vel nibh ac turpis rutrum laoreet at non odio. Phasellus ut posuere mi. Suspendisse " \
                           "malesuada velit nec mauris convallis porta. Vivamus sed ultrices sapien, at cras amet."

        expected.token = token
        expected.block2 = (1, 0, 1024)
        expected.source = "127.0.0.1", 5683

        transaction = await client.send_request(req)
        ret = await client.receive_response(transaction, 10)

        if ret != expected:
            print("Received: {0}".format(ret))
            print("Expected: {0}".format(expected))
            self.assertEqual(ret, expected)

        expected = Response()
        expected.type = defines.Type.CON
        expected.mid = self.server_mid + 2
        expected.code = defines.Code.CONTENT
        expected.token = token
        expected.observe = 3
        expected.source = "127.0.0.1", 5683
        expected.payload = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sollicitudin fermentum ornare. " \
                           "Cras accumsan tellus quis dui lacinia eleifend. Proin ultrices rutrum orci vitae luctus. " \
                           "Nullam malesuada pretium elit, at aliquam odio vehicula in. Etiam nec maximus elit. " \
                           "Etiam at erat ac ex ornare feugiat. Curabitur sed malesuada orci, id aliquet nunc. Phasellus " \
                           "nec leo luctus, blandit lorem sit amet, interdum metus. Duis efficitur volutpat magna, ac " \
                           "ultricies nibh aliquet sit amet. Etiam tempor egestas augue in hendrerit. Nunc eget augue " \
                           "ultricies, dignissim lacus et, vulputate dolor. Nulla eros odio, fringilla vel massa ut, " \
                           "facilisis cursus quam. Fusce faucibus lobortis congue. Fusce consectetur porta neque, id " \
                           "sollicitudin velit maximus eu. Sed pharetra leo quam, vel finibus turpis cursus ac. " \
                           "Aenean ac nisi massa. Cras commodo arcu nec ante tristique ullamcorper. Quisque eu hendrerit" \
                           " urna. Cras fringilla eros ut nunc maximus, non porta nisl mollis. Aliquam in rutrum massa." \
                           " Praesent tristique turpis dui, at ultri"
        expected.block2 = (0, 1, 1024)

        transaction.response = None
        ret = await client.receive_response(transaction, 10)

        req = Message()
        req.code = defines.Code.EMPTY
        req.type = defines.Type.ACK
        req.mid = self.server_mid + 2
        req.destination = self.server_address
        req.token = token

        transaction = await client.send_request(req)

        if ret == expected:
            print("PASS")
        else:
            print("Received: {0}".format(ret))
            print("Expected: {0}".format(expected))
            print(ret.pretty_print())

        self.assertEqual(ret, expected)

        self.stop_client_server(client, server)
Пример #5
0
    async def test_td_coap_obs_12(self):
        client, server = await self.start_client_server()
        print("TD_COAP_OBS_12")
        path = "/obs"

        token = utils.generate_random_hex(2)
        req = Request()
        req.code = defines.Code.GET
        req.uri_path = path
        req.type = defines.Type.CON
        req.mid = random.randint(1, 1000)
        req.destination = self.server_address
        req.token = token
        req.observe = 0

        expected = Response()
        expected.type = defines.Type.ACK
        expected.mid = req.mid
        expected.code = defines.Code.CONTENT
        expected.payload = "5"
        expected.token = token
        expected.observe = 2
        expected.source = "127.0.0.1", 5683

        transaction = await client.send_request(req)
        ret = await client.receive_response(transaction, 10)

        if ret != expected:
            print("Received: {0}".format(ret))
            print("Expected: {0}".format(expected))
            self.assertEqual(ret, expected)

        expected = Response()
        expected.type = defines.Type.CON
        expected.mid = self.server_mid + 1
        expected.code = defines.Code.CONTENT
        expected.payload = "6"
        expected.token = token
        expected.observe = 3
        expected.source = "127.0.0.1", 5683

        transaction.response = None
        ret = await client.receive_response(transaction, 10)

        if ret != expected:
            print("Received: {0}".format(ret))
            print("Expected: {0}".format(expected))
            self.assertEqual(ret, expected)

        req = Message()
        req.code = defines.Code.EMPTY
        req.type = defines.Type.ACK
        req.mid = self.server_mid
        req.destination = self.server_address
        req.token = token

        transaction = await client.send_request(req)

        req = Request()
        req.code = defines.Code.GET
        req.uri_path = path
        req.type = defines.Type.CON
        req.mid = random.randint(1, 1000)
        req.destination = self.server_address
        req.observe = 1
        req.token = token

        expected = Response()
        expected.type = defines.Type.ACK
        expected.mid = req.mid
        expected.code = defines.Code.CONTENT
        expected.token = token
        expected.source = self.server_address
        expected.payload = "6"

        transaction = await client.send_request(req)
        ret = await client.receive_response(transaction, 10)

        if ret == expected:
            print("PASS")
        else:
            print("Received: {0}".format(ret))
            print("Expected: {0}".format(expected))
            print(ret.pretty_print())

        self.assertEqual(ret, expected)

        self.stop_client_server(client, server)
Пример #6
0
    async def test_td_coap_obs_08(self):
        client, server = await self.start_client_server()
        print("TD_COAP_OBS_08")
        path = "/obs"

        token = utils.generate_random_hex(2)
        req = Request()
        req.code = defines.Code.GET
        req.uri_path = path
        req.type = defines.Type.CON
        req.mid = random.randint(1, 1000)
        req.destination = self.server_address
        req.token = token
        req.observe = 0

        expected = Response()
        expected.type = defines.Type.ACK
        expected.mid = req.mid
        expected.code = defines.Code.CONTENT
        expected.payload = "5"
        expected.token = token
        expected.observe = 2
        expected.source = "127.0.0.1", 5683

        transaction = await client.send_request(req)
        ret = await client.receive_response(transaction, 10)

        if ret != expected:
            print("Received: {0}".format(ret))
            print("Expected: {0}".format(expected))
            self.assertEqual(ret, expected)
        else:
            print("PASS-EXCHANGE-01")

        expected = Response()
        expected.type = defines.Type.CON
        expected.mid = self.server_mid + 1
        expected.code = defines.Code.CONTENT
        expected.payload = "6"
        expected.token = token
        expected.observe = 3
        expected.source = "127.0.0.1", 5683

        transaction.response = None
        ret = await client.receive_response(transaction, 10)

        if ret != expected:
            print("Received: {0}".format(ret))
            print("Expected: {0}".format(expected))
            self.assertEqual(ret, expected)
        else:
            print("PASS-EXCHANGE-02")

        req = Message()
        req.code = defines.Code.EMPTY
        req.type = defines.Type.ACK
        req.mid = self.server_mid + 1
        req.destination = self.server_address
        req.token = token

        transaction = await client.send_request(req)

        token2 = utils.generate_random_hex(2)
        req = Request()
        req.code = defines.Code.PUT
        req.uri_path = path
        req.type = defines.Type.CON
        req.mid = random.randint(1, 1000)
        req.destination = self.server_address
        req.token = token2
        req.payload = "{ \"value\": 100}"
        req.content_type = defines.ContentType.application_json

        expected = Response()
        expected.type = defines.Type.ACK
        expected.mid = req.mid
        expected.code = defines.Code.CHANGED
        expected.token = token2
        expected.source = self.server_address

        transaction2 = await client.send_request(req)
        ret = await client.receive_response(transaction2, 10)

        if ret != expected:
            print("Received: {0}".format(ret))
            print("Expected: {0}".format(expected))
            self.assertEqual(ret, expected)
        else:
            print("PASS-EXCHANGE-03")

        expected = Response()
        expected.type = defines.Type.CON
        expected.mid = self.server_mid + 4
        expected.code = defines.Code.NOT_ACCEPTABLE
        expected.token = token
        expected.source = self.server_address
        expected.payload = "Content-Type changed"

        transaction.response = None
        ret = await client.receive_response(transaction, 10)

        if ret == expected:
            print("PASS")
        else:
            print("Received: {0}".format(ret))
            print("Expected: {0}".format(expected))
            print(ret.pretty_print())

        self.assertEqual(ret, expected)

        self.stop_client_server(client, server)
Пример #7
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)