Skip to content

Implement tower::Service for cyper::Client #49

@alekseysidorov

Description

@alekseysidorov

Hi All!

I think, that it would be very useful to implement tower::Service trait for cyper::Client:

impl tower::Service<http::Request<cyper::Body>> for cyper::Client {
    type Response = http::Response<cyper::Body>;
    /// ...
}

That would allow using cyper with the whole Tower middleware ecosystem (tower::ServiceBuilder, tower-http layers: retries, timeouts, rate limits, tracing, auth, etc.).

Also, I maintain a crate tower-http-client that turns any tower::Service<http::Request<B>> into a backend-agnostic HTTP client.

Here is the main part of example:

#[derive(Debug, Deserialize)]
struct IpInfo {
    ip: String,
    country: String,
}

#[tokio::main]
async fn main() -> Result<(), BoxError> {
    eprintln!("-> Creating an HTTP client with Tower layers...");
    // First of all, we create an opaque client using the reqwest client.
    let opaque_client = into_tower_http_client(reqwest::Client::new());
    // Secondary we add some middleware to the client and then box it.
    let mut client: HttpClient = ServiceBuilder::new()
        .layer_fn(BoxCloneSyncService::new)
        .map_request(|mut request: http::Request<_>| {
            request
                .headers_mut()
                .typed_insert(UserAgent::from_static("tower-http-client"));
            request
        })
        .service(opaque_client);

    // Finally, we can use the boxed client to send requests.
    eprintln!("-> Getting IP information...");
    let response = client.get("http://api.myip.com").send().await?;
    let info = response.body_reader().json::<IpInfo>().await?;

    eprintln!("-> Got information:");
    eprintln!("   IP address: {}", info.ip);
    eprintln!("   Country: {}", info.country);

    Ok(())
}

So exposing Service would make cyper instantly compatible with that model. Happy to help with a PR if this direction looks reasonable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions