From 1c18883b59cdaf3f46c1293d80b6260ab8c9e88f Mon Sep 17 00:00:00 2001 From: Michiel De Witte Date: Mon, 19 Feb 2018 14:06:35 +0100 Subject: [PATCH 1/5] Basic client-side implementation of RFC7967: CoAP No-Response Option --- coapthon/client/coap.py | 7 +++++++ coapthon/client/helperclient.py | 20 +++++++++++++++----- coapthon/defines.py | 2 ++ coapthon/messages/request.py | 10 ++++++++++ 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/coapthon/client/coap.py b/coapthon/client/coap.py index 8627f8e..a9badd8 100644 --- a/coapthon/client/coap.py +++ b/coapthon/client/coap.py @@ -165,6 +165,13 @@ class CoAP(object): if not self._cb_ignore_write_exception(e, self): raise + # if you're explicitly setting that you don't want a response, don't wait for it + # https://tools.ietf.org/html/rfc7967#section-2.1 + for opt in message.options: + if opt.number == defines.OptionRegistry.NO_RESPONSE.number: + if opt.value == 26: + return + if self._receiver_thread is None or not self._receiver_thread.isAlive(): self._receiver_thread = threading.Thread(target=self.receive_datagram) self._receiver_thread.start() diff --git a/coapthon/client/helperclient.py b/coapthon/client/helperclient.py index 986be8b..dc2672b 100644 --- a/coapthon/client/helperclient.py +++ b/coapthon/client/helperclient.py @@ -134,7 +134,7 @@ class HelperClient(object): return self.send_request(request, callback, timeout) - def post(self, path, payload, callback=None, timeout=None, **kwargs): # pragma: no cover + def post(self, path, payload, callback=None, timeout=None, no_response=False, **kwargs): # pragma: no cover """ Perform a POST on a certain path. @@ -148,13 +148,17 @@ class HelperClient(object): request.token = generate_random_token(2) request.payload = payload + if no_response: + request.add_no_response() + request.type = defines.Types["NON"] + for k, v in kwargs.items(): if hasattr(request, k): setattr(request, k, v) - return self.send_request(request, callback, timeout) + return self.send_request(request, callback, timeout, no_response=no_response) - def put(self, path, payload, callback=None, timeout=None, **kwargs): # pragma: no cover + def put(self, path, payload, callback=None, timeout=None, no_response=False, **kwargs): # pragma: no cover """ Perform a PUT on a certain path. @@ -168,11 +172,15 @@ class HelperClient(object): request.token = generate_random_token(2) request.payload = payload + if no_response: + request.add_no_response() + request.type = defines.Types["NON"] + for k, v in kwargs.items(): if hasattr(request, k): setattr(request, k, v) - return self.send_request(request, callback, timeout) + return self.send_request(request, callback, timeout, no_response=no_response) def discover(self, callback=None, timeout=None, **kwargs): # pragma: no cover """ @@ -190,7 +198,7 @@ class HelperClient(object): return self.send_request(request, callback, timeout) - def send_request(self, request, callback=None, timeout=None): # pragma: no cover + def send_request(self, request, callback=None, timeout=None, no_response=False): # pragma: no cover """ Send a request to the remote server. @@ -204,6 +212,8 @@ class HelperClient(object): thread.start() else: self.protocol.send_message(request) + if no_response: + return try: response = self.queue.get(block=True, timeout=timeout) except Empty: diff --git a/coapthon/defines.py b/coapthon/defines.py index b786021..9b081ea 100644 --- a/coapthon/defines.py +++ b/coapthon/defines.py @@ -127,6 +127,7 @@ class OptionRegistry(object): PROXY_URI = OptionItem(35, "Proxy-Uri", STRING, False, None) PROXY_SCHEME = OptionItem(39, "Proxy-Schema", STRING, False, None) SIZE1 = OptionItem(60, "Size1", INTEGER, False, None) + NO_RESPONSE = OptionItem(258, "No-Response", INTEGER, False, None) RM_MESSAGE_SWITCHING = OptionItem(65524, "Routing", OPAQUE, False, None) LIST = { @@ -149,6 +150,7 @@ class OptionRegistry(object): 35: PROXY_URI, 39: PROXY_SCHEME, 60: SIZE1, + 258: NO_RESPONSE, 65524: RM_MESSAGE_SWITCHING } diff --git a/coapthon/messages/request.py b/coapthon/messages/request.py index 9eee41d..f6067d0 100644 --- a/coapthon/messages/request.py +++ b/coapthon/messages/request.py @@ -187,6 +187,16 @@ class Request(Message): option.value = None self.add_option(option) + def add_no_response(self): + """ + Add the no-response option to the request + # https://tools.ietf.org/html/rfc7967#section-2.1 + """ + option = Option() + option.number = defines.OptionRegistry.NO_RESPONSE.number + option.value = 26 + self.add_option(option) + @if_none_match.deleter def if_none_match(self): """ From 94aa766b8f86cff259213b76e729a9bb2ecb5e4a Mon Sep 17 00:00:00 2001 From: Michiel De Witte Date: Wed, 21 Feb 2018 13:23:47 +0100 Subject: [PATCH 2/5] removed forced logging --- coapthon/client/coap.py | 5 ----- coapthon/forward_proxy/coap.py | 5 +---- coapthon/reverse_proxy/coap.py | 6 +----- coapthon/server/coap.py | 5 ----- 4 files changed, 2 insertions(+), 19 deletions(-) diff --git a/coapthon/client/coap.py b/coapthon/client/coap.py index a9badd8..471282e 100644 --- a/coapthon/client/coap.py +++ b/coapthon/client/coap.py @@ -14,18 +14,13 @@ from coapthon.messages.message import Message from coapthon.messages.request import Request from coapthon.messages.response import Response from coapthon.serializer import Serializer -from coapthon.utils import create_logging import collections __author__ = 'Giacomo Tanganelli' -if not os.path.isfile("logging.conf"): - create_logging() - logger = logging.getLogger(__name__) -logging.config.fileConfig("logging.conf", disable_existing_loggers=False) class CoAP(object): diff --git a/coapthon/forward_proxy/coap.py b/coapthon/forward_proxy/coap.py index 5b42ddb..b448cca 100644 --- a/coapthon/forward_proxy/coap.py +++ b/coapthon/forward_proxy/coap.py @@ -17,15 +17,12 @@ from coapthon.messages.message import Message from coapthon.messages.request import Request from coapthon.resources.resource import Resource from coapthon.serializer import Serializer -from coapthon.utils import Tree, create_logging +from coapthon.utils import Tree __author__ = 'Giacomo Tanganelli' -if not os.path.isfile("logging.conf"): - create_logging() logger = logging.getLogger(__name__) -logging.config.fileConfig("logging.conf", disable_existing_loggers=False) class CoAP(object): diff --git a/coapthon/reverse_proxy/coap.py b/coapthon/reverse_proxy/coap.py index 50db0d2..61ba364 100644 --- a/coapthon/reverse_proxy/coap.py +++ b/coapthon/reverse_proxy/coap.py @@ -21,16 +21,12 @@ from coapthon.messages.request import Request from coapthon.resources.remoteResource import RemoteResource from coapthon.resources.resource import Resource from coapthon.serializer import Serializer -from coapthon.utils import Tree, create_logging +from coapthon.utils import Tree __author__ = 'Giacomo Tanganelli' -if not os.path.isfile("logging.conf"): - create_logging() - logger = logging.getLogger(__name__) -logging.config.fileConfig("logging.conf", disable_existing_loggers=False) class CoAP(object): diff --git a/coapthon/server/coap.py b/coapthon/server/coap.py index e2b4e5e..5a52102 100644 --- a/coapthon/server/coap.py +++ b/coapthon/server/coap.py @@ -17,18 +17,13 @@ from coapthon.messages.response import Response from coapthon.resources.resource import Resource from coapthon.serializer import Serializer from coapthon.utils import Tree -from coapthon.utils import create_logging import collections __author__ = 'Giacomo Tanganelli' -if not os.path.isfile("logging.conf"): - create_logging() - logger = logging.getLogger(__name__) -logging.config.fileConfig("logging.conf", disable_existing_loggers=False) class CoAP(object): From e9a08fa1d9e2b06c96841e97309d14fae74e317c Mon Sep 17 00:00:00 2001 From: Michiel De Witte Date: Fri, 2 Mar 2018 15:42:22 +0100 Subject: [PATCH 3/5] set receiver thread as daemon --- coapthon/client/coap.py | 1 + 1 file changed, 1 insertion(+) diff --git a/coapthon/client/coap.py b/coapthon/client/coap.py index 471282e..d2bed4c 100644 --- a/coapthon/client/coap.py +++ b/coapthon/client/coap.py @@ -169,6 +169,7 @@ class CoAP(object): if self._receiver_thread is None or not self._receiver_thread.isAlive(): self._receiver_thread = threading.Thread(target=self.receive_datagram) + self._receiver_thread.daemon = True self._receiver_thread.start() def _start_retransmission(self, transaction, message): From f81f415da8ce6bdde1541a6a2bd70b07e6a38ba6 Mon Sep 17 00:00:00 2001 From: office-gateway Date: Mon, 5 Mar 2018 16:41:11 +0100 Subject: [PATCH 4/5] Let the outbound payload be binary --- coapthon/serializer.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/coapthon/serializer.py b/coapthon/serializer.py index c85445d..f938eeb 100644 --- a/coapthon/serializer.py +++ b/coapthon/serializer.py @@ -219,8 +219,12 @@ class Serializer(object): fmt += "B" values.append(defines.PAYLOAD_MARKER) - fmt += str(len(bytes(payload, "utf-8"))) + "s" - values.append(bytes(payload, "utf-8")) + if isinstance(payload, bytes): + fmt += str(len(payload)) + "s" + values.append(payload) + else: + fmt += str(len(bytes(payload, "utf-8"))) + "s" + values.append(bytes(payload, "utf-8")) # for b in str(payload): # fmt += "c" # values.append(bytes(b, "utf-8")) From 264167e3d1ab2e9dd703bf62fe46c50e7b4cb502 Mon Sep 17 00:00:00 2001 From: Matthieu BRIEDA Date: Wed, 14 Mar 2018 17:24:57 +0100 Subject: [PATCH 5/5] Update Blocklayer to deal with Block1 option On the last block answer, the Client still sent another empty message, it didn't take into account the fact that the previous block was sent with m=0. This commit fixed that for me. --- coapthon/layers/blocklayer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/coapthon/layers/blocklayer.py b/coapthon/layers/blocklayer.py index 451ba3c..8f698a3 100644 --- a/coapthon/layers/blocklayer.py +++ b/coapthon/layers/blocklayer.py @@ -141,10 +141,10 @@ class BlockLayer(object): item.num += 1 item.byte += item.size if len(item.payload) <= item.byte: - m = 0 + item.m = 0 else: - m = 1 - request.block1 = (item.num, m, item.size) + item.m = 1 + request.block1 = (item.num, item.m, item.size) elif transaction.response.block2 is not None: num, m, size = transaction.response.block2