11// Copyright (c) Six Labors.
22// Licensed under the Six Labors Split License.
3- #nullable disable
43
54using Amazon . S3 ;
65using Amazon . S3 . Model ;
@@ -29,7 +28,7 @@ private readonly Dictionary<string, AmazonS3Client> buckets
2928 = new ( ) ;
3029
3130 private readonly AWSS3StorageImageProviderOptions storageOptions ;
32- private Func < HttpContext , bool > match ;
31+ private Func < HttpContext , bool > ? match ;
3332
3433 /// <summary>
3534 /// Contains various helper methods based on the current configuration.
@@ -70,17 +69,23 @@ public bool IsValidRequest(HttpContext context)
7069 => this . formatUtilities . TryGetExtensionFromUri ( context . Request . GetDisplayUrl ( ) , out _ ) ;
7170
7271 /// <inheritdoc />
73- public async Task < IImageResolver > GetAsync ( HttpContext context )
72+ public async Task < IImageResolver ? > GetAsync ( HttpContext context )
7473 {
7574 // Strip the leading slash and bucket name from the HTTP request path and treat
7675 // the remaining path string as the key.
7776 // Path has already been correctly parsed before here.
7877 string bucketName = string . Empty ;
79- IAmazonS3 s3Client = null ;
78+ IAmazonS3 ? s3Client = null ;
8079
8180 // We want an exact match here to ensure that bucket names starting with
8281 // the same prefix are not mixed up.
83- string path = context . Request . Path . Value . TrimStart ( SlashChars ) ;
82+ string ? path = context . Request . Path . Value ? . TrimStart ( SlashChars ) ;
83+
84+ if ( path is null )
85+ {
86+ return null ;
87+ }
88+
8489 int index = path . IndexOfAny ( SlashChars ) ;
8590 string nameToMatch = index != - 1 ? path . Substring ( 0 , index ) : path ;
8691
@@ -108,19 +113,26 @@ public async Task<IImageResolver> GetAsync(HttpContext context)
108113 return null ;
109114 }
110115
111- if ( ! await KeyExists ( s3Client , bucketName , key ) )
116+ KeyExistsResult keyExists = await KeyExists ( s3Client , bucketName , key ) ;
117+ if ( ! keyExists . Exists )
112118 {
113119 return null ;
114120 }
115121
116- return new AWSS3StorageImageResolver ( s3Client , bucketName , key ) ;
122+ return new AWSS3StorageImageResolver ( s3Client , bucketName , key , keyExists . Metadata ) ;
117123 }
118124
119125 private bool IsMatch ( HttpContext context )
120126 {
121127 // Only match loosly here for performance.
122128 // Path matching conflicts should be dealt with by configuration.
123- string path = context . Request . Path . Value . TrimStart ( SlashChars ) ;
129+ string ? path = context . Request . Path . Value ? . TrimStart ( SlashChars ) ;
130+
131+ if ( path is null )
132+ {
133+ return false ;
134+ }
135+
124136 foreach ( string bucket in this . buckets . Keys )
125137 {
126138 if ( path . StartsWith ( bucket , StringComparison . OrdinalIgnoreCase ) )
@@ -133,39 +145,40 @@ private bool IsMatch(HttpContext context)
133145 }
134146
135147 // ref https://github.com/aws/aws-sdk-net/blob/master/sdk/src/Services/S3/Custom/_bcl/IO/S3FileInfo.cs#L118
136- private static async Task < bool > KeyExists ( IAmazonS3 s3Client , string bucketName , string key )
148+ private static async Task < KeyExistsResult > KeyExists ( IAmazonS3 s3Client , string bucketName , string key )
137149 {
138150 try
139151 {
140- GetObjectMetadataRequest request = new ( )
141- {
142- BucketName = bucketName ,
143- Key = key
144- } ;
152+ GetObjectMetadataRequest request = new ( ) { BucketName = bucketName , Key = key } ;
145153
146154 // If the object doesn't exist then a "NotFound" will be thrown
147- await s3Client . GetObjectMetadataAsync ( request ) ;
148- return true ;
155+ GetObjectMetadataResponse metadata = await s3Client . GetObjectMetadataAsync ( request ) ;
156+ return new KeyExistsResult ( metadata ) ;
149157 }
150158 catch ( AmazonS3Exception e )
151159 {
152160 if ( string . Equals ( e . ErrorCode , "NoSuchBucket" , StringComparison . Ordinal ) )
153161 {
154- return false ;
162+ return default ;
155163 }
156164
157165 if ( string . Equals ( e . ErrorCode , "NotFound" , StringComparison . Ordinal ) )
158166 {
159- return false ;
167+ return default ;
160168 }
161169
162170 // If the object exists but the client is not authorized to access it, then a "Forbidden" will be thrown.
163171 if ( string . Equals ( e . ErrorCode , "Forbidden" , StringComparison . Ordinal ) )
164172 {
165- return false ;
173+ return default ;
166174 }
167175
168176 throw ;
169177 }
170178 }
179+
180+ private readonly record struct KeyExistsResult ( GetObjectMetadataResponse ? Metadata )
181+ {
182+ public bool Exists => this . Metadata is not null ;
183+ }
171184}
0 commit comments