Source code for edri.switch.receiver
from logging import getLogger
from queue import Queue
from threading import Thread
from uuid import UUID
from edri.protocol.delivery import decode_delivery
from edri.protocol.errors import ProtocolError
from edri.protocol.frame import FrameType
from edri.switch.connection import Connection
from edri.switch.forwarder import ForwardMessage, ForwardType
[docs]
class Receiver(Thread):
def __init__(self, forwarder_queue: Queue[ForwardMessage], router_id: UUID,
connection: Connection, connections: dict[UUID, Connection]) -> None:
super().__init__(daemon=True, name=f"switch-receiver-{router_id}")
self.forwarder_queue = forwarder_queue
self.connection = connection
self.connections = connections
self.router_id = router_id
self.logger = getLogger(f"{__name__}.{router_id}")
[docs]
def run(self) -> None:
channel = self.connection.channel
if channel is None:
return
try:
while not self.connection.stop.is_set():
frame = channel.receive()
# An evicted session must not inject more events or ACKs.
if self.connections.get(self.router_id) is not self.connection:
return
if frame.type == FrameType.ACK:
if frame.payload:
raise ValueError("ACK frames must have an empty payload")
delivery_id = UUID(bytes=frame.target)
if not self.connection.mailbox.ack(delivery_id):
self.logger.debug("Stale/unknown ACK: %s", delivery_id)
elif frame.type == FrameType.DEMANDS:
self.connection.demands = frame.payload
self.forwarder_queue.put(ForwardMessage(self.router_id, ForwardType.OTHERS, frame.payload))
elif frame.type == FrameType.LAST_EVENTS:
self.forwarder_queue.put(ForwardMessage(self.router_id, ForwardType.OTHERS, frame.payload))
elif frame.type == FrameType.EVENT:
delivery = decode_delivery(frame.payload)
self.forwarder_queue.put(ForwardMessage(
UUID(bytes=frame.target), ForwardType.SPECIFIC, delivery.payload,
self.router_id, delivery.id, self.connection,
))
else:
self.logger.error("Unexpected frame type: %s", frame.type.name)
return
except (ProtocolError, ValueError) as e:
self.logger.error("Invalid connection frame: %s", e)
except (ConnectionError, OSError):
self.logger.info("Router disconnected: %s", self.router_id)
finally:
self.connection.disconnect()