rust_serv/middleware/
logging.rs1use std::task::{Context, Poll};
2use hyper::Request;
3use http_body_util::BodyExt;
4use tower::{Layer, Service};
5
6#[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 #[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 }
86
87 #[test]
88 fn test_logging_layer_clone() {
89 let layer = LoggingLayer;
90 let _cloned = layer.clone();
91 }
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 }
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 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 }
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 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}