|
| 1 | +//! Shows a couple of ways to use the `from_fn` middleware. |
| 2 | +
|
| 3 | +use std::{collections::HashMap, io, rc::Rc, time::Duration}; |
| 4 | + |
| 5 | +use actix_web::{ |
| 6 | + body::MessageBody, |
| 7 | + dev::{Service, ServiceRequest, ServiceResponse, Transform}, |
| 8 | + http::header::{self, HeaderValue, Range}, |
| 9 | + middleware::{from_fn, Logger, Next}, |
| 10 | + web::{self, Header, Query}, |
| 11 | + App, Error, HttpResponse, HttpServer, |
| 12 | +}; |
| 13 | + |
| 14 | +async fn noop<B>(req: ServiceRequest, next: Next<B>) -> Result<ServiceResponse<B>, Error> { |
| 15 | + next.call(req).await |
| 16 | +} |
| 17 | + |
| 18 | +async fn print_range_header<B>( |
| 19 | + range_header: Option<Header<Range>>, |
| 20 | + req: ServiceRequest, |
| 21 | + next: Next<B>, |
| 22 | +) -> Result<ServiceResponse<B>, Error> { |
| 23 | + if let Some(Header(range)) = range_header { |
| 24 | + println!("Range: {range}"); |
| 25 | + } else { |
| 26 | + println!("No Range header"); |
| 27 | + } |
| 28 | + |
| 29 | + next.call(req).await |
| 30 | +} |
| 31 | + |
| 32 | +async fn mutate_body_type( |
| 33 | + req: ServiceRequest, |
| 34 | + next: Next<impl MessageBody + 'static>, |
| 35 | +) -> Result<ServiceResponse<impl MessageBody>, Error> { |
| 36 | + let res = next.call(req).await?; |
| 37 | + Ok(res.map_into_left_body::<()>()) |
| 38 | +} |
| 39 | + |
| 40 | +async fn mutate_body_type_with_extractors( |
| 41 | + string_body: String, |
| 42 | + query: Query<HashMap<String, String>>, |
| 43 | + req: ServiceRequest, |
| 44 | + next: Next<impl MessageBody + 'static>, |
| 45 | +) -> Result<ServiceResponse<impl MessageBody>, Error> { |
| 46 | + println!("body is: {string_body}"); |
| 47 | + println!("query string: {query:?}"); |
| 48 | + |
| 49 | + let res = next.call(req).await?; |
| 50 | + |
| 51 | + Ok(res.map_body(move |_, _| string_body)) |
| 52 | +} |
| 53 | + |
| 54 | +async fn timeout_10secs( |
| 55 | + req: ServiceRequest, |
| 56 | + next: Next<impl MessageBody + 'static>, |
| 57 | +) -> Result<ServiceResponse<impl MessageBody>, Error> { |
| 58 | + match tokio::time::timeout(Duration::from_secs(10), next.call(req)).await { |
| 59 | + Ok(res) => res, |
| 60 | + Err(_err) => Err(actix_web::error::ErrorRequestTimeout("")), |
| 61 | + } |
| 62 | +} |
| 63 | + |
| 64 | +struct MyMw(bool); |
| 65 | + |
| 66 | +impl MyMw { |
| 67 | + async fn mw_cb( |
| 68 | + &self, |
| 69 | + req: ServiceRequest, |
| 70 | + next: Next<impl MessageBody + 'static>, |
| 71 | + ) -> Result<ServiceResponse<impl MessageBody>, Error> { |
| 72 | + let mut res = match self.0 { |
| 73 | + true => req.into_response("short-circuited").map_into_right_body(), |
| 74 | + false => next.call(req).await?.map_into_left_body(), |
| 75 | + }; |
| 76 | + |
| 77 | + res.headers_mut() |
| 78 | + .insert(header::WARNING, HeaderValue::from_static("42")); |
| 79 | + |
| 80 | + Ok(res) |
| 81 | + } |
| 82 | + |
| 83 | + pub fn into_middleware<S, B>( |
| 84 | + self, |
| 85 | + ) -> impl Transform< |
| 86 | + S, |
| 87 | + ServiceRequest, |
| 88 | + Response = ServiceResponse<impl MessageBody>, |
| 89 | + Error = Error, |
| 90 | + InitError = (), |
| 91 | + > |
| 92 | + where |
| 93 | + S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static, |
| 94 | + B: MessageBody + 'static, |
| 95 | + { |
| 96 | + let this = Rc::new(self); |
| 97 | + from_fn(move |req, next| { |
| 98 | + let this = Rc::clone(&this); |
| 99 | + async move { Self::mw_cb(&this, req, next).await } |
| 100 | + }) |
| 101 | + } |
| 102 | +} |
| 103 | + |
| 104 | +#[actix_web::main] |
| 105 | +async fn main() -> io::Result<()> { |
| 106 | + env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); |
| 107 | + |
| 108 | + let bind = ("127.0.0.1", 8080); |
| 109 | + log::info!("staring server at http://{}:{}", &bind.0, &bind.1); |
| 110 | + |
| 111 | + HttpServer::new(|| { |
| 112 | + App::new() |
| 113 | + .wrap(from_fn(noop)) |
| 114 | + .wrap(from_fn(print_range_header)) |
| 115 | + .wrap(from_fn(mutate_body_type)) |
| 116 | + .wrap(from_fn(mutate_body_type_with_extractors)) |
| 117 | + .wrap(from_fn(timeout_10secs)) |
| 118 | + // switch bool to true to observe early response |
| 119 | + .wrap(MyMw(false).into_middleware()) |
| 120 | + .wrap(Logger::default()) |
| 121 | + .default_service(web::to(HttpResponse::Ok)) |
| 122 | + }) |
| 123 | + .workers(1) |
| 124 | + .bind(bind)? |
| 125 | + .run() |
| 126 | + .await |
| 127 | +} |
0 commit comments