Skip to main content

rust_serv/middleware/
logging.rs

1use std::task::{Context, Poll};
2use hyper::Request;
3use http_body_util::BodyExt;
4use tower::{Layer, Service};
5
6/// Logging middleware layer
7#[derive(Clone)]
8pub struct LoggingLayer;
9
10impl<S> Layer<S> for LoggingLayer {
11    type Service = LoggingService<S>;
12
13    fn layer(&self, inner: S) -> Self::Service {
14        LoggingService { inner }
15    }
16}
17
18#[derive(Clone)]
19pub struct LoggingService<S> {
20    inner: S,
21}
22
23impl<S, ReqBody, ResBody> Service<Request<ReqBody>> for LoggingService<S>
24where
25    S: Service<Request<ReqBody>, Response = hyper::Response<ResBody>>,
26    ReqBody: BodyExt + Send + 'static,
27    ResBody: BodyExt + Send + 'static,
28{
29    type Response = S::Response;
30    type Error = S::Error;
31    type Future = S::Future;
32
33    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
34        self.inner.poll_ready(cx)
35    }
36
37    fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
38        tracing::info!("{} {}", req.method(), req.uri().path());
39        self.inner.call(req)
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46    use hyper::{Request, Response, Method};
47    use http_body_util::Full;
48    use hyper::body::Bytes;
49    use tower::{ServiceBuilder, ServiceExt};
50    use std::pin::Pin;
51    use std::future::Future;
52    use std::task::{Context, Poll};
53
54    /// Mock service for testing
55    #[derive(Clone)]
56    struct MockService;
57
58    impl Service<Request<Full<Bytes>>> for MockService {
59        type Response = Response<Full<Bytes>>;
60        type Error = std::convert::Infallible;
61        type Future = Pin<Box<dyn std::future::Future<Output = Result<Self::Response, Self::Error>> + Send>>;
62
63        fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
64            Poll::Ready(Ok(()))
65        }
66
67        fn call(&mut self, req: Request<Full<Bytes>>) -> Self::Future {
68            let method = req.method().clone();
69            let uri = req.uri().clone();
70
71            Box::pin(async move {
72                Ok(Response::new(Full::new(Bytes::from(format!(
73                    "Response to {} {}",
74                    method,
75                    uri.path()
76                )))))
77            })
78        }
79    }
80
81    #[test]
82    fn test_logging_layer_creation() {
83        let _layer = LoggingLayer;
84        // Layer should be created successfully
85    }
86
87    #[test]
88    fn test_logging_layer_clone() {
89        let layer = LoggingLayer;
90        let _cloned = layer.clone();
91        // Layer should be clonable
92    }
93
94    #[test]
95    fn test_logging_service_creation() {
96        let layer = LoggingLayer;
97        let mock_service = MockService;
98        let _logging_service = layer.layer(mock_service);
99        // LoggingService should be created successfully
100    }
101
102    #[tokio::test]
103    async fn test_logging_service_call() {
104        let layer = LoggingLayer;
105        let mock_service = MockService;
106        let mut logging_service = layer.layer(mock_service);
107
108        let request = Request::builder()
109            .method(Method::GET)
110            .uri("/test")
111            .body(Full::new(Bytes::new()))
112            .unwrap();
113
114        let response = logging_service.call(request).await.unwrap();
115        assert_eq!(response.status(), hyper::StatusCode::OK);
116    }
117
118    #[tokio::test]
119    async fn test_logging_service_with_post() {
120        let layer = LoggingLayer;
121        let mock_service = MockService;
122        let mut logging_service = layer.layer(mock_service);
123
124        let request = Request::builder()
125            .method(Method::POST)
126            .uri("/api/users")
127            .body(Full::new(Bytes::from("test data")))
128            .unwrap();
129
130        let response = logging_service.call(request).await.unwrap();
131        assert_eq!(response.status(), hyper::StatusCode::OK);
132    }
133
134    #[tokio::test]
135    async fn test_logging_service_with_query_params() {
136        let layer = LoggingLayer;
137        let mock_service = MockService;
138        let mut logging_service = layer.layer(mock_service);
139
140        let request = Request::builder()
141            .method(Method::GET)
142            .uri("/search?q=rust&limit=10")
143            .body(Full::new(Bytes::new()))
144            .unwrap();
145
146        let response = logging_service.call(request).await.unwrap();
147        assert_eq!(response.status(), hyper::StatusCode::OK);
148    }
149
150    #[tokio::test]
151    async fn test_logging_service_poll_ready() {
152        use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
153
154        let layer = LoggingLayer;
155        let mock_service = MockService;
156        let mut logging_service = layer.layer(mock_service);
157
158        // Create a dummy waker
159        fn dummy_clone(_: *const ()) -> RawWaker {
160            RawWaker::new(std::ptr::null(), &VTABLE)
161        }
162        fn dummy(_: *const ()) {}
163        static VTABLE: RawWakerVTable = RawWakerVTable::new(dummy_clone, dummy, dummy, dummy);
164        let raw_waker = RawWaker::new(std::ptr::null(), &VTABLE);
165        let waker = unsafe { Waker::from_raw(raw_waker) };
166        let mut cx = Context::from_waker(&waker);
167
168        let poll_result = logging_service.poll_ready(&mut cx);
169        assert!(matches!(poll_result, Poll::Ready(Ok(()))));
170    }
171
172    #[test]
173    fn test_logging_service_clone() {
174        let layer = LoggingLayer;
175        let mock_service = MockService;
176        let logging_service = layer.layer(mock_service);
177        let _cloned = logging_service.clone();
178        // LoggingService should be clonable
179    }
180
181    #[tokio::test]
182    async fn test_logging_service_multiple_requests() {
183        let layer = LoggingLayer;
184        let mock_service = MockService;
185        let mut logging_service = layer.layer(mock_service);
186
187        // Make multiple requests
188        for i in 0..5 {
189            let request = Request::builder()
190                .method(Method::GET)
191                .uri(&format!("/page/{}", i))
192                .body(Full::new(Bytes::new()))
193                .unwrap();
194
195            let response = logging_service.call(request).await.unwrap();
196            assert_eq!(response.status(), hyper::StatusCode::OK);
197        }
198    }
199
200    #[tokio::test]
201    async fn test_logging_service_with_different_methods() {
202        let methods = vec![
203            Method::GET,
204            Method::POST,
205            Method::PUT,
206            Method::DELETE,
207            Method::PATCH,
208            Method::HEAD,
209            Method::OPTIONS,
210        ];
211
212        for method in methods {
213            let layer = LoggingLayer;
214            let mock_service = MockService;
215            let mut logging_service = layer.layer(mock_service);
216
217            let request = Request::builder()
218                .method(method.clone())
219                .uri("/test")
220                .body(Full::new(Bytes::new()))
221                .unwrap();
222
223            let response = logging_service.call(request).await.unwrap();
224            assert_eq!(response.status(), hyper::StatusCode::OK);
225        }
226    }
227}