kindly_guard_server/transport/
http.rs1use anyhow::Result;
17use async_trait::async_trait;
18use axum::{extract::State, routing::post, Json, Router};
19use std::net::SocketAddr;
20use std::sync::atomic::{AtomicBool, Ordering};
21use std::sync::Arc;
22use tokio::sync::{mpsc, Mutex};
23use tower::ServiceBuilder;
24use tower_http::cors::CorsLayer;
25use tracing::{debug, error, info};
26
27use super::{
28 ConnectionInfo, ConnectionStats, Transport, TransportConnection, TransportMessage,
29 TransportStats, TransportType,
30};
31
32use super::Deserialize;
33use super::Serialize;
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct HttpConfig {
38 pub bind_addr: String,
40 pub tls: bool,
42 pub cert_path: Option<String>,
44 pub key_path: Option<String>,
46 pub max_body_size: usize,
48 pub request_timeout_ms: u64,
50}
51
52impl Default for HttpConfig {
53 fn default() -> Self {
54 Self {
55 bind_addr: "127.0.0.1:8080".to_string(),
56 tls: false,
57 cert_path: None,
58 key_path: None,
59 max_body_size: 10 * 1024 * 1024, request_timeout_ms: 30000,
61 }
62 }
63}
64
65pub struct HttpTransport {
67 config: HttpConfig,
68 running: AtomicBool,
69 stats: Arc<Mutex<TransportStats>>,
70 shutdown_tx: Option<mpsc::Sender<()>>,
71}
72
73impl HttpTransport {
74 pub fn new(config: serde_json::Value) -> Result<Self> {
76 let config: HttpConfig = serde_json::from_value(config)?;
77
78 Ok(Self {
79 config,
80 running: AtomicBool::new(false),
81 stats: Arc::new(Mutex::new(TransportStats::default())),
82 shutdown_tx: None,
83 })
84 }
85
86 async fn start_server(&mut self) -> Result<mpsc::Sender<()>> {
88 let (shutdown_tx, mut shutdown_rx) = mpsc::channel(1);
89 let addr: SocketAddr = self.config.bind_addr.parse()?;
90 let stats = self.stats.clone();
91
92 let app = Router::new()
94 .route("/rpc", post(handle_rpc))
95 .layer(
96 ServiceBuilder::new()
97 .layer(CorsLayer::permissive())
98 .into_inner(),
99 )
100 .with_state(stats);
101
102 let listener = tokio::net::TcpListener::bind(&addr).await?;
104 info!("HTTP server listening on {}", addr);
105
106 tokio::spawn(async move {
107 axum::serve(listener, app)
108 .with_graceful_shutdown(async move {
109 let _ = shutdown_rx.recv().await;
110 info!("HTTP server shutting down");
111 })
112 .await
113 .map_err(|e| error!("Server error: {}", e))
114 .ok();
115 });
116
117 Ok(shutdown_tx)
118 }
119}
120
121#[async_trait]
122impl Transport for HttpTransport {
123 fn transport_type(&self) -> TransportType {
124 TransportType::Http
125 }
126
127 async fn start(&mut self) -> Result<()> {
128 if self.running.load(Ordering::Relaxed) {
129 return Err(anyhow::anyhow!("Transport already running"));
130 }
131
132 let shutdown_tx = self.start_server().await?;
133 self.shutdown_tx = Some(shutdown_tx);
134 self.running.store(true, Ordering::Relaxed);
135
136 info!("Started HTTP transport on {}", self.config.bind_addr);
137 Ok(())
138 }
139
140 async fn stop(&mut self) -> Result<()> {
141 if let Some(shutdown_tx) = self.shutdown_tx.take() {
142 let _ = shutdown_tx.send(()).await;
143 }
144
145 self.running.store(false, Ordering::Relaxed);
146 info!("Stopped HTTP transport");
147 Ok(())
148 }
149
150 async fn accept(&mut self) -> Result<Box<dyn TransportConnection>> {
151 if !self.running.load(Ordering::Relaxed) {
152 return Err(anyhow::anyhow!("Transport not running"));
153 }
154
155 let mut stats = self.stats.lock().await;
158 stats.connections_accepted += 1;
159 stats.active_connections += 1;
160 drop(stats);
161
162 Ok(Box::new(HttpConnection::new(
163 "127.0.0.1:12345".to_string(),
164 self.stats.clone(),
165 )))
166 }
167
168 async fn connect(&mut self, address: &str) -> Result<Box<dyn TransportConnection>> {
169 Ok(Box::new(HttpConnection::new(
171 address.to_string(),
172 self.stats.clone(),
173 )))
174 }
175
176 fn is_running(&self) -> bool {
177 self.running.load(Ordering::Relaxed)
178 }
179
180 fn get_stats(&self) -> TransportStats {
181 if let Ok(stats) = self.stats.try_lock() {
182 stats.clone()
183 } else {
184 TransportStats::default()
185 }
186 }
187
188 async fn set_option(&mut self, key: &str, value: serde_json::Value) -> Result<()> {
189 match key {
190 "max_body_size" => {
191 if let Some(size) = value.as_u64() {
192 self.config.max_body_size = size as usize;
193 }
194 },
195 "request_timeout_ms" => {
196 if let Some(timeout) = value.as_u64() {
197 self.config.request_timeout_ms = timeout;
198 }
199 },
200 _ => {},
201 }
202 Ok(())
203 }
204}
205
206pub struct HttpConnection {
208 info: ConnectionInfo,
209 remote_addr: String,
210 connected: AtomicBool,
211 stats: Arc<Mutex<ConnectionStats>>,
212 transport_stats: Arc<Mutex<TransportStats>>,
213 message_queue: Arc<Mutex<mpsc::UnboundedReceiver<TransportMessage>>>,
214 #[allow(dead_code)] message_tx: mpsc::UnboundedSender<TransportMessage>,
216}
217
218impl HttpConnection {
219 fn new(remote_addr: String, transport_stats: Arc<Mutex<TransportStats>>) -> Self {
220 let (message_tx, message_rx) = mpsc::unbounded_channel();
221
222 let info = ConnectionInfo {
223 id: uuid::Uuid::new_v4().to_string(),
224 transport_type: TransportType::Http,
225 client_id: None,
226 remote_addr: Some(remote_addr.clone()),
227 connected_at: chrono::Utc::now(),
228 security_info: None, };
230
231 Self {
232 info,
233 remote_addr,
234 connected: AtomicBool::new(true),
235 stats: Arc::new(Mutex::new(ConnectionStats::default())),
236 transport_stats,
237 message_queue: Arc::new(Mutex::new(message_rx)),
238 message_tx,
239 }
240 }
241}
242
243#[async_trait]
244impl TransportConnection for HttpConnection {
245 fn connection_info(&self) -> &ConnectionInfo {
246 &self.info
247 }
248
249 async fn send(&mut self, message: TransportMessage) -> Result<()> {
250 if !self.connected.load(Ordering::Relaxed) {
251 return Err(anyhow::anyhow!("Connection closed"));
252 }
253
254 debug!("HTTP send to {}: {}", self.remote_addr, message.id);
256
257 let mut stats = self.stats.lock().await;
258 stats.messages_sent += 1;
259 stats.bytes_sent += serde_json::to_vec(&message.payload)?.len() as u64;
260 drop(stats);
261
262 let mut transport_stats = self.transport_stats.lock().await;
263 transport_stats.messages_sent += 1;
264 drop(transport_stats);
265
266 Ok(())
267 }
268
269 async fn receive(&mut self) -> Result<Option<TransportMessage>> {
270 if !self.connected.load(Ordering::Relaxed) {
271 return Ok(None);
272 }
273
274 let mut queue = self.message_queue.lock().await;
276 match queue.recv().await {
277 Some(message) => {
278 let mut stats = self.stats.lock().await;
279 stats.messages_received += 1;
280 drop(stats);
281
282 let mut transport_stats = self.transport_stats.lock().await;
283 transport_stats.messages_received += 1;
284 drop(transport_stats);
285
286 Ok(Some(message))
287 },
288 None => Ok(None),
289 }
290 }
291
292 async fn close(&mut self) -> Result<()> {
293 self.connected.store(false, Ordering::Relaxed);
294
295 let mut transport_stats = self.transport_stats.lock().await;
296 transport_stats.active_connections = transport_stats.active_connections.saturating_sub(1);
297 drop(transport_stats);
298
299 info!("Closed HTTP connection to {}", self.remote_addr);
300 Ok(())
301 }
302
303 fn is_connected(&self) -> bool {
304 self.connected.load(Ordering::Relaxed)
305 }
306
307 fn get_stats(&self) -> ConnectionStats {
308 if let Ok(stats) = self.stats.try_lock() {
309 stats.clone()
310 } else {
311 ConnectionStats::default()
312 }
313 }
314
315 async fn set_option(&mut self, key: &str, value: serde_json::Value) -> Result<()> {
316 if key == "client_id" {
317 if let Some(id) = value.as_str() {
318 self.info.client_id = Some(id.to_string());
319 }
320 }
321 Ok(())
322 }
323}
324
325async fn handle_rpc(
327 State(stats): State<Arc<Mutex<TransportStats>>>,
328 Json(payload): Json<serde_json::Value>,
329) -> Result<Json<serde_json::Value>, (axum::http::StatusCode, String)> {
330 let mut s = stats.lock().await;
332 s.messages_received += 1;
333
334 Ok(Json(serde_json::json!({
336 "jsonrpc": "2.0",
337 "result": payload,
338 "id": payload.get("id").cloned().unwrap_or(serde_json::Value::Null)
339 })))
340}
341
342