Skip to content

Commit 2f9904d

Browse files
committed
Updated the KeyExists from aws keep the response metadata in the image resolver.
1 parent fb8b334 commit 2f9904d

File tree

2 files changed

+38
-23
lines changed

2 files changed

+38
-23
lines changed

src/ImageSharp.Web.Providers.AWS/Providers/AWSS3StorageImageProvider.cs

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
3-
#nullable disable
43

54
using Amazon.S3;
65
using 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
}

src/ImageSharp.Web.Providers.AWS/Resolvers/AWSS3StorageImageResolver.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
3-
#nullable disable
43

54
using System.Net.Http.Headers;
65
using Amazon.S3;
@@ -16,29 +15,32 @@ public class AWSS3StorageImageResolver : IImageResolver
1615
private readonly IAmazonS3 amazonS3;
1716
private readonly string bucketName;
1817
private readonly string imagePath;
18+
private readonly GetObjectMetadataResponse? metadataResponse;
1919

2020
/// <summary>
2121
/// Initializes a new instance of the <see cref="AWSS3StorageImageResolver"/> class.
2222
/// </summary>
2323
/// <param name="amazonS3">The Amazon S3 Client</param>
2424
/// <param name="bucketName">The bucket name.</param>
2525
/// <param name="imagePath">The image path.</param>
26-
public AWSS3StorageImageResolver(IAmazonS3 amazonS3, string bucketName, string imagePath)
26+
/// <param name="metadataResponse">Optional metadata response.</param>
27+
public AWSS3StorageImageResolver(IAmazonS3 amazonS3, string bucketName, string imagePath, GetObjectMetadataResponse? metadataResponse = null)
2728
{
2829
this.amazonS3 = amazonS3;
2930
this.bucketName = bucketName;
3031
this.imagePath = imagePath;
32+
this.metadataResponse = metadataResponse;
3133
}
3234

3335
/// <inheritdoc />
3436
public async Task<ImageMetadata> GetMetaDataAsync()
3537
{
36-
GetObjectMetadataResponse metadata = await this.amazonS3.GetObjectMetadataAsync(this.bucketName, this.imagePath);
38+
GetObjectMetadataResponse metadata = this.metadataResponse ?? await this.amazonS3.GetObjectMetadataAsync(this.bucketName, this.imagePath);
3739

3840
// Try to parse the max age from the source. If it's not zero then we pass it along
3941
// to set the cache control headers for the response.
4042
TimeSpan maxAge = TimeSpan.MinValue;
41-
if (CacheControlHeaderValue.TryParse(metadata.Headers.CacheControl, out CacheControlHeaderValue cacheControl))
43+
if (CacheControlHeaderValue.TryParse(metadata.Headers.CacheControl, out CacheControlHeaderValue? cacheControl))
4244
{
4345
// Weirdly passing null to TryParse returns true.
4446
if (cacheControl?.MaxAge.HasValue == true)

0 commit comments

Comments
 (0)