Skip to main content

build_client

Function build_client 

Source
pub fn build_client(config: &Config) -> Result<HttpClient, SendraError>
Expand description

Build the HTTP client a run sends every one of its requests through.

Once per run, not once per request. A reqwest::Client owns the connection pool: the TLS session, the kept-alive TCP connection and the resolved DNS for a host all live in it, and all of it is thrown away with the client. Building one per request means a collection of twenty requests against one API pays twenty TLS handshakes to send twenty requests, which is most of the wall clock for a run that does nothing else. Built once and borrowed by every send, the second request onwards reuses the connection the first opened.

It is a function taking a &Config rather than a method on Config because a client is not configuration: it holds sockets, it is cheap to clone and expensive to rebuild, and it belongs to a run, whereas the config it is built from is a resolved set of values that outlives any particular one. The config decides six things here — the timeout, the redirect policy, whether TLS certificates are verified, which proxy (if any) requests go through, which client certificate (if any) to present for mutual TLS, and whether cookies received are stored and resent automatically — and nothing else about the client is configurable; reqwest’s own pool defaults are what a command-line tool wants.

Cookies are opt-in. Config::cookie_jar defaults to false, matching curl’s own default of not persisting cookies across requests unless -c/-b is passed. When enabled, this hands the client reqwest’s own in-memory jar (ClientBuilder::cookie_store(true)) rather than a jar Sendra owns: there is no persistence to disk and nothing beyond one invocation to manage, so reqwest’s default implementation is exactly what is needed. A request whose headers: already sets Cookie is left alone — reqwest only fills in the jar’s Cookie header when the request does not already carry one, confirmed by reading reqwest’s own CookieService rather than assumed, so an explicit Cookie: header always wins outright rather than merging with the jar; Sendra raises no conflict for this the way it does for auth: plus an explicit Authorization header, since the two are not the same field the way auth: resolves into Authorization — the jar operates beneath any one request’s headers, at the client’s own connection machinery. Cookies received in response to that request are still stored in the jar regardless of the request’s own Cookie header, so a later request with no explicit header of its own picks them up.

The jar sees every hop of a redirect chain, not just the final response. reqwest layers its cookie handling underneath its redirect-following — each hop of a chain is a separate request/response pair the jar’s CookieService processes on its own, confirmed by reading reqwest’s source rather than assumed — so a Set-Cookie on an intermediate hop is stored just as reliably as one on the final response, and is even available to later hops in the same chain. This is a genuine advantage over capture’s manual Set-Cookie capture, which can only see the final response’s headers once redirects have been followed — see the module doc comment on crate::capture for that limitation. For a login flow that redirects through an intermediate hop before setting its session cookie, the jar is the only one of the two that can pick it up.

Fails when reqwest cannot construct a client at all (a TLS backend that will not initialise, say), when Config::proxy does not parse as a URL reqwest accepts, or when the client certificate cannot be built — either because client_cert/client_key names a file that cannot be read (SendraError::ClientCertIo), only one of the pair is set (SendraError::ClientCertIncomplete), or the files read do not form a valid identity (SendraError::Client) — all fatal to the whole run: a malformed proxy:/--proxy value, or an unusable client certificate, means no request in this run could ever have gone anywhere, same as a client reqwest itself refuses to build.