fix: retry Block2 GETs on timeout, track server-negotiated block size
Ported from localthings fork: bounded per-block retransmission (_BLOCK_MAX_ATTEMPTS/_BLOCK_ACK_TIMEOUT) instead of a single attempt per block, SZX renegotiation tracking, and a bugfix moving conn.send(datagram) inside the try that catches its own SSL errors.
This commit is contained in:
+39
-21
@@ -48,6 +48,12 @@ _OCF_ROOT_CA = str(Path(__file__).parent / 'ocf_root_ca.pem')
|
|||||||
# new resources and field semantics; otherwise quiet.
|
# new resources and field semantics; otherwise quiet.
|
||||||
DEBUG_BRIDGE = os.environ.get('DEBUG_BRIDGE') == '1'
|
DEBUG_BRIDGE = os.environ.get('DEBUG_BRIDGE') == '1'
|
||||||
|
|
||||||
|
# Per-block retransmission: send up to this many times before giving up.
|
||||||
|
# Each attempt waits at most _BLOCK_ACK_TIMEOUT seconds (capped by the
|
||||||
|
# overall deadline). Matches RFC 7252 CON retransmit behaviour.
|
||||||
|
_BLOCK_MAX_ATTEMPTS = 3
|
||||||
|
_BLOCK_ACK_TIMEOUT = 4.0
|
||||||
|
|
||||||
|
|
||||||
class DtlsCoapSession:
|
class DtlsCoapSession:
|
||||||
"""Single sustained DTLS-CoAP session.
|
"""Single sustained DTLS-CoAP session.
|
||||||
@@ -252,8 +258,8 @@ class DtlsCoapSession:
|
|||||||
with self._send_lock:
|
with self._send_lock:
|
||||||
if self.conn is None:
|
if self.conn is None:
|
||||||
raise ConnectionError("DTLS session closed")
|
raise ConnectionError("DTLS session closed")
|
||||||
self.conn.send(datagram)
|
|
||||||
try:
|
try:
|
||||||
|
self.conn.send(datagram)
|
||||||
while True:
|
while True:
|
||||||
o = self.conn.bio_read(65535)
|
o = self.conn.bio_read(65535)
|
||||||
if not o:
|
if not o:
|
||||||
@@ -399,28 +405,37 @@ class DtlsCoapSession:
|
|||||||
last_code = None
|
last_code = None
|
||||||
last_opts = []
|
last_opts = []
|
||||||
deadline = time.time() + timeout
|
deadline = time.time() + timeout
|
||||||
|
szx = BLOCK_SZX # server may negotiate down; track per-transfer
|
||||||
while True:
|
while True:
|
||||||
ev = threading.Event()
|
if num > 0:
|
||||||
|
self.pace()
|
||||||
container = {}
|
container = {}
|
||||||
self._pending[tok] = (ev, container)
|
for attempt in range(_BLOCK_MAX_ATTEMPTS):
|
||||||
try:
|
ev = threading.Event()
|
||||||
mid = self._next_mid()
|
container = {}
|
||||||
opts = [(URI_PATH, s.encode()) for s in path_segs]
|
self._pending[tok] = (ev, container)
|
||||||
for q in query:
|
try:
|
||||||
opts.append((URI_QUERY, q.encode()))
|
mid = self._next_mid()
|
||||||
opts.append((ACCEPT, CF_CBOR))
|
opts = [(URI_PATH, s.encode()) for s in path_segs]
|
||||||
if num > 0:
|
for q in query:
|
||||||
opts.append((BLOCK2, block_value(num, 0, BLOCK_SZX)))
|
opts.append((URI_QUERY, q.encode()))
|
||||||
self._send_dgram(
|
opts.append((ACCEPT, CF_CBOR))
|
||||||
build_coap(TYPE_CON, METHOD_GET, mid, tok, opts))
|
if num > 0:
|
||||||
wait = max(0.1, deadline - time.time())
|
opts.append((BLOCK2, block_value(num, 0, szx)))
|
||||||
if not ev.wait(wait):
|
self._send_dgram(
|
||||||
raise TimeoutError(
|
build_coap(TYPE_CON, METHOD_GET, mid, tok, opts))
|
||||||
f"GET /{'/'.join(path_segs)} block {num} timeout")
|
per_wait = min(_BLOCK_ACK_TIMEOUT,
|
||||||
if 'err' in container:
|
max(0.1, deadline - time.time()))
|
||||||
raise ConnectionError(container['err'])
|
if ev.wait(per_wait):
|
||||||
finally:
|
break # got a response
|
||||||
self._pending.pop(tok, None)
|
remaining = deadline - time.time()
|
||||||
|
if remaining <= 0 or attempt == _BLOCK_MAX_ATTEMPTS - 1:
|
||||||
|
raise TimeoutError(
|
||||||
|
f"GET /{'/'.join(path_segs)} block {num} timeout")
|
||||||
|
finally:
|
||||||
|
self._pending.pop(tok, None)
|
||||||
|
if 'err' in container:
|
||||||
|
raise ConnectionError(container['err'])
|
||||||
|
|
||||||
code = container['code']
|
code = container['code']
|
||||||
payload = container['payload']
|
payload = container['payload']
|
||||||
@@ -437,6 +452,9 @@ class DtlsCoapSession:
|
|||||||
if b2:
|
if b2:
|
||||||
bv = int.from_bytes(b2[0], 'big')
|
bv = int.from_bytes(b2[0], 'big')
|
||||||
more = (bv >> 3) & 1
|
more = (bv >> 3) & 1
|
||||||
|
server_szx = bv & 0x07
|
||||||
|
if server_szx != szx:
|
||||||
|
szx = server_szx
|
||||||
if not more:
|
if not more:
|
||||||
break
|
break
|
||||||
num += 1
|
num += 1
|
||||||
|
|||||||
Reference in New Issue
Block a user