mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-02 21:02:37 +00:00
Add only_authenticated option to the client (#7545)
This commit is contained in:
parent
0d81bfbc67
commit
d9a5f5ca1c
2 changed files with 77 additions and 35 deletions
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
realm::Realm,
|
||||
CredentialsCache, KeyringProvider, CREDENTIALS_CACHE,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use anyhow::{anyhow, format_err};
|
||||
use netrc::Netrc;
|
||||
use reqwest::{Request, Response};
|
||||
use reqwest_middleware::{Error, Middleware, Next};
|
||||
|
|
@ -22,6 +22,9 @@ pub struct AuthMiddleware {
|
|||
netrc: Option<Netrc>,
|
||||
keyring: Option<KeyringProvider>,
|
||||
cache: Option<CredentialsCache>,
|
||||
/// We know that the endpoint needs authentication, so we don't try to send an unauthenticated
|
||||
/// request, avoiding cloning an uncloneable request.
|
||||
only_authenticated: bool,
|
||||
}
|
||||
|
||||
impl AuthMiddleware {
|
||||
|
|
@ -30,6 +33,7 @@ impl AuthMiddleware {
|
|||
netrc: Netrc::new().ok(),
|
||||
keyring: None,
|
||||
cache: None,
|
||||
only_authenticated: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,6 +60,14 @@ impl AuthMiddleware {
|
|||
self
|
||||
}
|
||||
|
||||
/// We know that the endpoint needs authentication, so we don't try to send an unauthenticated
|
||||
/// request, avoiding cloning an uncloneable request.
|
||||
#[must_use]
|
||||
pub fn with_only_authenticated(mut self, only_authenticated: bool) -> Self {
|
||||
self.only_authenticated = only_authenticated;
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the configured authentication store.
|
||||
///
|
||||
/// If not set, the global store is used.
|
||||
|
|
@ -198,14 +210,21 @@ impl Middleware for AuthMiddleware {
|
|||
.as_ref()
|
||||
.is_some_and(|credentials| credentials.username().is_some());
|
||||
|
||||
let (mut retry_request, response) = if self.only_authenticated {
|
||||
// For endpoints where we require the user to provide credentials, we don't try the
|
||||
// unauthenticated request first.
|
||||
trace!("Checking for credentials for {url}");
|
||||
(request, None)
|
||||
} else {
|
||||
// Otherwise, attempt an anonymous request
|
||||
trace!("Attempting unauthenticated request for {url}");
|
||||
|
||||
// <https://github.com/TrueLayer/reqwest-middleware/blob/abdf1844c37092d323683c2396b7eefda1418d3c/reqwest-retry/src/middleware.rs#L141-L149>
|
||||
// Clone the request so we can retry it on authentication failure
|
||||
let mut retry_request = request.try_clone().ok_or_else(|| {
|
||||
let retry_request = request.try_clone().ok_or_else(|| {
|
||||
Error::Middleware(anyhow!(
|
||||
"Request object is not cloneable. Are you passing a streaming body?".to_string()
|
||||
"Request object is not cloneable. Are you passing a streaming body?"
|
||||
.to_string()
|
||||
))
|
||||
})?;
|
||||
|
||||
|
|
@ -225,6 +244,9 @@ impl Middleware for AuthMiddleware {
|
|||
response.status()
|
||||
);
|
||||
|
||||
(retry_request, Some(response))
|
||||
};
|
||||
|
||||
// Check in the cache first
|
||||
let credentials = self.cache().get_realm(
|
||||
Realm::from(retry_request.url()),
|
||||
|
|
@ -265,7 +287,13 @@ impl Middleware for AuthMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(response) = response {
|
||||
Ok(response)
|
||||
} else {
|
||||
Err(Error::Middleware(format_err!(
|
||||
"Missing credentials for {url}"
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ pub struct BaseClientBuilder<'a> {
|
|||
client: Option<Client>,
|
||||
markers: Option<&'a MarkerEnvironment>,
|
||||
platform: Option<&'a Platform>,
|
||||
only_authenticated: bool,
|
||||
}
|
||||
|
||||
impl Default for BaseClientBuilder<'_> {
|
||||
|
|
@ -55,6 +56,7 @@ impl BaseClientBuilder<'_> {
|
|||
client: None,
|
||||
markers: None,
|
||||
platform: None,
|
||||
only_authenticated: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -108,6 +110,12 @@ impl<'a> BaseClientBuilder<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn only_authenticated(mut self, only_authenticated: bool) -> Self {
|
||||
self.only_authenticated = only_authenticated;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_offline(&self) -> bool {
|
||||
matches!(self.connectivity, Connectivity::Offline)
|
||||
}
|
||||
|
|
@ -230,8 +238,10 @@ impl<'a> BaseClientBuilder<'a> {
|
|||
fn apply_middleware(&self, client: Client) -> ClientWithMiddleware {
|
||||
match self.connectivity {
|
||||
Connectivity::Online => {
|
||||
let client = reqwest_middleware::ClientBuilder::new(client);
|
||||
let mut client = reqwest_middleware::ClientBuilder::new(client);
|
||||
|
||||
// Avoid uncloneable errors with a streaming body during publish.
|
||||
if self.retries > 0 {
|
||||
// Initialize the retry strategy.
|
||||
let retry_policy =
|
||||
ExponentialBackoff::builder().build_with_max_retries(self.retries);
|
||||
|
|
@ -239,11 +249,15 @@ impl<'a> BaseClientBuilder<'a> {
|
|||
retry_policy,
|
||||
UvRetryableStrategy,
|
||||
);
|
||||
let client = client.with(retry_strategy);
|
||||
client = client.with(retry_strategy);
|
||||
}
|
||||
|
||||
// Initialize the authentication middleware to set headers.
|
||||
let client =
|
||||
client.with(AuthMiddleware::new().with_keyring(self.keyring.to_provider()));
|
||||
client = client.with(
|
||||
AuthMiddleware::new()
|
||||
.with_keyring(self.keyring.to_provider())
|
||||
.with_only_authenticated(self.only_authenticated),
|
||||
);
|
||||
|
||||
client.build()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue