Module follow_redirect

Middleware for following redirections.

Overview

The FollowRedirect middleware retries requests with the inner Service to follow HTTP redirections.

The middleware tries to clone the original Request when making a redirected request. However, since [Extensions][http::Extensions] are !Clone, any extensions set by outer middleware will be discarded. Also, the request body cannot always be cloned. When the original body is known to be empty by Body::size_hint, the middleware uses Default implementation of the body type to create a new request body. If you know that the body can be cloned in some way, you can tell the middleware to clone it by configuring a policy.

Examples

Basic usage

use http::{Request, Response};
use bytes::Bytes;
use http_body_util::Full;
use tower::{Service, ServiceBuilder, ServiceExt};
use tower_http::follow_redirect::{FollowRedirectLayer, RequestUri};

# #[tokio::main]
# async fn main() -> Result<(), std::convert::Infallible> {
# let http_client = tower::service_fn(|req: Request<_>| async move {
#     let dest = "https://www.rust-lang.org/";
#     let mut res = http::Response::builder();
#     if req.uri() != dest {
#         res = res
#             .status(http::StatusCode::MOVED_PERMANENTLY)
#             .header(http::header::LOCATION, dest);
#     }
#     Ok::<_, std::convert::Infallible>(res.body(Full::<Bytes>::default()).unwrap())
# });
let mut client = ServiceBuilder::new()
    .layer(FollowRedirectLayer::new())
    .service(http_client);

let request = Request::builder()
    .uri("https://rust-lang.org/")
    .body(Full::<Bytes>::default())
    .unwrap();

let response = client.ready().await?.call(request).await?;
// Get the final request URI.
assert_eq!(response.extensions().get::<RequestUri>().unwrap().0, "https://www.rust-lang.org/");
# Ok(())
# }

Customizing the Policy

You can use a Policy value to customize how the middleware handles redirections.

use http::{Request, Response};
use http_body_util::Full;
use bytes::Bytes;
use tower::{Service, ServiceBuilder, ServiceExt};
use tower_http::follow_redirect::{
    policy::{self, PolicyExt},
    FollowRedirectLayer,
};

#[derive(Debug)]
enum MyError {
    TooManyRedirects,
    Other(tower::BoxError),
}

# #[tokio::main]
# async fn main() -> Result<(), MyError> {
# let http_client =
#     tower::service_fn(|_: Request<Full<Bytes>>| async { Ok(Response::new(Full::<Bytes>::default())) });
let policy = policy::Limited::new(10) // Set the maximum number of redirections to 10.
    // Return an error when the limit was reached.
    .or::<_, (), _>(policy::redirect_fn(|_| Err(MyError::TooManyRedirects)))
    // Do not follow cross-origin redirections, and return the redirection responses as-is.
    .and::<_, (), _>(policy::SameOrigin::new());

let mut client = ServiceBuilder::new()
    .layer(FollowRedirectLayer::with_policy(policy))
    .map_err(MyError::Other)
    .service(http_client);

// ...
# let _ = client.ready().await?.call(Request::default()).await?;
# Ok(())
# }

Modules

Structs