Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Http Clients

In The “🗼 Services all the way down 🐢” chapter you can read and learn that a big pillar of Rama’s architecture is build on top of the Service concept. A Service takes a Request, and uses it to serve either a Response or Error. Such a Service can produce the response “directly” (also called ☘️ Leaf services) or instead pass the request to an inner Service which it wraps around (so called 🍔 Middlewares).

It’s a powerful concept, originally introduced to Rust by the Tower ecosystem and allows you build complex stacks specialised to your needs in a modular and easy manner. Even cooler is that this works for both clients and servers alike.

Rama provides an EasyHttpWebClient which sends your Http Request over the network and returns the Response if it receives and read one or an Error otherwise. Combined with the many Layers (middleware) that Rama provides and perhaps also some developed by you it is possible to create a powerful Http client suited to your needs.

As a 🍒 cherry on the cake you can import the HttpClientExt trait in your Rust module to be able to use your Http Client Service stack using a high level API to build and send requests with ease.

Note

The same client-side composition model is also used by Rama’s gRPC support. See the dedicated gRPC chapter for how a regular Rama HTTP client can act as the transport substrate for typed gRPC clients.

Http Client Example

See for a full and tested “high level” example of a http client at https://github.com/plabayo/rama/tree/main/examples/src/http_high_level_client.rs.

More client examples:

Server certificate pinning

Rama clients can pin the server leaf through TlsServerCertPins, backend agnostic for rustls and BoringSSL. The standard pin is the SHA-256 of the leaf’s public key (TlsServerCertPin::SpkiSha256), exchanged in the usual sha256/<base64> format and printed by rama probe tls. Parsing also accepts a PEM certificate, deriving its key pin. It survives certificate renewal as long as the key pair is unchanged. Pin the exact DER-encoded certificate (TlsServerCertPin::ExactDer) only when you control the certificate file itself.

With the default ServerVerifyMode::Auto, both the pin and normal certificate verification must succeed. ServerVerifyMode::Disable makes the applicable pins the only certificate check.

Pins are grouped in sets: pins within a set and applicable sets are alternatives (e.g. the current and next key during rotation). A set without server names applies globally; otherwise only when the effective TLS server name matches — never inferred from certificate contents. If no set applies, pinning imposes no check and normal verification continues.

#![allow(unused)]
fn main() {
let pins = TlsServerCertPins::new(
    TlsServerCertPinSet::try_new([api_current_key_pin, api_next_key_pin])?
        .with_server_name(Host::from_static("api.example.com")),
)
.with_pin_set(
    TlsServerCertPinSet::new("sha256/xg6kqyS+uaJikboVvZPxNOYXMD3XPakJAakHSfGau/M=".parse::<TlsServerCertPin>()?)
        .with_server_name(Host::from_static("login.example.com")),
);
}

For a private CA or a self-signed server certificate that is suitable as a trust anchor, replace the default trust anchors directly from a certificate list:

#![allow(unused)]
fn main() {
let tls_config = TlsClientConfig::default_http()
    .try_with_server_trust_anchors(certificates)?;
}

This common API builds the native rustls or BoringSSL verification store. Normal chain, validity, usage, and server-name checks remain enabled. It can be combined with TlsServerCertPins when both trust verification and a leaf pin must succeed. These anchors replace the system trust store; they are not added to it. Use a pin instead of treating an arbitrary CA-issued server leaf as a trust anchor.