Expand description

reverse-proxy-service is tower Services that performs “reverse proxy” with various rewriting rules.

Internally these services use hyper::Client to send an incoming request to the another server. The connector for a client can be HttpConnector, HttpsConnector, or any ones whichever you want.

Examples

There are two types of services, OneshotService and ReusedService. The OneshotService owns the Client, while the ReusedService shares the Client via Arc.

General usage

use reverse_proxy_service::ReusedServiceBuilder;
use reverse_proxy_service::{ReplaceAll, ReplaceN};

use hyper::body::Body;
use http::Request;
use tower_service::Service as _;

let svc_builder = reverse_proxy_service::builder_http("example.com:1234").unwrap();

let req1 = Request::builder()
    .method("GET")
    .uri("https://myserver.com/foo/bar/foo")
    .body(Body::empty())
    .unwrap();

// Clones Arc<Client>
let mut svc1 = svc_builder.build(ReplaceAll("foo", "baz"));
// http://example.com:1234/baz/bar/baz
let _res = svc1.call(req1).await.unwrap();

let req2 = Request::builder()
    .method("POST")
    .uri("https://myserver.com/foo/bar/foo")
    .header("Content-Type", "application/x-www-form-urlencoded")
    .body(Body::from("key=value"))
    .unwrap();

let mut svc2 = svc_builder.build(ReplaceN("foo", "baz", 1));
// http://example.com:1234/baz/bar/foo
let _res = svc2.call(req2).await.unwrap();

In this example, the svc1 and svc2 shares the same Client, holding the Arc<Client>s inside them.

For more information of rewriting rules (ReplaceAll, ReplaceN etc.), see the documentations of rewrite.

With axum

use reverse_proxy_service::ReusedServiceBuilder;
use reverse_proxy_service::{TrimPrefix, AppendSuffix, Static};

use axum::Router;

#[tokio::main]
async fn main() {
    let host1 = reverse_proxy_service::builder_http("example.com").unwrap();
    let host2 = reverse_proxy_service::builder_http("example.net:1234").unwrap();

    let app = Router::new()
        .route_service("/healthcheck", host1.build(Static("/")))
        .route_service("/users/*path", host1.build(TrimPrefix("/users")))
        .route_service("/posts", host2.build(AppendSuffix("/")));

    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

Return Types

The return type (Future::Output) of ReusedService and OneshotService is Result<Result<Response, Error>, Infallible>. This is because axum’s Router accepts only such Services.

The Error type implements IntoResponse if you enable the axumfeature. It returns an empty body, with the status code INTERNAL_SERVER_ERROR. The description of this error will be logged out at error level in the into_response() method.

Features

  • http1: default on
  • http2: default off
  • https: default off
  • axum: default off

You must turn on either http1or http2. You cannot use the services if, for example, only the https feature is on.

Re-exports

pub use rewrite::*;

Modules

clienthttp1 or http2
Includes helper functions to build Clients, and some re-exports from hyper::client or hyper_tls.
A PathRewriter instance defines a rule to rewrite the request path.

Structs

A Service<Request<B>> that sends a request and returns the response, owning a Client.
A Service<Request<B>> that sends a request and returns the response, sharing a Client.

Enums

Functions