Merge remote-tracking branch 'refs/remotes/Tanganelli/master' into development
This commit is contained in:
commit
209d4683c9
11 changed files with 64 additions and 22 deletions
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
response = self.queue.get(block=True, timeout=timeout)
|
while True:
|
||||||
|
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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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]
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import time
|
import time
|
||||||
from coapthon import defines
|
from coapthon import defines
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue