https://github.com/nigma143/actix-web-buffering
Request/Response body buffering with support spooled to a temp file on disk
https://github.com/nigma143/actix-web-buffering
actix actix-web buffering request response
Last synced: 3 months ago
JSON representation
Request/Response body buffering with support spooled to a temp file on disk
- Host: GitHub
- URL: https://github.com/nigma143/actix-web-buffering
- Owner: nigma143
- License: mit
- Created: 2021-03-04T16:55:26.000Z (over 5 years ago)
- Default Branch: main
- Last Pushed: 2025-06-22T09:04:21.000Z (about 1 year ago)
- Last Synced: 2025-12-13T22:14:07.884Z (7 months ago)
- Topics: actix, actix-web, buffering, request, response
- Language: Rust
- Homepage:
- Size: 23.4 KB
- Stars: 0
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[](https://docs.rs/actix-web-buffering)
[](https://crates.io/crates/actix-web-buffering)
# actix-web-buffering
Request/Response body buffering with support spooled to a temp file on disk
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)
## Example:
```rust
use std::{
cell::RefCell,
pin::Pin,
rc::Rc,
sync::Arc,
task::{Context, Poll},
};
use actix_service::Transform;
use actix_web::{
dev::{Body, Service, ServiceRequest, ServiceResponse},
web,
web::BytesMut,
App, Error, HttpMessage, HttpResponse, HttpServer, Responder,
};
use actix_web_buffering::{
enable_request_buffering, enable_response_buffering, FileBufferingStreamWrapper,
};
use futures::{
future::{ok, Ready},
stream::StreamExt,
Future, FutureExt,
};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let wrapper = FileBufferingStreamWrapper::new()
.tmp_dir(std::env::temp_dir())
.threshold(1024 * 30)
.produce_chunk_size(1024 * 30)
.buffer_limit(Some(1024 * 30 * 10));
let wrapper = Arc::new(wrapper);
HttpServer::new(move || {
let r1 = Arc::clone(&wrapper);
let r2 = Arc::clone(&wrapper);
App::new()
.wrap(Example(r1))
.wrap(Example(r2))
.service(web::resource("/").route(web::post().to(echo)))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
async fn echo(req_body: String) -> impl Responder {
HttpResponse::Ok().body(req_body)
}
struct Example(Arc);
impl Transform for Example
where
S: Service, Error = Error> + 'static,
{
type Request = ServiceRequest;
type Response = ServiceResponse;
type Error = Error;
type InitError = ();
type Transform = ExampleMiddleware;
type Future = Ready>;
fn new_transform(&self, service: S) -> Self::Future {
ok(ExampleMiddleware {
service: Rc::new(RefCell::new(service)),
wrapper: Arc::clone(&self.0),
})
}
}
pub struct ExampleMiddleware {
service: Rc>,
wrapper: Arc,
}
impl Service for ExampleMiddleware
where
S: Service, Error = Error> + 'static,
{
type Request = ServiceRequest;
type Response = ServiceResponse;
type Error = Error;
type Future = Pin>>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> {
self.service.poll_ready(cx)
}
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
let mut svc = self.service.clone();
let wrapper = self.wrapper.clone();
async move {
enable_request_buffering(&wrapper, &mut req);
let mut stream = req.take_payload();
let mut body = BytesMut::new();
while let Some(chunk) = stream.next().await {
body.extend_from_slice(&chunk.unwrap());
}
req.set_payload(stream);
println!("request body: {:?}", body);
let svc_res = svc.call(req).await?;
let mut svc_res = enable_response_buffering(&wrapper, svc_res);
let mut stream = svc_res.take_body();
let mut body = BytesMut::new();
while let Some(chunk) = stream.next().await {
body.extend_from_slice(&chunk.unwrap());
}
let svc_res = svc_res.map_body(|_, _| stream);
println!("response body: {:?}", body);
Ok(svc_res)
}
.boxed_local()
}
}
```