actix_web_buffering/lib.rs
1//! Request/Response body buffering with support spooled to a temp file on disk
2//!
3//! Use it in actix-web middleware. For example this is used at [actix-web-detached-jws-middleware](https://crates.io/crates/actix-web-detached-jws-middleware)
4//!
5//! # Example:
6//! ```ignore
7//! use std::{
8//! cell::RefCell,
9//! pin::Pin,
10//! rc::Rc,
11//! sync::Arc,
12//! task::{Context, Poll},
13//! };
14//!
15//! use actix_service::Transform;
16//! use actix_web::{
17//! dev::{Body, Service, ServiceRequest, ServiceResponse},
18//! web,
19//! web::BytesMut,
20//! App, Error, HttpMessage, HttpResponse, HttpServer, Responder,
21//! };
22//! use actix_web_buffering::{
23//! enable_request_buffering, enable_response_buffering, FileBufferingStreamWrapper,
24//! };
25//! use futures::{
26//! future::{ok, Ready},
27//! stream::StreamExt,
28//! Future, FutureExt,
29//! };
30//!
31//! #[actix_web::main]
32//! async fn main() -> std::io::Result<()> {
33//! let wrapper = FileBufferingStreamWrapper::new()
34//! .tmp_dir(std::env::temp_dir())
35//! .threshold(1024 * 30)
36//! .produce_chunk_size(1024 * 30)
37//! .buffer_limit(Some(1024 * 30 * 10));
38//!
39//! let wrapper = Arc::new(wrapper);
40//!
41//! HttpServer::new(move || {
42//! let r1 = Arc::clone(&wrapper);
43//! let r2 = Arc::clone(&wrapper);
44//! App::new()
45//! .wrap(Example(r1))
46//! .wrap(Example(r2))
47//! .service(web::resource("/").route(web::post().to(echo)))
48//! })
49//! .bind("127.0.0.1:8080")?
50//! .run()
51//! .await
52//! }
53//!
54//! async fn echo(req_body: String) -> impl Responder {
55//! HttpResponse::Ok().body(req_body)
56//! }
57//!
58//! struct Example(Arc<FileBufferingStreamWrapper>);
59//!
60//! impl<S> Transform<S> for Example
61//! where
62//! S: Service<Request = ServiceRequest, Response = ServiceResponse<Body>, Error = Error> + 'static,
63//! {
64//! type Request = ServiceRequest;
65//! type Response = ServiceResponse<Body>;
66//! type Error = Error;
67//! type InitError = ();
68//! type Transform = ExampleMiddleware<S>;
69//! type Future = Ready<Result<Self::Transform, Self::InitError>>;
70//!
71//! fn new_transform(&self, service: S) -> Self::Future {
72//! ok(ExampleMiddleware {
73//! service: Rc::new(RefCell::new(service)),
74//! wrapper: Arc::clone(&self.0),
75//! })
76//! }
77//! }
78//! pub struct ExampleMiddleware<S> {
79//! service: Rc<RefCell<S>>,
80//! wrapper: Arc<FileBufferingStreamWrapper>,
81//! }
82//!
83//! impl<S> Service for ExampleMiddleware<S>
84//! where
85//! S: Service<Request = ServiceRequest, Response = ServiceResponse<Body>, Error = Error> + 'static,
86//! {
87//! type Request = ServiceRequest;
88//! type Response = ServiceResponse<Body>;
89//! type Error = Error;
90//! type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
91//!
92//! fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
93//! self.service.poll_ready(cx)
94//! }
95//!
96//! fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
97//! let mut svc = self.service.clone();
98//! let wrapper = self.wrapper.clone();
99//!
100//! async move {
101//! enable_request_buffering(&wrapper, &mut req);
102//!
103//! let mut stream = req.take_payload();
104//! let mut body = BytesMut::new();
105//! while let Some(chunk) = stream.next().await {
106//! body.extend_from_slice(&chunk.unwrap());
107//! }
108//! req.set_payload(stream);
109//! println!("request body: {:?}", body);
110//!
111//! let svc_res = svc.call(req).await?;
112//!
113//! let mut svc_res = enable_response_buffering(&wrapper, svc_res);
114//!
115//! let mut stream = svc_res.take_body();
116//! let mut body = BytesMut::new();
117//! while let Some(chunk) = stream.next().await {
118//! body.extend_from_slice(&chunk.unwrap());
119//! }
120//! let svc_res = svc_res.map_body(|_, _| stream);
121//! println!("response body: {:?}", body);
122//!
123//! Ok(svc_res)
124//! }
125//! .boxed_local()
126//! }
127//! }
128//! ```
129pub mod buffering;
130
131pub use crate::buffering::{
132 enable_request_buffering, enable_response_buffering, FileBufferingStreamWrapper,
133};