Merge remote-tracking branch 'refs/remotes/Tanganelli/master' into development

This commit is contained in:
Björn Freise 2021-01-18 09:26:17 +01:00
commit 209d4683c9
11 changed files with 64 additions and 22 deletions

View file

@ -65,6 +65,13 @@ class CoAP(object):
self._receiver_thread = None self._receiver_thread = None
def purge_transactions(self, timeout_time=defines.EXCHANGE_LIFETIME):
"""
Clean old transactions
"""
self._messageLayer.purge(timeout_time)
def close(self): def close(self):
""" """
Stop the client. Stop the client.
@ -96,16 +103,21 @@ class CoAP(object):
assert isinstance(c, int) assert isinstance(c, int)
self._currentMID = c self._currentMID = c
def send_message(self, message): def send_message(self, message, no_response=False):
""" """
Prepare a message to send on the UDP socket. Eventually set retransmissions. Prepare a message to send on the UDP socket. Eventually set retransmissions.
:param message: the message to send :param message: the message to send
:param no_response: whether to await a response from the request
""" """
if isinstance(message, Request): if isinstance(message, Request):
request = self._requestLayer.send_request(message) request = self._requestLayer.send_request(message)
request = self._observeLayer.send_request(request) request = self._observeLayer.send_request(request)
request = self._blockLayer.send_request(request) request = self._blockLayer.send_request(request)
if no_response:
# don't add the send message to the message layer transactions
self.send_datagram(request)
return
transaction = self._messageLayer.send_request(request) transaction = self._messageLayer.send_request(request)
self.send_datagram(transaction.request) self.send_datagram(transaction.request)
if transaction.request.type == defines.Types["CON"]: if transaction.request.type == defines.Types["CON"]:
@ -238,7 +250,7 @@ class CoAP(object):
while not self.stopped.isSet(): while not self.stopped.isSet():
self._socket.settimeout(0.1) self._socket.settimeout(0.1)
try: try:
datagram, addr = self._socket.recvfrom(1152) datagram, addr = self._socket.recvfrom(1500)
except socket.timeout: # pragma: no cover except socket.timeout: # pragma: no cover
continue continue
except Exception as e: # pragma: no cover except Exception as e: # pragma: no cover

View file

@ -223,17 +223,26 @@ class HelperClient(object):
:param request: the request to send :param request: the request to send
:param callback: the callback function to invoke upon response :param callback: the callback function to invoke upon response
:param timeout: the timeout of the request :param timeout: the timeout of the request
:param no_response: whether to await a response from the request
:return: the response :return: the response
""" """
if callback is not None: if callback is not None:
thread = threading.Thread(target=self._thread_body, args=(request, callback)) thread = threading.Thread(target=self._thread_body, args=(request, callback))
thread.start() thread.start()
else: else:
self.protocol.send_message(request) self.protocol.send_message(request, no_response=no_response)
if no_response: if no_response:
return return
try: try:
while True:
response = self.queue.get(block=True, timeout=timeout) response = self.queue.get(block=True, timeout=timeout)
if response is not None:
if response.mid == request.mid:
return response
if response.type == defines.Types["NON"]:
return response
else:
return response
except Empty: except Empty:
#if timeout is set #if timeout is set
response = None response = None

View file

@ -125,7 +125,7 @@ class OptionRegistry(object):
LOCATION_QUERY = OptionItem(20,"Location-Query",STRING, True, None) LOCATION_QUERY = OptionItem(20,"Location-Query",STRING, True, None)
BLOCK2 = OptionItem(23, "Block2", INTEGER, False, None) BLOCK2 = OptionItem(23, "Block2", INTEGER, False, None)
BLOCK1 = OptionItem(27, "Block1", INTEGER, False, None) BLOCK1 = OptionItem(27, "Block1", INTEGER, False, None)
SIZE2 = OptionItem(28, "Size2", INTEGER, False, None) SIZE2 = OptionItem(28, "Size2", INTEGER, False, 0)
PROXY_URI = OptionItem(35, "Proxy-Uri", STRING, False, None) PROXY_URI = OptionItem(35, "Proxy-Uri", STRING, False, None)
PROXY_SCHEME = OptionItem(39, "Proxy-Schema", STRING, False, None) PROXY_SCHEME = OptionItem(39, "Proxy-Schema", STRING, False, None)
SIZE1 = OptionItem(60, "Size1", INTEGER, False, None) SIZE1 = OptionItem(60, "Size1", INTEGER, False, None)

View file

@ -60,7 +60,7 @@ class BlockLayer(object):
del transaction.request.block2 del transaction.request.block2
else: else:
# early negotiation # early negotiation
byte = 0 byte = num * size
self._block2_receive[key_token] = BlockItem(byte, num, m, size) self._block2_receive[key_token] = BlockItem(byte, num, m, size)
del transaction.request.block2 del transaction.request.block2
@ -233,6 +233,7 @@ class BlockLayer(object):
self._block2_receive[key_token] = BlockItem(byte, num, m, size) self._block2_receive[key_token] = BlockItem(byte, num, m, size)
# correct m # correct m
m = 0 if ((num * size) + size) > len(transaction.response.payload) else 1 m = 0 if ((num * size) + size) > len(transaction.response.payload) else 1
# add size2 if requested or if payload is bigger than one datagram # add size2 if requested or if payload is bigger than one datagram
@ -240,6 +241,7 @@ class BlockLayer(object):
if (transaction.request.size2 is not None and transaction.request.size2 == 0) or \ if (transaction.request.size2 is not None and transaction.request.size2 == 0) or \
(transaction.response.payload is not None and len(transaction.response.payload) > defines.MAX_PAYLOAD): (transaction.response.payload is not None and len(transaction.response.payload) > defines.MAX_PAYLOAD):
transaction.response.size2 = len(transaction.response.payload) transaction.response.size2 = len(transaction.response.payload)
transaction.response.payload = transaction.response.payload[byte:byte + size] transaction.response.payload = transaction.response.payload[byte:byte + size]
del transaction.response.block2 del transaction.response.block2
transaction.response.block2 = (num, m, size) transaction.response.block2 = (num, m, size)

View file

@ -53,11 +53,12 @@ class ForwardLayer(object):
:rtype : Transaction :rtype : Transaction
:return: the edited transaction :return: the edited transaction
""" """
wkc_resource_is_defined = defines.DISCOVERY_URL in self._server.root
path = str("/" + transaction.request.uri_path) path = str("/" + transaction.request.uri_path)
transaction.response = Response() transaction.response = Response()
transaction.response.destination = transaction.request.source transaction.response.destination = transaction.request.source
transaction.response.token = transaction.request.token transaction.response.token = transaction.request.token
if path == defines.DISCOVERY_URL: if path == defines.DISCOVERY_URL and not wkc_resource_is_defined:
transaction = self._server.resourceLayer.discover(transaction) transaction = self._server.resourceLayer.discover(transaction)
else: else:
new = False new = False

View file

@ -42,17 +42,17 @@ class MessageLayer(object):
self._current_mid %= 65535 self._current_mid %= 65535
return current_mid return current_mid
def purge(self): def purge(self, timeout_time=defines.EXCHANGE_LIFETIME):
for k in list(self._transactions.keys()): for k in list(self._transactions.keys()):
now = time.time() now = time.time()
transaction = self._transactions[k] transaction = self._transactions[k]
if transaction.timestamp + defines.EXCHANGE_LIFETIME < now: if transaction.timestamp + timeout_time < now:
logger.debug("Delete transaction") logger.debug("Delete transaction")
del self._transactions[k] del self._transactions[k]
for k in list(self._transactions_token.keys()): for k in list(self._transactions_token.keys()):
now = time.time() now = time.time()
transaction = self._transactions_token[k] transaction = self._transactions_token[k]
if transaction.timestamp + defines.EXCHANGE_LIFETIME < now: if transaction.timestamp + timeout_time < now:
logger.debug("Delete transaction") logger.debug("Delete transaction")
del self._transactions_token[k] del self._transactions_token[k]

View file

@ -52,11 +52,12 @@ class RequestLayer(object):
:rtype : Transaction :rtype : Transaction
:return: the edited transaction with the response to the request :return: the edited transaction with the response to the request
""" """
wkc_resource_is_defined = defines.DISCOVERY_URL in self._server.root
path = str("/" + transaction.request.uri_path) path = str("/" + transaction.request.uri_path)
transaction.response = Response() transaction.response = Response()
transaction.response.destination = transaction.request.source transaction.response.destination = transaction.request.source
transaction.response.token = transaction.request.token transaction.response.token = transaction.request.token
if path == defines.DISCOVERY_URL: if path == defines.DISCOVERY_URL and not wkc_resource_is_defined:
transaction = self._server.resourceLayer.discover(transaction) transaction = self._server.resourceLayer.discover(transaction)
else: else:
try: try:

View file

@ -119,17 +119,16 @@ class Message(object):
""" """
Set the Token of the message. Set the Token of the message.
:type value: String :type value: Bytes
:param value: the Token :param value: the Token
:raise AttributeError: if value is longer than 256 :raise AttributeError: if value is longer than 256
""" """
if value is None: if value is None:
self._token = value self._token = value
return return
if isinstance(value, int):
value = bytes([value])
if not isinstance(value, bytes): if not isinstance(value, bytes):
value = bytes(value, "utf-8") value = bytes(value)
if len(value) > 256: if len(value) > 256:
raise AttributeError raise AttributeError
self._token = value self._token = value
@ -647,7 +646,6 @@ class Message(object):
""" """
self.del_option_by_number(defines.OptionRegistry.BLOCK2.number) self.del_option_by_number(defines.OptionRegistry.BLOCK2.number)
@property
def size1(self): def size1(self):
value = None value = None
for option in self.options: for option in self.options:
@ -668,14 +666,24 @@ class Message(object):
@property @property
def size2(self): def size2(self):
"""
Get the Size2 option.
:return: the Size2 value
"""
value = None value = None
for option in self.options: for option in self.options:
if option.number == defines.OptionRegistry.SIZE2.number: if option.number == defines.OptionRegistry.SIZE2.number:
value = option.value if option.value is not None else 0 value = option.value
return value return value
@size2.setter @size2.setter
def size2(self, value): def size2(self, value):
"""
Set the Size2 option.
:param value: the Block2 value
"""
option = Option() option = Option()
option.number = defines.OptionRegistry.SIZE2.number option.number = defines.OptionRegistry.SIZE2.number
option.value = value option.value = value
@ -683,6 +691,9 @@ class Message(object):
@size2.deleter @size2.deleter
def size2(self): def size2(self):
"""
Delete the Size2 option.
"""
self.del_option_by_number(defines.OptionRegistry.SIZE2.number) self.del_option_by_number(defines.OptionRegistry.SIZE2.number)
@property @property

View file

@ -129,6 +129,9 @@ class Serializer(object):
return defines.Codes.BAD_REQUEST.number return defines.Codes.BAD_REQUEST.number
except struct.error: except struct.error:
return defines.Codes.BAD_REQUEST.number return defines.Codes.BAD_REQUEST.number
except UnicodeDecodeError as e:
logger.debug(e)
return defines.Codes.BAD_REQUEST.number
@staticmethod @staticmethod
def serialize(message): def serialize(message):
@ -154,10 +157,8 @@ class Serializer(object):
values = [tmp, message.code, message.mid] values = [tmp, message.code, message.mid]
if message.token is not None and tkl > 0: if message.token is not None and tkl > 0:
fmt += "%ss" % tkl
for b in message.token: values.append(message.token)
fmt += "B"
values.append(b)
options = Serializer.as_sorted_list(message.options) # already sorted options = Serializer.as_sorted_list(message.options) # already sorted
lastoptionnumber = 0 lastoptionnumber = 0

View file

@ -201,3 +201,6 @@ class Tree(object):
def __delitem__(self, key): def __delitem__(self, key):
del self.tree[key] del self.tree[key]
def __contains__(self, item):
return item in self.tree

View file

@ -1,3 +1,5 @@
#!/usr/bin/env python
import time import time
from coapthon import defines from coapthon import defines