A Python port of the Invisible Internet Project (I2P)
at main 94 lines 3.0 kB view raw
1"""SAM primary session -- multi-session management on single I2P tunnel. 2 3Ported from net.i2p.sam.SAMv3Handler PRIMARY session support. 4""" 5 6from __future__ import annotations 7 8import logging 9from typing import TYPE_CHECKING 10 11if TYPE_CHECKING: 12 from i2p_sam.sessions_db import SessionRecord 13 14logger = logging.getLogger(__name__) 15 16 17class PrimarySession: 18 """Manages multiple subsessions (STREAM, DATAGRAM, RAW) on one I2P session. 19 20 A PRIMARY session allows a single I2P destination to be used for 21 multiple communication styles simultaneously. Subsessions are keyed 22 by their FROM_PORT to allow multiplexing. 23 """ 24 25 def __init__(self, nickname: str, destination_b64: str) -> None: 26 self._nickname = nickname 27 self._destination_b64 = destination_b64 28 self._subsessions: dict[str, "SessionRecord"] = {} 29 30 async def add_subsession(self, sub_key: str, style: str, 31 handler: object) -> "SessionRecord": 32 """Add a subsession under this primary session. 33 34 Args: 35 sub_key: Subsession identifier (typically FROM_PORT value). 36 style: Communication style (STREAM, DATAGRAM, RAW). 37 handler: The SAMHandler managing this subsession. 38 39 Returns: 40 The created SessionRecord. 41 42 Raises: 43 ValueError: If sub_key already exists. 44 """ 45 from i2p_sam.sessions_db import SessionRecord 46 from i2p_sam.utils import generate_transient_destination 47 48 if sub_key in self._subsessions: 49 raise ValueError(f"Subsession {sub_key} already exists") 50 51 raw, b64 = generate_transient_destination() 52 record = SessionRecord( 53 nickname=f"{self._nickname}:{sub_key}", 54 style=style, 55 destination=raw, 56 destination_b64=b64, 57 handler=handler, # type: ignore[arg-type] 58 ) 59 self._subsessions[sub_key] = record 60 logger.info("Added subsession %s (style=%s) to primary %s", 61 sub_key, style, self._nickname) 62 return record 63 64 async def remove_subsession(self, sub_key: str) -> bool: 65 """Remove a subsession. 66 67 Args: 68 sub_key: Subsession identifier to remove. 69 70 Returns: 71 True if removed, False if not found. 72 """ 73 if sub_key in self._subsessions: 74 del self._subsessions[sub_key] 75 logger.info("Removed subsession %s from primary %s", 76 sub_key, self._nickname) 77 return True 78 return False 79 80 def get_subsession(self, sub_key: str) -> "SessionRecord | None": 81 """Get a subsession by key. 82 83 Args: 84 sub_key: Subsession identifier. 85 86 Returns: 87 The SessionRecord, or None if not found. 88 """ 89 return self._subsessions.get(sub_key) 90 91 @property 92 def subsession_count(self) -> int: 93 """Number of active subsessions.""" 94 return len(self._subsessions)