Skip to main content

smcp_server_hyper/
lib.rs

1//! Hyper adapter for SMCP Server
2//!
3//! This crate provides a Hyper-based HTTP server implementation for the SMCP protocol.
4//! It exposes both programmatic API and a standalone binary.
5
6use std::convert::Infallible;
7use std::net::SocketAddr;
8
9use http_body_util::Full;
10use hyper::body::Bytes;
11use hyper::service::service_fn;
12use hyper::{Method, Request, Response, StatusCode};
13use hyper_util::rt::TokioIo;
14use socketioxide::SocketIo;
15use tokio::net::TcpListener;
16use tower::ServiceBuilder;
17use tracing::{error, info};
18
19use smcp_server_core::SmcpServerLayer;
20
21/// A Hyper-based SMCP server
22pub struct HyperServer {
23    pub layer: Option<SmcpServerLayer>,
24    pub addr: SocketAddr,
25}
26
27impl HyperServer {
28    /// Create a new HyperServer
29    pub fn new() -> Self {
30        Self {
31            layer: None,
32            addr: "127.0.0.1:0".parse().unwrap(),
33        }
34    }
35
36    /// Set the SMCP server layer
37    pub fn with_layer(mut self, layer: SmcpServerLayer) -> Self {
38        self.layer = Some(layer);
39        self
40    }
41
42    /// Run the server on the given address
43    pub async fn run(
44        self,
45        addr: SocketAddr,
46    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
47        let layer = self.layer.ok_or("SMCP layer not configured")?;
48
49        info!("Starting SMCP server on {}", addr);
50
51        // Create a TCP listener
52        let listener = TcpListener::bind(addr).await?;
53        let local_addr = listener.local_addr()?;
54        info!("Server listening on {}", local_addr);
55
56        // Build the service stack
57        let service = ServiceBuilder::new()
58            .layer(layer.layer)
59            .service(service_fn(move |req| {
60                let io = layer.io.clone();
61                async move { handle_request(req, &io).await }
62            }));
63
64        // Serve connections
65        loop {
66            let (stream, remote_addr) = listener.accept().await?;
67            info!("New connection from: {}", remote_addr);
68
69            let service = service.clone();
70            tokio::spawn(async move {
71                let io = TokioIo::new(stream);
72                if let Err(err) = hyper::server::conn::http1::Builder::new()
73                    .serve_connection(io, service)
74                    .with_upgrades()
75                    .await
76                {
77                    error!("Failed to serve connection: {}", err);
78                }
79            });
80        }
81    }
82}
83
84impl Default for HyperServer {
85    fn default() -> Self {
86        Self::new()
87    }
88}
89
90/// Handle HTTP requests
91pub async fn handle_request(
92    req: Request<hyper::body::Incoming>,
93    _io: &SocketIo,
94) -> Result<Response<Full<Bytes>>, Infallible> {
95    let response = match (req.method(), req.uri().path()) {
96        (&Method::GET, "/") => Response::builder()
97            .status(StatusCode::OK)
98            .body(Full::new(Bytes::from("SMCP Server is running")))
99            .unwrap(),
100        (&Method::GET, "/health") => Response::builder()
101            .status(StatusCode::OK)
102            .body(Full::new(Bytes::from("{\"status\":\"ok\"}")))
103            .unwrap(),
104        (&Method::GET, "/socket.io/") => {
105            // Socket.IO will handle these requests through the layer
106            Response::builder()
107                .status(StatusCode::NOT_FOUND)
108                .body(Full::new(Bytes::from("Not found")))
109                .unwrap()
110        }
111        _ => Response::builder()
112            .status(StatusCode::NOT_FOUND)
113            .body(Full::new(Bytes::from("Not found")))
114            .unwrap(),
115    };
116
117    Ok(response)
118}
119
120/// Builder for creating and configuring a HyperServer
121pub struct HyperServerBuilder {
122    layer: Option<SmcpServerLayer>,
123    addr: Option<SocketAddr>,
124}
125
126impl HyperServerBuilder {
127    /// Create a new builder
128    pub fn new() -> Self {
129        Self {
130            layer: None,
131            addr: None,
132        }
133    }
134
135    /// Set the SMCP server layer
136    pub fn with_layer(mut self, layer: SmcpServerLayer) -> Self {
137        self.layer = Some(layer);
138        self
139    }
140
141    /// Set the server address
142    pub fn with_addr(mut self, addr: SocketAddr) -> Self {
143        self.addr = Some(addr);
144        self
145    }
146
147    /// Build the HyperServer
148    pub fn build(self) -> HyperServer {
149        let mut server = HyperServer::new();
150        if let Some(layer) = self.layer {
151            server = server.with_layer(layer);
152        }
153        if let Some(addr) = self.addr {
154            server.addr = addr;
155        }
156        server
157    }
158}
159
160impl Default for HyperServerBuilder {
161    fn default() -> Self {
162        Self::new()
163    }
164}
165
166/// Convenience function to quickly run a server with default configuration
167pub async fn run_server(addr: SocketAddr) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
168    // Build SMCP layer with default configuration
169    let layer = smcp_server_core::SmcpServerBuilder::new()
170        .build_layer()
171        .map_err(|e| format!("Failed to build SMCP layer: {}", e))?;
172
173    let server = HyperServerBuilder::new()
174        .with_layer(layer)
175        .with_addr(addr)
176        .build();
177
178    server.run(addr).await
179}
180
181#[cfg(test)]
182mod tests {
183    use super::*;
184
185    #[test]
186    fn test_hyper_server_creation() {
187        let server = HyperServer::new();
188        assert_eq!(server.addr, "127.0.0.1:0".parse().unwrap());
189    }
190
191    #[test]
192    fn test_hyper_server_builder() {
193        let builder = HyperServerBuilder::new();
194        assert!(builder.layer.is_none());
195        assert!(builder.addr.is_none());
196    }
197}