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.