-
Notifications
You must be signed in to change notification settings - Fork 15
Let hosts fix attribution headers on every engine request #196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
78e242a
b72c1d0
686dcec
be9dd26
07b5d01
19a5e18
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,7 +17,7 @@ mod failure; | |
|
|
||
| use std::time::Duration; | ||
|
|
||
| use reqwest::header::{AUTHORIZATION, HeaderValue}; | ||
| use reqwest::header::{AUTHORIZATION, HeaderMap, HeaderName, HeaderValue}; | ||
| use reqwest::{Method, RequestBuilder, Url}; | ||
| use serde_json::Value; | ||
|
|
||
|
|
@@ -59,6 +59,54 @@ pub(crate) struct HttpClient { | |
| wire: CortexWire, | ||
| read_backoff: Duration, | ||
| actor: actor::ActorCache, | ||
| /// Fixed headers the host attaches to every request (see | ||
| /// [`default_headers`]). | ||
| default_headers: HeaderMap, | ||
| } | ||
|
|
||
| /// Headers a host may not fix: the credential, the claim and actor this | ||
| /// transport sets itself, and what the HTTP stack owns. | ||
| const RESERVED_HEADERS: [&str; 7] = [ | ||
| "authorization", | ||
| "proxy-authorization", | ||
| "cookie", | ||
| "host", | ||
| "content-length", | ||
| "idempotency-key", | ||
| actor::ACTOR_HEADER, | ||
| ]; | ||
|
|
||
| /// The header map for `pairs`: fixed, non-credential headers a host sends | ||
| /// on every request, such as its product attribution (`x-sdk-name`). | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// [`Error::Config`] for a name or value no header may carry, or a reserved | ||
| /// name (the credential, `Idempotency-Key`, the actor header, and what the | ||
| /// HTTP stack sets). No message carries a value. | ||
| pub(crate) fn default_headers<K, V>(pairs: impl IntoIterator<Item = (K, V)>) -> Result<HeaderMap> | ||
| where | ||
| K: AsRef<str>, | ||
| V: AsRef<str>, | ||
| { | ||
| let mut map = HeaderMap::new(); | ||
| for (name, value) in pairs { | ||
| let name = name.as_ref().trim(); | ||
| let parsed = HeaderName::from_bytes(name.as_bytes()) | ||
| .map_err(|_| Error::Config(format!("`{name}` is not a valid header name")))?; | ||
| if RESERVED_HEADERS | ||
| .iter() | ||
| .any(|reserved| reserved.eq_ignore_ascii_case(parsed.as_str())) | ||
| { | ||
| return Err(Error::Config(format!( | ||
| "`{name}` is set by the memory transport and cannot be fixed" | ||
| ))); | ||
| } | ||
| let value = HeaderValue::from_str(value.as_ref().trim()) | ||
| .map_err(|_| Error::Config(format!("the `{name}` header value is not valid")))?; | ||
| map.insert(parsed, value); | ||
| } | ||
| Ok(map) | ||
| } | ||
|
|
||
| impl std::fmt::Debug for HttpClient { | ||
|
|
@@ -129,9 +177,15 @@ impl HttpClient { | |
| wire, | ||
| read_backoff: READ_BACKOFF, | ||
| actor: actor::ActorCache::default(), | ||
| default_headers: HeaderMap::new(), | ||
| }) | ||
| } | ||
|
|
||
| /// Sends `headers` on every request from now on. | ||
| pub(crate) fn set_default_headers(&mut self, headers: HeaderMap) { | ||
| self.default_headers = headers; | ||
| } | ||
|
|
||
| /// The wire this client speaks. | ||
| pub(crate) fn wire(&self) -> CortexWire { | ||
| self.wire | ||
|
|
@@ -166,6 +220,7 @@ impl HttpClient { | |
| Ok(self | ||
| .inner | ||
| .request(method, url) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Drive the running engine with configured headers end to end The new fixed-headers feature has two external surfaces: the [RULE] e2e-uncovered · |
||
| .headers(self.default_headers.clone()) | ||
| .header(AUTHORIZATION, header)) | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apply configured headers when building the engine
EngineSettings::headersis added to the public configuration and documented as being sent on every request, but the existingMemoryConfig::buildpath passes the settings tobuild_enginewithout any visible header application. The repository search shows this field is only declared here; the header-enabled API exists onCortexEngine, but no configuration code invokes it. As a result, a host can successfully deserialize and serialize headers while every request omits them, making the new configuration option ineffective. Forward the map through the registry/build path and call the engine's header configuration method, propagating invalid or reserved-header errors.[RULE] unused-configuration ·