@@ -1025,17 +1025,8 @@ impl Bucket {
10251025 pub async fn object_exists < S : AsRef < str > > ( & self , path : S ) -> Result < bool , S3Error > {
10261026 let command = Command :: HeadObject ;
10271027 let request = RequestImpl :: new ( self , path. as_ref ( ) , command) . await ?;
1028- let response_data = match request. response_data ( false ) . await {
1029- Ok ( response_data) => response_data,
1030- Err ( S3Error :: HttpFailWithBody ( status_code, error) ) => {
1031- if status_code == 404 {
1032- return Ok ( false ) ;
1033- }
1034- return Err ( S3Error :: HttpFailWithBody ( status_code, error) ) ;
1035- }
1036- Err ( e) => return Err ( e) ,
1037- } ;
1038- Ok ( response_data. status_code ( ) != 404 )
1028+ let status_code = request. response_status ( ) . await ?;
1029+ Ok ( status_code != 404 )
10391030 }
10401031
10411032 #[ maybe_async:: maybe_async]
@@ -3113,11 +3104,75 @@ mod test {
31133104 use crate :: { Bucket , PostPolicy } ;
31143105 use http:: header:: { CACHE_CONTROL , HeaderMap , HeaderName , HeaderValue } ;
31153106 use std:: env;
3107+ #[ cfg( all( not( feature = "sync" ) , feature = "with-tokio" ) ) ]
3108+ use std:: io:: { Read , Write } ;
3109+ #[ cfg( all( not( feature = "sync" ) , feature = "with-tokio" ) ) ]
3110+ use std:: net:: TcpListener ;
3111+ #[ cfg( all( not( feature = "sync" ) , feature = "with-tokio" ) ) ]
3112+ use std:: sync:: {
3113+ Arc ,
3114+ atomic:: { AtomicUsize , Ordering } ,
3115+ } ;
3116+ #[ cfg( all( not( feature = "sync" ) , feature = "with-tokio" ) ) ]
3117+ use std:: thread;
31163118
31173119 fn init ( ) {
31183120 let _ = env_logger:: builder ( ) . is_test ( true ) . try_init ( ) ;
31193121 }
31203122
3123+ #[ cfg( all( not( feature = "sync" ) , feature = "with-tokio" ) ) ]
3124+ #[ tokio:: test]
3125+ async fn test_object_exists_404_does_not_retry ( ) {
3126+ init ( ) ;
3127+
3128+ let listener = TcpListener :: bind ( "127.0.0.1:0" ) . unwrap ( ) ;
3129+ let endpoint = format ! ( "http://{}" , listener. local_addr( ) . unwrap( ) ) ;
3130+ let requests = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
3131+ let request_count = Arc :: clone ( & requests) ;
3132+
3133+ let server = thread:: spawn ( move || {
3134+ let ( mut stream, _) = listener. accept ( ) . unwrap ( ) ;
3135+ request_count. fetch_add ( 1 , Ordering :: SeqCst ) ;
3136+
3137+ let mut buffer = [ 0 ; 2048 ] ;
3138+ let _ = stream. read ( & mut buffer) . unwrap ( ) ;
3139+ stream
3140+ . write_all (
3141+ b"HTTP/1.1 404 Not Found\r \n Content-Length: 0\r \n Connection: close\r \n \r \n " ,
3142+ )
3143+ . unwrap ( ) ;
3144+ } ) ;
3145+
3146+ crate :: set_retries ( 1 ) ;
3147+
3148+ let credentials = Credentials :: new (
3149+ Some ( "test_access_key" ) ,
3150+ Some ( "test_secret_key" ) ,
3151+ None ,
3152+ None ,
3153+ None ,
3154+ )
3155+ . unwrap ( ) ;
3156+ let bucket = Bucket :: new (
3157+ "test-bucket" ,
3158+ Region :: Custom {
3159+ region : "us-east-1" . to_owned ( ) ,
3160+ endpoint,
3161+ } ,
3162+ credentials,
3163+ )
3164+ . unwrap ( )
3165+ . with_path_style ( ) ;
3166+
3167+ let exists = bucket. object_exists ( "/missing.txt" ) . await . unwrap ( ) ;
3168+
3169+ crate :: set_retries ( 1 ) ;
3170+ server. join ( ) . unwrap ( ) ;
3171+
3172+ assert ! ( !exists) ;
3173+ assert_eq ! ( requests. load( Ordering :: SeqCst ) , 1 ) ;
3174+ }
3175+
31213176 fn test_aws_credentials ( ) -> Credentials {
31223177 Credentials :: new (
31233178 Some ( & env:: var ( "EU_AWS_ACCESS_KEY_ID" ) . unwrap ( ) ) ,
0 commit comments