A Python port of the Invisible Internet Project (I2P)
at main 112 lines 3.0 kB view raw
1"""Main I2P Router — lifecycle management and state machine. 2 3Ported from net.i2p.router.Router. 4""" 5 6from __future__ import annotations 7 8import enum 9import time 10from dataclasses import asdict 11 12from i2p_router.config import RouterConfig 13from i2p_router.core import RouterContext 14 15 16class RouterState(enum.Enum): 17 """Router lifecycle states.""" 18 19 STOPPED = "stopped" 20 STARTING = "starting" 21 RUNNING = "running" 22 SHUTTING_DOWN = "shutting_down" 23 24 25class Router: 26 """Main I2P router instance. 27 28 Manages the router lifecycle (start, run, shutdown) and owns 29 the RouterContext which wires together all subsystems. 30 """ 31 32 def __init__(self, config: RouterConfig | None = None) -> None: 33 self._config = config or RouterConfig() 34 self._state = RouterState.STOPPED 35 self._context: RouterContext | None = None 36 self._started_at: float | None = None 37 38 @property 39 def state(self) -> RouterState: 40 """Current router state.""" 41 return self._state 42 43 @property 44 def config(self) -> RouterConfig: 45 """Router configuration.""" 46 return self._config 47 48 @property 49 def uptime_seconds(self) -> float: 50 """Seconds since the router was started, or 0.0 if stopped.""" 51 if self._started_at is None: 52 return 0.0 53 return time.monotonic() - self._started_at 54 55 def start(self) -> None: 56 """Start the router. 57 58 1. If already RUNNING, this is a no-op (idempotent). 59 2. Validate config. 60 3. Transition to STARTING. 61 4. Create RouterContext and initialize subsystems. 62 5. Transition to RUNNING. 63 64 Raises 65 ------ 66 ValueError 67 If the configuration is invalid. State remains STOPPED. 68 """ 69 if self._state == RouterState.RUNNING: 70 return 71 72 # Validate before changing state 73 self._config.validate() 74 75 self._state = RouterState.STARTING 76 77 # Create the router context (wires all subsystems) 78 self._context = RouterContext() 79 self._started_at = time.monotonic() 80 81 self._state = RouterState.RUNNING 82 83 def shutdown(self) -> None: 84 """Gracefully shut down the router. 85 86 If already STOPPED, this is a no-op. 87 Transitions through SHUTTING_DOWN to STOPPED. 88 """ 89 if self._state == RouterState.STOPPED: 90 return 91 92 self._state = RouterState.SHUTTING_DOWN 93 94 # Clean up 95 self._context = None 96 self._started_at = None 97 98 self._state = RouterState.STOPPED 99 100 def get_status(self) -> dict: 101 """Return router status including state, uptime, and config. 102 103 Returns 104 ------- 105 dict 106 Status dictionary with state, uptime_seconds, and config fields. 107 """ 108 return { 109 "state": self._state.value, 110 "uptime_seconds": self.uptime_seconds, 111 "config": asdict(self._config), 112 }