"""I2CP session state machine.""" import enum from typing import Callable class SessionState(enum.Enum): INIT = 0 OPENING = 1 OPEN = 2 CLOSING = 3 CLOSED = 4 ERROR = 5 class SessionId: """2-byte session identifier.""" def __init__(self, value: int): if value < 0 or value > 0xFFFF: raise ValueError(f"SessionId must be 0..65535, got {value}") self._value = value def __int__(self) -> int: return self._value def __eq__(self, other) -> bool: if not isinstance(other, SessionId): return NotImplemented return self._value == other._value def __hash__(self) -> int: return hash(self._value) def __repr__(self) -> str: return f"SessionId({self._value})" class I2CPSession: """I2CP session state machine.""" def __init__(self): self.state = SessionState.INIT self.session_id: SessionId | None = None self.destination_data: bytes | None = None self.config = None self.error_message: str | None = None self._message_handlers: list[Callable] = [] def open(self, destination_data: bytes, config): if self.state != SessionState.INIT: raise RuntimeError(f"Cannot open from state {self.state}") self.destination_data = destination_data self.config = config self.state = SessionState.OPENING def confirm_open(self, session_id: SessionId): if self.state != SessionState.OPENING: raise RuntimeError(f"Cannot confirm_open from state {self.state}") self.session_id = session_id self.state = SessionState.OPEN def close(self): if self.state != SessionState.OPEN: raise RuntimeError(f"Cannot close from state {self.state}") self.state = SessionState.CLOSING def confirm_close(self): if self.state != SessionState.CLOSING: raise RuntimeError(f"Cannot confirm_close from state {self.state}") self.state = SessionState.CLOSED def set_error(self, message: str): self.error_message = message self.state = SessionState.ERROR def on_message(self, handler: Callable): self._message_handlers.append(handler) def deliver_message(self, payload: bytes): for handler in self._message_handlers: handler(payload)