Skip to content

Commit 9d6212c

Browse files
committed
Revert "[Static Assets] Consume original-resource instead of the Etag (#63379)"
This reverts commit b8a8adf.
1 parent 7561f25 commit 9d6212c

File tree

2 files changed

+24
-73
lines changed

2 files changed

+24
-73
lines changed

src/StaticAssets/src/Development/StaticAssetDevelopmentRuntimeHandler.cs

Lines changed: 22 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Diagnostics;
54
using System.Globalization;
65
using System.IO.Compression;
76
using System.IO.Pipelines;
@@ -23,34 +22,20 @@
2322
namespace Microsoft.AspNetCore.Builder;
2423

2524
// Handles changes during development to support common scenarios where for example, a developer changes a file in the wwwroot folder.
26-
internal sealed partial class StaticAssetDevelopmentRuntimeHandler
25+
internal sealed partial class StaticAssetDevelopmentRuntimeHandler(List<StaticAssetDescriptor> descriptors)
2726
{
2827
internal const string ReloadStaticAssetsAtRuntimeKey = "ReloadStaticAssetsAtRuntime";
2928

30-
private readonly Dictionary<(string Route, string ETag), StaticAssetDescriptor> _descriptorsMap = [];
31-
32-
public StaticAssetDevelopmentRuntimeHandler(List<StaticAssetDescriptor> descriptors)
33-
{
34-
CreateDescriptorMap(descriptors);
35-
}
36-
3729
public void AttachRuntimePatching(EndpointBuilder builder)
3830
{
3931
var original = builder.RequestDelegate!;
4032
var asset = builder.Metadata.OfType<StaticAssetDescriptor>().Single();
4133
if (asset.HasContentEncoding())
4234
{
43-
var originalETag = GetDescriptorOriginalResourceProperty(asset);
44-
StaticAssetDescriptor? originalAsset = null;
45-
if (originalETag is not null && _descriptorsMap.TryGetValue((asset.Route, originalETag), out originalAsset))
46-
{
47-
asset = originalAsset;
48-
}
49-
else
50-
{
51-
Debug.Assert(originalETag != null, $"The static asset descriptor {asset.Route} - {asset.AssetPath} does not have an original-resource property.");
52-
Debug.Assert(originalAsset != null, $"The static asset descriptor {asset.Route} - {asset.AssetPath} has an original-resource property that does not match any known static asset descriptor.");
53-
}
35+
// This is a compressed asset, which might get out of "sync" with the original uncompressed version.
36+
// We are going to find the original by using the weak etag from this compressed asset and locating an asset with the same etag.
37+
var eTag = asset.GetWeakETag();
38+
asset = FindOriginalAsset(eTag.Tag.Value!, descriptors);
5439
}
5540

5641
builder.RequestDelegate = async context =>
@@ -72,57 +57,6 @@ public void AttachRuntimePatching(EndpointBuilder builder)
7257
};
7358
}
7459

75-
private static string? GetDescriptorOriginalResourceProperty(StaticAssetDescriptor descriptor)
76-
{
77-
for (var i = 0; i < descriptor.Properties.Count; i++)
78-
{
79-
var property = descriptor.Properties[i];
80-
if (string.Equals(property.Name, "original-resource", StringComparison.OrdinalIgnoreCase))
81-
{
82-
return property.Value;
83-
}
84-
}
85-
86-
return null;
87-
}
88-
89-
private static string? GetDescriptorETagResponseHeader(StaticAssetDescriptor descriptor)
90-
{
91-
for (var i = 0; i < descriptor.ResponseHeaders.Count; i++)
92-
{
93-
var header = descriptor.ResponseHeaders[i];
94-
if (string.Equals(header.Name, HeaderNames.ETag, StringComparison.OrdinalIgnoreCase))
95-
{
96-
return header.Value;
97-
}
98-
}
99-
100-
return null;
101-
}
102-
103-
private void CreateDescriptorMap(List<StaticAssetDescriptor> descriptors)
104-
{
105-
for (var i = 0; i < descriptors.Count; i++)
106-
{
107-
var descriptor = descriptors[i];
108-
if (descriptor.HasContentEncoding())
109-
{
110-
continue;
111-
}
112-
var etag = GetDescriptorETagResponseHeader(descriptor);
113-
if (etag != null && !_descriptorsMap.ContainsKey((descriptor.Route, etag)))
114-
{
115-
_descriptorsMap[(descriptor.Route, etag)] = descriptor;
116-
}
117-
else
118-
{
119-
Debug.Assert(etag != null, $"The static asset descriptor {descriptor.Route} - {descriptor.AssetPath} does not have an ETag response header.");
120-
Debug.Assert(_descriptorsMap.ContainsKey((descriptor.Route, etag)),
121-
$"The static asset descriptor {descriptor.Route} - {descriptor.AssetPath} has an ETag response header that is already registered in the map. This should not happen, as the ETag should be unique for each static asset.");
122-
}
123-
}
124-
}
125-
12660
internal static string GetETag(IFileInfo fileInfo)
12761
{
12862
using var stream = fileInfo.CreateReadStream();
@@ -180,7 +114,10 @@ public Task SendFileAsync(string path, long offset, long? count, CancellationTok
180114
_context.Response.Headers.ContentLength = stream.Length;
181115

182116
var eTag = Convert.ToBase64String(SHA256.HashData(stream));
183-
_context.Response.Headers.ETag = new StringValues($"\"{eTag}\"");
117+
var weakETag = $"W/{GetETag(fileInfo)}";
118+
119+
// Here we add the ETag for the Gzip stream as well as the weak ETag for the original asset.
120+
_context.Response.Headers.ETag = new StringValues([$"\"{eTag}\"", weakETag]);
184121

185122
stream.Seek(0, SeekOrigin.Begin);
186123
return stream.CopyToAsync(_context.Response.Body, cancellationToken);
@@ -205,6 +142,19 @@ public Task StartAsync(CancellationToken cancellationToken = default)
205142
}
206143
}
207144

145+
private static StaticAssetDescriptor FindOriginalAsset(string tag, List<StaticAssetDescriptor> descriptors)
146+
{
147+
for (var i = 0; i < descriptors.Count; i++)
148+
{
149+
if (descriptors[i].HasETag(tag))
150+
{
151+
return descriptors[i];
152+
}
153+
}
154+
155+
throw new InvalidOperationException("The original asset was not found.");
156+
}
157+
208158
internal static bool IsEnabled(bool isBuildManifest, IServiceProvider serviceProvider)
209159
{
210160
var config = serviceProvider.GetRequiredService<IConfiguration>();

src/StaticAssets/test/StaticAssetsIntegrationTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,12 +519,13 @@ private static void CreateTestManifest(string appName, string webRoot, params Sp
519519
Route = resource.Path,
520520
AssetPath = $"{resource.Path}.gz",
521521
Selectors = [new StaticAssetSelector("Content-Encoding", "gzip", "1.0")],
522-
Properties = [new("original-resource", $"\"{GetEtag(resource.Content)}\"")],
522+
Properties = [],
523523
ResponseHeaders = [
524524
new ("Accept-Ranges", "bytes"),
525525
new ("Content-Type", GetContentType(filePath)),
526526

527527
new ("Content-Length", length.ToString(CultureInfo.InvariantCulture)),
528+
new ("ETag", $"W/\"{GetEtag(resource.Content)}\""),
528529
new ("ETag", $"\"{GetEtagForFile(compressedFilePath)}\""),
529530
new ("Last-Modified", lastModified.ToString("ddd, dd MMM yyyy HH:mm:ss 'GMT'", CultureInfo.InvariantCulture)),
530531

0 commit comments

Comments
 (0)