sui_http/middleware/callback/
mod.rs1use http::HeaderMap;
88use http::request;
89use http::response;
90
91mod body;
92mod future;
93mod layer;
94mod service;
95
96pub use self::body::RequestBody;
97pub use self::body::ResponseBody;
98pub use self::future::ResponseFuture;
99pub use self::layer::CallbackLayer;
100pub use self::service::Callback;
101
102pub trait MakeCallbackHandler {
108 type RequestHandler: RequestHandler;
111 type ResponseHandler: ResponseHandler;
114
115 fn make_handler(
117 &self,
118 request: &request::Parts,
119 ) -> (Self::RequestHandler, Self::ResponseHandler);
120}
121
122pub trait RequestHandler {
129 fn on_body_chunk<B>(&mut self, _chunk: &B)
131 where
132 B: bytes::Buf,
133 {
134 }
136
137 fn on_end_of_stream(&mut self, _trailers: Option<&HeaderMap>) {
142 }
144
145 fn on_body_error<E>(&mut self, _error: &E)
147 where
148 E: std::fmt::Display + 'static,
149 {
150 }
152}
153
154impl RequestHandler for () {}
155
156pub trait ResponseHandler {
162 fn on_response(&mut self, response: &response::Parts);
164
165 fn on_service_error<E>(&mut self, error: &E)
169 where
170 E: std::fmt::Display + 'static;
171
172 fn on_body_chunk<B>(&mut self, _chunk: &B)
174 where
175 B: bytes::Buf,
176 {
177 }
179
180 fn on_end_of_stream(&mut self, _trailers: Option<&HeaderMap>) {
182 }
184
185 fn on_body_error<E>(&mut self, _error: &E)
187 where
188 E: std::fmt::Display + 'static,
189 {
190 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use bytes::Buf;
198 use bytes::Bytes;
199 use futures::stream;
200 use http::Request;
201 use http::Response;
202 use http_body::Body;
203 use http_body_util::BodyExt;
204 use http_body_util::Full;
205 use http_body_util::StreamBody;
206 use std::convert::Infallible;
207 use std::sync::Arc;
208 use std::sync::Mutex;
209 use tower::ServiceBuilder;
210 use tower::ServiceExt;
211
212 #[derive(Debug, Default, PartialEq, Eq)]
216 struct Events {
217 request_chunks: Vec<Vec<u8>>,
218 request_end_trailers: Vec<Option<HeaderMap>>,
219 request_body_errors: Vec<String>,
220 response_seen: u32,
221 response_chunks: Vec<Vec<u8>>,
222 response_end_trailers: Vec<Option<HeaderMap>>,
223 response_body_errors: Vec<String>,
224 response_service_errors: Vec<String>,
225 }
226
227 #[derive(Clone, Default)]
228 struct Recorder(Arc<Mutex<Events>>);
229
230 struct ReqH(Arc<Mutex<Events>>);
231 struct RespH(Arc<Mutex<Events>>);
232
233 impl RequestHandler for ReqH {
234 fn on_body_chunk<B: Buf>(&mut self, chunk: &B) {
235 self.0
236 .lock()
237 .unwrap()
238 .request_chunks
239 .push(chunk.chunk().to_vec());
240 }
241 fn on_end_of_stream(&mut self, trailers: Option<&HeaderMap>) {
242 self.0
243 .lock()
244 .unwrap()
245 .request_end_trailers
246 .push(trailers.cloned());
247 }
248 fn on_body_error<E: std::fmt::Display + 'static>(&mut self, error: &E) {
249 self.0
250 .lock()
251 .unwrap()
252 .request_body_errors
253 .push(error.to_string());
254 }
255 }
256
257 impl ResponseHandler for RespH {
258 fn on_response(&mut self, _parts: &response::Parts) {
259 self.0.lock().unwrap().response_seen += 1;
260 }
261 fn on_service_error<E: std::fmt::Display + 'static>(&mut self, error: &E) {
262 self.0
263 .lock()
264 .unwrap()
265 .response_service_errors
266 .push(error.to_string());
267 }
268 fn on_body_chunk<B: Buf>(&mut self, chunk: &B) {
269 self.0
270 .lock()
271 .unwrap()
272 .response_chunks
273 .push(chunk.chunk().to_vec());
274 }
275 fn on_end_of_stream(&mut self, trailers: Option<&HeaderMap>) {
276 self.0
277 .lock()
278 .unwrap()
279 .response_end_trailers
280 .push(trailers.cloned());
281 }
282 fn on_body_error<E: std::fmt::Display + 'static>(&mut self, error: &E) {
283 self.0
284 .lock()
285 .unwrap()
286 .response_body_errors
287 .push(error.to_string());
288 }
289 }
290
291 impl MakeCallbackHandler for Recorder {
292 type RequestHandler = ReqH;
293 type ResponseHandler = RespH;
294
295 fn make_handler(
296 &self,
297 _request: &request::Parts,
298 ) -> (Self::RequestHandler, Self::ResponseHandler) {
299 (ReqH(self.0.clone()), RespH(self.0.clone()))
300 }
301 }
302
303 async fn drain<B: Body + Unpin>(body: B) -> Result<(), B::Error> {
307 let collected = body.collect().await?;
308 let _ = collected.to_bytes();
309 Ok(())
310 }
311
312 #[tokio::test]
313 async fn observes_request_chunks_and_clean_end() {
314 let recorder = Recorder::default();
315 let events = recorder.0.clone();
316
317 let inner = tower::service_fn(|req: Request<RequestBody<Full<Bytes>, ReqH>>| async move {
318 drain(req.into_body()).await.unwrap();
319 Ok::<_, Infallible>(Response::new(Full::new(Bytes::from_static(b"ok"))))
320 });
321 let svc = ServiceBuilder::new()
322 .layer(CallbackLayer::new(recorder))
323 .service(inner);
324
325 let request = Request::new(Full::new(Bytes::from_static(b"hello world")));
326 let response = svc.oneshot(request).await.unwrap();
327 drain(response.into_body()).await.unwrap();
328
329 let events = events.lock().unwrap();
330 assert_eq!(events.request_chunks, vec![b"hello world".to_vec()]);
331 assert_eq!(events.request_end_trailers, vec![None]);
332 assert!(events.request_body_errors.is_empty());
333 assert_eq!(events.response_seen, 1);
335 assert_eq!(events.response_chunks, vec![b"ok".to_vec()]);
336 assert_eq!(events.response_end_trailers, vec![None]);
337 assert!(events.response_body_errors.is_empty());
338 assert!(events.response_service_errors.is_empty());
339 }
340
341 #[tokio::test]
342 async fn observes_request_trailers_on_end() {
343 let recorder = Recorder::default();
344 let events = recorder.0.clone();
345
346 let mut trailers = HeaderMap::new();
347 trailers.insert("x-req-trailer", "abc".parse().unwrap());
348 let frames: Vec<Result<http_body::Frame<Bytes>, Infallible>> = vec![
349 Ok(http_body::Frame::data(Bytes::from_static(b"chunk-1"))),
350 Ok(http_body::Frame::data(Bytes::from_static(b"chunk-2"))),
351 Ok(http_body::Frame::trailers(trailers.clone())),
352 ];
353 let body = StreamBody::new(stream::iter(frames));
354
355 let inner = tower::service_fn(
356 |req: Request<RequestBody<StreamBody<_>, ReqH>>| async move {
357 drain(req.into_body()).await.unwrap();
358 Ok::<_, Infallible>(Response::new(Full::new(Bytes::new())))
359 },
360 );
361 let svc = ServiceBuilder::new()
362 .layer(CallbackLayer::new(recorder))
363 .service(inner);
364
365 let response = svc.oneshot(Request::new(body)).await.unwrap();
366 drain(response.into_body()).await.unwrap();
367
368 let events = events.lock().unwrap();
369 assert_eq!(
370 events.request_chunks,
371 vec![b"chunk-1".to_vec(), b"chunk-2".to_vec()]
372 );
373 assert_eq!(events.request_end_trailers.len(), 1);
374 assert_eq!(events.request_end_trailers[0].as_ref(), Some(&trailers));
375 assert!(events.request_body_errors.is_empty());
376 }
377
378 #[tokio::test]
379 async fn observes_request_body_error() {
380 #[derive(Debug)]
381 struct BodyErr;
382 impl std::fmt::Display for BodyErr {
383 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
384 f.write_str("boom")
385 }
386 }
387 impl std::error::Error for BodyErr {}
388
389 let recorder = Recorder::default();
390 let events = recorder.0.clone();
391
392 let frames: Vec<Result<http_body::Frame<Bytes>, BodyErr>> = vec![
393 Ok(http_body::Frame::data(Bytes::from_static(b"partial"))),
394 Err(BodyErr),
395 ];
396 let body = StreamBody::new(stream::iter(frames));
397
398 let inner = tower::service_fn(
399 |req: Request<RequestBody<StreamBody<_>, ReqH>>| async move {
400 let _ = drain(req.into_body()).await;
402 Ok::<_, Infallible>(Response::new(Full::new(Bytes::new())))
403 },
404 );
405 let svc = ServiceBuilder::new()
406 .layer(CallbackLayer::new(recorder))
407 .service(inner);
408
409 let response = svc.oneshot(Request::new(body)).await.unwrap();
410 drain(response.into_body()).await.unwrap();
411
412 let events = events.lock().unwrap();
413 assert_eq!(events.request_chunks, vec![b"partial".to_vec()]);
414 assert_eq!(events.request_body_errors, vec!["boom".to_string()]);
415 assert!(events.request_end_trailers.is_empty());
417 }
418
419 #[tokio::test]
423 async fn unit_request_handler_is_noop() {
424 #[derive(Clone)]
425 struct MakeResponseOnly(Arc<Mutex<u32>>);
426
427 struct CountResp(Arc<Mutex<u32>>);
428 impl ResponseHandler for CountResp {
429 fn on_response(&mut self, _parts: &response::Parts) {
430 *self.0.lock().unwrap() += 1;
431 }
432 fn on_service_error<E: std::fmt::Display + 'static>(&mut self, _error: &E) {}
433 }
434
435 impl MakeCallbackHandler for MakeResponseOnly {
436 type RequestHandler = ();
437 type ResponseHandler = CountResp;
438
439 fn make_handler(
440 &self,
441 _request: &request::Parts,
442 ) -> (Self::RequestHandler, Self::ResponseHandler) {
443 ((), CountResp(self.0.clone()))
444 }
445 }
446
447 let counter = Arc::new(Mutex::new(0));
448 let make = MakeResponseOnly(counter.clone());
449
450 let inner = tower::service_fn(|req: Request<RequestBody<Full<Bytes>, ()>>| async move {
451 drain(req.into_body()).await.unwrap();
452 Ok::<_, Infallible>(Response::new(Full::new(Bytes::from_static(b"hi"))))
453 });
454 let svc = ServiceBuilder::new()
455 .layer(CallbackLayer::new(make))
456 .service(inner);
457
458 let response = svc
459 .oneshot(Request::new(Full::new(Bytes::from_static(b"ping"))))
460 .await
461 .unwrap();
462 drain(response.into_body()).await.unwrap();
463
464 assert_eq!(*counter.lock().unwrap(), 1);
465 }
466
467 #[tokio::test]
468 async fn observes_response_trailers_on_end() {
469 let recorder = Recorder::default();
470 let events = recorder.0.clone();
471
472 let mut trailers = HeaderMap::new();
473 trailers.insert("x-resp-trailer", "xyz".parse().unwrap());
474 let frames: Vec<Result<http_body::Frame<Bytes>, Infallible>> = vec![
475 Ok(http_body::Frame::data(Bytes::from_static(b"part-1"))),
476 Ok(http_body::Frame::data(Bytes::from_static(b"part-2"))),
477 Ok(http_body::Frame::trailers(trailers.clone())),
478 ];
479 let body_slot = Arc::new(Mutex::new(Some(StreamBody::new(stream::iter(frames)))));
482
483 let inner = tower::service_fn({
484 let body_slot = body_slot.clone();
485 move |req: Request<RequestBody<Full<Bytes>, ReqH>>| {
486 let body = body_slot.lock().unwrap().take().expect("called once");
487 async move {
488 drain(req.into_body()).await.unwrap();
489 Ok::<_, Infallible>(Response::new(body))
490 }
491 }
492 });
493 let svc = ServiceBuilder::new()
494 .layer(CallbackLayer::new(recorder))
495 .service(inner);
496
497 let response = svc
498 .oneshot(Request::new(Full::new(Bytes::from_static(b"ping"))))
499 .await
500 .unwrap();
501 drain(response.into_body()).await.unwrap();
502
503 let events = events.lock().unwrap();
504 assert_eq!(events.response_seen, 1);
505 assert_eq!(
506 events.response_chunks,
507 vec![b"part-1".to_vec(), b"part-2".to_vec()]
508 );
509 assert_eq!(events.response_end_trailers.len(), 1);
510 assert_eq!(events.response_end_trailers[0].as_ref(), Some(&trailers));
511 assert!(events.response_body_errors.is_empty());
512 assert!(events.response_service_errors.is_empty());
513 }
514
515 #[tokio::test]
516 async fn observes_response_body_error() {
517 #[derive(Debug)]
518 struct BodyErr;
519 impl std::fmt::Display for BodyErr {
520 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
521 f.write_str("body-boom")
522 }
523 }
524 impl std::error::Error for BodyErr {}
525
526 let recorder = Recorder::default();
527 let events = recorder.0.clone();
528
529 let inner = tower::service_fn(|req: Request<RequestBody<Full<Bytes>, ReqH>>| async move {
530 drain(req.into_body()).await.unwrap();
531 let frames: Vec<Result<http_body::Frame<Bytes>, BodyErr>> = vec![
532 Ok(http_body::Frame::data(Bytes::from_static(b"partial"))),
533 Err(BodyErr),
534 ];
535 Ok::<_, Infallible>(Response::new(StreamBody::new(stream::iter(frames))))
536 });
537 let svc = ServiceBuilder::new()
538 .layer(CallbackLayer::new(recorder))
539 .service(inner);
540
541 let response = svc
542 .oneshot(Request::new(Full::new(Bytes::new())))
543 .await
544 .unwrap();
545 let _ = drain(response.into_body()).await;
547
548 let events = events.lock().unwrap();
549 assert_eq!(events.response_seen, 1);
550 assert_eq!(events.response_chunks, vec![b"partial".to_vec()]);
551 assert_eq!(events.response_body_errors, vec!["body-boom".to_string()]);
552 assert!(events.response_service_errors.is_empty());
553 assert!(events.response_end_trailers.is_empty());
555 }
556
557 #[tokio::test]
558 async fn observes_service_error() {
559 #[derive(Debug)]
560 struct SvcErr;
561 impl std::fmt::Display for SvcErr {
562 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
563 f.write_str("svc-boom")
564 }
565 }
566 impl std::error::Error for SvcErr {}
567
568 let recorder = Recorder::default();
569 let events = recorder.0.clone();
570
571 let inner = tower::service_fn(|_req: Request<RequestBody<Full<Bytes>, ReqH>>| async move {
572 Err::<Response<Full<Bytes>>, _>(SvcErr)
573 });
574 let svc = ServiceBuilder::new()
575 .layer(CallbackLayer::new(recorder))
576 .service(inner);
577
578 let result = svc
579 .oneshot(Request::new(Full::new(Bytes::from_static(b"ping"))))
580 .await;
581 let err = match result {
582 Ok(_) => panic!("expected service error"),
583 Err(err) => err,
584 };
585 assert_eq!(err.to_string(), "svc-boom");
586
587 let events = events.lock().unwrap();
588 assert_eq!(events.response_seen, 0);
590 assert!(events.response_chunks.is_empty());
591 assert!(events.response_end_trailers.is_empty());
592 assert!(events.response_body_errors.is_empty());
593 assert_eq!(events.response_service_errors, vec!["svc-boom".to_string()]);
595 }
596}