@@ -286,9 +286,42 @@ impl HttpClient {
286286
287287 // Queries the Iceberg REST catalog after authentication with the given `Request` and
288288 // returns a `Response`.
289+ //
290+ // If a custom authenticator is configured, the first request is sent without authentication.
291+ // If it fails with a 401/403 permission denied error, the custom authenticator is called
292+ // to get a fresh token and the request is retried.
293+ //
294+ // For other authentication methods (static token, OAuth credentials), authentication
295+ // is applied to all requests as before.
289296 pub async fn query_catalog ( & self , mut request : Request ) -> Result < Response > {
290- self . authenticate ( & mut request) . await ?;
291- self . execute ( request) . await
297+ let has_custom_authenticator = self . authenticator . is_some ( ) ;
298+ let token_is_set = self . token . lock ( ) . await . is_some ( ) ;
299+
300+ if has_custom_authenticator && token_is_set {
301+ // For custom authenticators with a cached token, try without authentication first
302+ // to avoid unnecessary token fetches
303+ let cloned_request = request
304+ . try_clone ( )
305+ . ok_or_else ( || Error :: new ( ErrorKind :: DataInvalid , "Unable to clone request" ) ) ?;
306+ let response = self . execute ( cloned_request) . await ?;
307+
308+ // Check if we got a permission denied error
309+ if matches ! (
310+ response. status( ) ,
311+ StatusCode :: UNAUTHORIZED | StatusCode :: FORBIDDEN
312+ ) {
313+ // Retry with authentication from the custom authenticator
314+ self . authenticate ( & mut request) . await ?;
315+ return self . execute ( request) . await ;
316+ }
317+
318+ Ok ( response)
319+ } else {
320+ // For custom authenticators without a token, or other auth methods:
321+ // authenticate on every request
322+ self . authenticate ( & mut request) . await ?;
323+ self . execute ( request) . await
324+ }
292325 }
293326}
294327
0 commit comments