1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//! Session management module
//!
//! Handles client sessions with different routing modes.
//!
//! This module handles the lifecycle of a client connection, including
//! command processing, authentication interception, and data transfer.
//!
//! # Quick Start
//!
//! ## Basic Stateful Session (1:1 mapping)
//!
//! ```no_run
//! use std::net::SocketAddr;
//! use std::sync::Arc;
//! use nntp_proxy::session::ClientSession;
//! use nntp_proxy::pool::BufferPool;
//! use nntp_proxy::types::BufferSize;
//! use nntp_proxy::auth::AuthHandler;
//! use nntp_proxy::metrics::MetricsCollector;
//!
//! # fn example() -> anyhow::Result<()> {
//! let client_addr: SocketAddr = "127.0.0.1:50000".parse()?;
//! let buffer_pool = BufferPool::new(BufferSize::try_new(8192)?, 10);
//! let auth = Arc::new(AuthHandler::new(None, None)?);
//! let metrics = MetricsCollector::new(1);
//!
//! // Create a simple 1:1 session (no load balancing)
//! let session = ClientSession::new(client_addr.into(), buffer_pool, auth, metrics);
//! assert_eq!(session.mode(), nntp_proxy::session::SessionMode::Stateful);
//! # Ok(())
//! # }
//! ```
//!
//! ## Per-Command Routing (Load Balancing)
//!
//! ```no_run
//! use std::sync::Arc;
//! use std::net::SocketAddr;
//! use nntp_proxy::session::ClientSession;
//! use nntp_proxy::pool::BufferPool;
//! use nntp_proxy::router::BackendSelector;
//! use nntp_proxy::config::RoutingMode;
//! use nntp_proxy::types::BufferSize;
//! use nntp_proxy::auth::AuthHandler;
//! use nntp_proxy::metrics::MetricsCollector;
//!
//! # fn example() -> anyhow::Result<()> {
//! let addr: SocketAddr = "127.0.0.1:50000".parse()?;
//! let buffer_pool = BufferPool::new(BufferSize::try_new(8192)?, 10);
//! let router = Arc::new(BackendSelector::new());
//! let auth = Arc::new(AuthHandler::new(None, None)?);
//! let metrics = MetricsCollector::new(1);
//!
//! // Each command routed to potentially different backend
//! let session = ClientSession::builder(addr.into(), buffer_pool, auth, metrics)
//! .with_router(router)
//! .with_routing_mode(RoutingMode::PerCommand)
//! .build();
//!
//! assert!(session.is_per_command_routing());
//! # Ok(())
//! # }
//! ```
//!
//! ## Hybrid Mode (Best of Both Worlds)
//!
//! ```no_run
//! use std::sync::Arc;
//! use std::net::SocketAddr;
//! use nntp_proxy::session::ClientSession;
//! use nntp_proxy::pool::BufferPool;
//! use nntp_proxy::router::BackendSelector;
//! use nntp_proxy::config::RoutingMode;
//! use nntp_proxy::types::BufferSize;
//! use nntp_proxy::auth::AuthHandler;
//! use nntp_proxy::metrics::MetricsCollector;
//!
//! # fn example() -> anyhow::Result<()> {
//! let addr: SocketAddr = "127.0.0.1:50000".parse()?;
//! let buffer_pool = BufferPool::new(BufferSize::try_new(8192)?, 10);
//! let router = Arc::new(BackendSelector::new());
//! let auth = Arc::new(AuthHandler::new(None, None)?);
//! let metrics = MetricsCollector::new(1);
//!
//! // Starts in per-command mode, auto-switches to stateful when needed
//! let session = ClientSession::builder(addr.into(), buffer_pool, auth, metrics)
//! .with_router(router)
//! .with_routing_mode(RoutingMode::Hybrid)
//! .build();
//!
//! // Initially per-command for load balancing
//! assert_eq!(session.mode(), nntp_proxy::session::SessionMode::PerCommand);
//! // Will switch to Stateful automatically on GROUP, NEXT, LAST, etc.
//! # Ok(())
//! # }
//! ```
//!
//! ## With Caching
//!
//! ```no_run
//! use std::sync::Arc;
//! use std::net::SocketAddr;
//! use std::time::Duration;
//! use nntp_proxy::session::ClientSession;
//! use nntp_proxy::pool::BufferPool;
//! use nntp_proxy::router::BackendSelector;
//! use nntp_proxy::config::RoutingMode;
//! use nntp_proxy::types::BufferSize;
//! use nntp_proxy::auth::AuthHandler;
//! use nntp_proxy::metrics::MetricsCollector;
//! use nntp_proxy::cache::UnifiedCache;
//!
//! # fn example() -> anyhow::Result<()> {
//! let addr: SocketAddr = "127.0.0.1:50000".parse()?;
//! let buffer_pool = BufferPool::new(BufferSize::try_new(8192)?, 10);
//! let router = Arc::new(BackendSelector::new());
//! let auth = Arc::new(AuthHandler::new(None, None)?);
//! let metrics = MetricsCollector::new(2); // 2 backends
//! let cache = Arc::new(UnifiedCache::memory(1000, Duration::from_secs(3600)));
//!
//! // Full-featured session with caching
//! let session = ClientSession::builder(addr.into(), buffer_pool, auth, metrics)
//! .with_router(router)
//! .with_routing_mode(RoutingMode::Hybrid)
//! .with_cache(cache)
//! .build();
//! # Ok(())
//! # }
//! ```
//!
//! # Architecture Overview
//!
//! ## Three Operating Modes
//!
//! 1. **Stateful (1:1) Mode** - `run_stateful_proxy_loop()`
//! - One client maps to one backend connection for entire session
//! - Lowest latency, simplest model
//! - Used when `routing_mode` = Stateful
//!
//! 2. **Per-Command Mode (Stateless)** - `handle_per_command_routing()`
//! - Each command is independently routed to potentially different backends
//! - Enables load balancing across multiple backend servers
//! - Rejects stateful commands (GROUP, NEXT, LAST, etc.)
//! - Used when `routing_mode` = `PerCommand`
//!
//! 3. **Hybrid Mode** - `handle_per_command_routing()` + dynamic switching
//! - Starts in per-command mode (stateless) for load balancing
//! - Automatically switches to stateful mode when stateful command detected
//! - Best of both worlds: load balancing + stateful command support
//! - Used when `routing_mode` = Hybrid
//!
//! ## Key Functions
//!
//! - `run_stateful_proxy_loop()` - **PERFORMANCE CRITICAL HOT PATH**
//! - Bidirectional streaming with `tokio::select`! for concurrent I/O
//! - Used by both stateful mode and hybrid mode after switching
//!
//! - `switch_to_stateful_mode()` - Hybrid mode transition
//! - Acquires dedicated backend connection
//! - Hands off to stateful proxy loop
//!
//! - `route_and_execute_request()` - Per-command orchestration
//! - Routes command to backend
//! - Handles connection pool management
//! - Distinguishes backend errors from client disconnects
pub
pub
pub
pub
pub
pub
pub
pub
pub use AuthState;
pub use format_hex_preview;
pub use ;
pub use MetricsRecorder;
pub use ;
pub use SessionError;
pub use SharedClientWriter;
pub use SessionLoopState;