Added max_response_size parameter to Link request API

This commit is contained in:
Mark Qvist
2026-07-24 15:10:03 +02:00
parent 1ecf845cea
commit 6a761a768c
2 changed files with 54 additions and 27 deletions
+2 -2
View File
@@ -368,9 +368,9 @@ class Destination:
def set_max_request_size(self, max_request_size):
"""
Registers a request handler.
Sets the maximum accepted request size for registered request handlers.
:param max_request_size: The path for the request handler to be registered.
:param max_request_size: The maximum accepted request size in bytes, as an integer.
:raises: ``TypeError`` or ``ValueError`` if any of the argument is invalid.
"""
try: max_request_size = int(max_request_size)
+52 -25
View File
@@ -470,7 +470,8 @@ class Link:
self.had_outbound()
def request(self, path, data = None, response_callback = None, failed_callback = None, progress_callback = None, timeout = None):
def request(self, path, data=None, response_callback=None, failed_callback=None, progress_callback=None,
timeout=None, max_response_size=None):
"""
Sends a request to the remote peer.
@@ -478,6 +479,7 @@ class Link:
:param response_callback: An optional function or method with the signature *response_callback(request_receipt)* to be called when a response is received. See the :ref:`Request Example<example-request>` for more info.
:param failed_callback: An optional function or method with the signature *failed_callback(request_receipt)* to be called when a request fails. See the :ref:`Request Example<example-request>` for more info.
:param progress_callback: An optional function or method with the signature *progress_callback(request_receipt)* to be called when progress is made receiving the response. Progress can be accessed as a float between 0.0 and 1.0 by the *request_receipt.progress* property.
:param max_response_size: An optional maximum accepted response size, in bytes as an integer.
:param timeout: An optional timeout in seconds for the request. If *None* is supplied it will be calculated based on link RTT.
:returns: A :ref:`RNS.RequestReceipt<api-requestreceipt>` instance if the request was sent, or *False* if it was not.
"""
@@ -495,18 +497,17 @@ class Link:
if packet_receipt == False: return False
else:
packet_receipt.set_timeout(timeout)
return RequestReceipt(self, packet_receipt = packet_receipt, response_callback = response_callback,
failed_callback = failed_callback, progress_callback = progress_callback,
timeout = timeout, request_size = len(packed_request))
return RequestReceipt(self, packet_receipt=packet_receipt, response_callback=response_callback,
failed_callback=failed_callback, progress_callback=progress_callback, timeout=timeout,
request_size=len(packed_request), max_response_size=max_response_size)
else:
request_id = RNS.Identity.truncated_hash(packed_request)
RNS.log("Sending request "+RNS.prettyhexrep(request_id)+" as resource.", RNS.LOG_DEBUG)
request_resource = RNS.Resource(packed_request, self, request_id = request_id, is_response = False, timeout = timeout)
return RequestReceipt(self, resource = request_resource, response_callback = response_callback,
failed_callback = failed_callback, progress_callback = progress_callback,
timeout = timeout, request_size = len(packed_request))
return RequestReceipt(self, resource=request_resource, response_callback=response_callback,
failed_callback=failed_callback, progress_callback=progress_callback, timeout=timeout,
request_size=len(packed_request), max_response_size=max_response_size)
def update_mdu(self):
self.mdu = self.mtu - RNS.Reticulum.HEADER_MAXSIZE - RNS.Reticulum.IFAC_MIN_SIZE
@@ -853,19 +854,26 @@ class Link:
identity_string = str(self.get_remote_identity()) if self.get_remote_identity() != None else "<Unknown>"
RNS.log("Request "+RNS.prettyhexrep(request_id)+" from "+identity_string+" not allowed for: "+str(path), RNS.LOG_DEBUG)
def handle_response(self, request_id, response_data, response_size, response_transfer_size, metadata=None, update_sizes=False):
def handle_response(self, request_id, response_data, response_size, response_transfer_size, metadata=None, update_sizes=False, check_size=False):
if self.status == Link.ACTIVE:
remove = None
for pending_request in self.pending_requests:
if pending_request.request_id == request_id:
if not check_size or pending_request.max_response_size == None: size_ok = True
else: size_ok = response_size <= pending_request.max_response_size
remove = pending_request
try:
if update_sizes:
pending_request.response_size = response_size
if pending_request.response_transfer_size == None: pending_request.response_transfer_size = 0
pending_request.response_transfer_size += response_transfer_size
pending_request.response_received(response_data, metadata)
if size_ok: pending_request.response_received(response_data, metadata)
else:
RNS.log(f"Rejected response with excessive size {RNS.prettysize(response_size)} on {self}", RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None
pending_request.response_rejected()
except Exception as e: RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
break
@@ -999,7 +1007,7 @@ class Link:
request_id = unpacked_response[0]
response_data = unpacked_response[1]
transfer_size = len(umsgpack.packb(response_data))-2
def job(): self.handle_response(request_id, response_data, transfer_size, transfer_size, update_sizes=True)
def job(): self.handle_response(request_id, response_data, transfer_size, transfer_size, update_sizes=True, check_size=True)
threading.Thread(target=job, daemon=True).start()
self.__update_phy_stats(packet, query_shared=True)
except Exception as e: RNS.log("Error occurred while handling response. The contained exception was: "+str(e), RNS.LOG_ERROR)
@@ -1025,21 +1033,28 @@ class Link:
if size_ok: RNS.Resource.accept(packet, callback=self.request_resource_concluded)
else:
RNS.Resource.reject(packet)
RNS.log(f"Ignored request with excessive size {RNS.prettysize(RNS.ResourceAdvertisement.read_size(packet))} on {self.destination}", RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None
RNS.log(f"Rejected request with excessive size {RNS.prettysize(RNS.ResourceAdvertisement.read_size(packet))} on {self}", RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None
elif RNS.ResourceAdvertisement.is_response(packet):
request_id = RNS.ResourceAdvertisement.read_request_id(packet)
for pending_request in self.pending_requests:
if pending_request.request_id == request_id:
response_resource = RNS.Resource.accept(packet, callback=self.response_resource_concluded, progress_callback=pending_request.response_resource_progress, request_id = request_id)
if response_resource != None:
if pending_request.response_size == None:
pending_request.response_size = RNS.ResourceAdvertisement.read_size(packet)
if pending_request.response_transfer_size == None:
pending_request.response_transfer_size = 0
pending_request.response_transfer_size += RNS.ResourceAdvertisement.read_transfer_size(packet)
if pending_request.started_at == None:
pending_request.started_at = time.time()
pending_request.response_resource_progress(response_resource)
if pending_request.max_response_size == None: size_ok = True
else: size_ok = RNS.ResourceAdvertisement.read_size(packet) <= pending_request.max_response_size
if not size_ok:
RNS.Resource.reject(packet)
pending_request.response_rejected()
RNS.log(f"Rejected response with excessive size {RNS.prettysize(RNS.ResourceAdvertisement.read_size(packet))} on {self}", RNS.LOG_DEBUG) if RNS.sl(RNS.LOG_DEBUG) else None
else:
response_resource = RNS.Resource.accept(packet, callback=self.response_resource_concluded, progress_callback=pending_request.response_resource_progress, request_id = request_id)
if response_resource != None:
if pending_request.response_size == None:
pending_request.response_size = RNS.ResourceAdvertisement.read_size(packet)
if pending_request.response_transfer_size == None:
pending_request.response_transfer_size = 0
pending_request.response_transfer_size += RNS.ResourceAdvertisement.read_transfer_size(packet)
if pending_request.started_at == None:
pending_request.started_at = time.time()
pending_request.response_resource_progress(response_resource)
elif self.resource_strategy == Link.ACCEPT_NONE: pass
elif self.resource_strategy == Link.ACCEPT_APP:
if self.callbacks.resource != None:
@@ -1301,7 +1316,9 @@ class RequestReceipt():
RECEIVING = 0x03
READY = 0x04
def __init__(self, link, packet_receipt = None, resource = None, response_callback = None, failed_callback = None, progress_callback = None, timeout = None, request_size = None):
def __init__(self, link, packet_receipt=None, resource=None, response_callback=None, failed_callback=None,
progress_callback=None, timeout=None, request_size=None, max_response_size=None):
self.packet_receipt = packet_receipt
self.resource = resource
self.started_at = None
@@ -1328,6 +1345,7 @@ class RequestReceipt():
self.progress = 0
self.concluded_at = None
self.response_concluded_at = None
self.max_response_size = max_response_size
if timeout != None:
self.timeout = timeout
@@ -1358,8 +1376,7 @@ class RequestReceipt():
self.link.pending_requests.remove(self)
if self.callbacks.failed != None:
try:
self.callbacks.failed(self)
try: self.callbacks.failed(self)
except Exception as e:
RNS.log("Error while executing request failed callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
@@ -1382,6 +1399,16 @@ class RequestReceipt():
try: self.callbacks.failed(self)
except Exception as e: RNS.log("Error while executing request timed out callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
def response_rejected(self):
if self in self.link.pending_requests and self.status == RequestReceipt.DELIVERED:
self.status = RequestReceipt.FAILED
self.concluded_at = time.time()
self.link.pending_requests.remove(self)
if self.callbacks.failed != None:
try: self.callbacks.failed(self)
except Exception as e: RNS.log("Error while executing request timed out callback from "+str(self)+". The contained exception was: "+str(e), RNS.LOG_ERROR)
def response_resource_progress(self, resource):
if resource != None:
if not self.status == RequestReceipt.FAILED: