Skip to content

Commit

Permalink
Be more leanient when applying compression negotiation
Browse files Browse the repository at this point in the history
  • Loading branch information
javiercn committed Dec 20, 2024
1 parent c9c5e11 commit 2e40457
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,12 @@ Copyright (c) .NET Foundation. All rights reserved.
</PropertyGroup>

<PropertyGroup>
<BuildCompressionFormats>$(BuildCompressionFormats);gzip</BuildCompressionFormats>
<PublishCompressionFormats>$(PublishCompressionFormats);gzip;brotli</PublishCompressionFormats>
<EnableDefaultCompressionFormats Condition="'$(EnableDefaultCompressionFormats)' == ''">true</EnableDefaultCompressionFormats>
<BuildCompressionFormats Condition="'$(EnableDefaultCompressionFormats)' == 'true'">$(BuildCompressionFormats);gzip</BuildCompressionFormats>
<PublishCompressionFormats Condition="'$(EnableDefaultCompressionFormats)' == 'true'">$(PublishCompressionFormats);gzip;brotli</PublishCompressionFormats>
<DisableBuildCompression Condition="'$(DisableBuildCompression)' == ''">false</DisableBuildCompression>
<CompressionIncludePatterns>$(CompressionIncludePatterns)</CompressionIncludePatterns>
<CompressionExcludePatterns>$(CompressionExcludePatterns)</CompressionExcludePatterns>
<!-- Support passing in a custom compression level to brotli and respect the old internal flag for blazor -->
<BrotliCompressionLevel Condition="'$(_BlazorBrotliCompressionLevel)' != ''">$(_BlazorBrotliCompressionLevel)</BrotliCompressionLevel>
</PropertyGroup>

<PropertyGroup>
Expand Down Expand Up @@ -195,7 +196,7 @@ Copyright (c) .NET Foundation. All rights reserved.
GeneratePublishCompressedStaticWebAssets
ResolvePublishCompressedStaticWebAssetsConfiguration
-->
<ResolvePublishRelatedStaticWebAssetsDependsOn Condition="'$(DisableBuildCompression)' != 'true'">
<ResolvePublishRelatedStaticWebAssetsDependsOn>
ResolvePublishCompressedStaticWebAssets;
$(ResolvePublishRelatedStaticWebAssetsDependsOn)
</ResolvePublishRelatedStaticWebAssetsDependsOn>
Expand All @@ -216,7 +217,7 @@ Copyright (c) .NET Foundation. All rights reserved.
<_DotNetHostFileName Condition="'$(OS)' == 'Windows_NT'">dotnet.exe</_DotNetHostFileName>
</PropertyGroup>

<!-- Build -->
<!-- Build -->

<Target Name="ResolveBuildCompressedStaticWebAssets" DependsOnTargets="$(ResolveBuildCompressedStaticWebAssetsDependsOn)">
<DefineStaticWebAssets
Expand Down Expand Up @@ -267,7 +268,7 @@ Copyright (c) .NET Foundation. All rights reserved.

<BrotliCompress Condition="'@(_BrotliCompressedStaticWebAssets)' != ''"
FilesToCompress="@(_BrotliCompressedStaticWebAssets)"
CompressionLevel="$(_BlazorBrotliCompressionLevel)"
CompressionLevel="$(BrotliCompressionLevel)"
ToolAssembly="$(_StaticWebAssetsSdkToolAssembly)"
ToolExe="$(_DotNetHostFileName)"
ToolPath="$(_DotNetHostDirectory)" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -672,7 +672,7 @@ Copyright (c) .NET Foundation. All rights reserved.
DependsOnTargets="ResolveStaticWebAssetsConfiguration;UpdateExistingPackageStaticWebAssets">

<DefineStaticWebAssets
CandidateAssets="@(Content)"
CandidateAssets="@(Content->Distinct())"
FingerprintCandidates="$(StaticWebAssetsFingerprintContent)"
FingerprintPatterns="@(StaticWebAssetFingerprintPattern)"
RelativePathPattern="wwwroot/**"
Expand Down
48 changes: 36 additions & 12 deletions src/StaticWebAssetsSdk/Tasks/ApplyCompressionNegotiation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,36 +36,60 @@ public override bool Execute()
}
}

var assetsById = CandidateAssets.Select(StaticWebAsset.FromTaskItem).ToDictionary(a => a.Identity);
var assetsById = new Dictionary<string, StaticWebAsset>(CandidateAssets.Length, OSPath.PathComparer);
// A good rule of thumb is that the number of compressed assets is half the number of assets.
var compressedAssets = new List<StaticWebAsset>(CandidateAssets.Length / 2);

var endpointsByAsset = CandidateEndpoints.Select(StaticWebAssetEndpoint.FromTaskItem)
.GroupBy(e => e.AssetFile)
.ToDictionary(g => g.Key, g => g.ToList());
for (var i = 0; i < CandidateAssets.Length; i++)
{
var candidate = StaticWebAsset.FromTaskItem(CandidateAssets[i]);
if (assetsById.ContainsKey(CandidateAssets[i].ItemSpec))
{
Log.LogWarning("Detected duplicated asset '{0}'. Skipping the asset because it was already processed.", candidate.Identity);
continue;
}

var compressedAssets = assetsById.Values.Where(a => a.AssetTraitName == "Content-Encoding").ToList();
var updatedEndpoints = new HashSet<StaticWebAssetEndpoint>(StaticWebAssetEndpoint.RouteAndAssetComparer);
assetsById[candidate.Identity] = candidate;
if (string.Equals(candidate.AssetTraitName, "Content-Encoding", StringComparison.Ordinal))
{
compressedAssets.Add(candidate);
}
}

var endpointsByAsset = new Dictionary<string, List<StaticWebAssetEndpoint>>(CandidateEndpoints.Length, StringComparer.OrdinalIgnoreCase);
for (var i = 0; i < CandidateEndpoints.Length; i++)
{
var endpoint = StaticWebAssetEndpoint.FromTaskItem(CandidateEndpoints[i]);
if (!endpointsByAsset.TryGetValue(endpoint.AssetFile, out var endpoints))
{
endpoints = [];
endpointsByAsset[endpoint.AssetFile] = endpoints;
}
endpoints.Add(endpoint);
}

var updatedEndpoints = new HashSet<StaticWebAssetEndpoint>(StaticWebAssetEndpoint.RouteAndAssetComparer);
var preservedEndpoints = new Dictionary<(string, string), StaticWebAssetEndpoint>();

// Add response headers to compressed endpoints
foreach (var compressedAsset in compressedAssets)
{
if (!assetsById.TryGetValue(compressedAsset.RelatedAsset, out var relatedAsset))
{
Log.LogWarning("Related asset not found for compressed asset: {0}", compressedAsset.Identity);
throw new InvalidOperationException($"Related asset not found for compressed asset: {compressedAsset.Identity}");
Log.LogWarning("Related asset '{0}' not found for compressed asset: '{1}'. Skipping asset", compressedAsset.RelatedAsset, compressedAsset.Identity);
continue;
}

if (!endpointsByAsset.TryGetValue(compressedAsset.Identity, out var compressedEndpoints))
{
Log.LogWarning("Endpoints not found for compressed asset: {0} {1}", compressedAsset.RelativePath, compressedAsset.Identity);
throw new InvalidOperationException($"Endpoints not found for compressed asset: {compressedAsset.Identity}");
Log.LogWarning("Endpoints not found for compressed asset: '{0}' '{1}'. Skipping asset", compressedAsset.RelativePath, compressedAsset.Identity);
continue;
}

if (!endpointsByAsset.TryGetValue(relatedAsset.Identity, out var relatedAssetEndpoints))
{
Log.LogWarning("Endpoints not found for related asset: {0}", relatedAsset.Identity);
throw new InvalidOperationException($"Endpoints not found for related asset: {relatedAsset.Identity}");
Log.LogWarning("Endpoints not found for related asset: '{0}'. Skipping asset", relatedAsset.Identity);
continue;
}

Log.LogMessage("Processing compressed asset: {0}", compressedAsset.Identity);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,19 @@ public override bool Execute()
var candidates = CandidateAssets.Select(StaticWebAsset.FromTaskItem).ToArray();
var assetsToUpdate = new List<ITaskItem>();

var candidatesByIdentity = candidates.ToDictionary(asset => asset.Identity, OSPath.PathComparer);
var candidatesByIdentity = new Dictionary<string, StaticWebAsset>(CandidateAssets.Length, OSPath.PathComparer);
for (var i = 0; i < candidates.Length; i++)
{
var candidate = candidates[i];
if (candidatesByIdentity.ContainsKey(candidate.Identity))
{
Log.LogMessage(MessageImportance.Low,
"Detected duplicated asset '{0}'. Skipping the asset because it was already processed.",
candidate.Identity);
return false;
}
candidatesByIdentity[candidate.Identity] = candidate;
}

foreach (var candidate in candidates)
{
Expand Down

0 comments on commit 2e40457

Please sign in to comment.