withHeaders method
Returns a context that shares this one's session state but sends
headers in place of this context's own.
The current session, the single in-flight refresh, and onSessionUpdated are one thing shared by both contexts: a refresh started through either is seen by both, and the refresh token — which is single-use — is spent exactly once. Everything else is carried over unchanged: protocol, service, relay service, timeout, retry strategy, HTTP clients, and the OAuth session manager when there is one.
This exists because request headers belong to the client that sends
them, while the session belongs to the account. A client that must send
a header the others must not — an atproto-proxy routing its calls to a
different service, say — would otherwise need a context of its own, and
a second context used to mean a second copy of the session. Two copies
race the moment the access token expires: whichever refreshes first
spends the token, and the other's refresh is rejected by the server as
an UnauthorizedException the caller did nothing to provoke.
final bare = ctx.withHeaders(const {
'atproto-proxy': 'did:web:example.com#service',
});
headers replaces this context's headers rather than extending them, so
bare above sends the proxy header and nothing else. To keep the origin's
headers, use withAdditionalHeaders rather than spreading headers of
the origin context by hand: a spread is key-exact, and header names are
not, so an origin sending Atproto-Proxy would keep it alongside the
added atproto-proxy and the request would carry the header twice.
Implementation
ServiceContext withHeaders(final Map<String, String> headers) =>
ServiceContext._shared(
_state,
headers: headers,
protocol: _protocol,
explicitService: _explicitService,
relayService: relayService,
oAuthSessionManager: oAuthSessionManager,
//! Stateless and immutable, so sharing the instance is equivalent to
//! rebuilding one from the same retry strategy.
challenge: _challenge,
timeout: _timeout,
getClient: _getClient,
postClient: _postClient,
);