Standalone precompressed asset gets the decompressed Content-Type but no Content-Encoding
- Dominant language
- C#
- Stars
- 3.2k
- Forks
- 1.3k
- PR merge metrics
- PR metrics pending
Description
### Describe the bug
When a `.br` or `.gz` file exists in `wwwroot` without the corresponding uncompressed sibling, the static web assets pipeline generates an endpoint that assigns the *decompressed* content type but does not assign `Content-Encoding`. The response then misdescribes its own body: the headers claim the decompressed type while the bytes are still compressed. Toolchains such as Unity WebGL builds can produce only the compressed assets (see https://github.com/dotnet/aspnetcore/issues/67884).
If the uncompressed sibling is present, the same physical file gets a correct endpoint carrying `Content-Encoding: br`. The two code paths disagree about how to describe identical bytes, and only the sibling-less case is wrong.
Beyond the response being self-inconsistent, the missing header has a concrete secondary effect under IIS. A non-empty `text/*` content type matches the `text/*` entry in the default IIS `httpCompression/dynamicTypes` list, which is enabled, so IIS applies dynamic compression to the already brotli encoded body and stamps `Content-Encoding: gzip`. The client then receives gzip wrapped around brotli.
### Steps to reproduce
A minimal repro is attached. It is a plain `Microsoft.NET.Sdk.Web` project whose `Program.cs` is only `app.MapStaticAssets()`, with two folders that differ solely in whether the uncompressed sibling exists:
```
wwwroot/standalone/hello.js.br <- compressed representation only
wwwroot/paired/hello.js + hello.js.br <- both representations, for control
```
Both `.br` files are byte identical.
1. `dotnet build`
2. Inspect `obj/Debug/net10.0/staticwebassets.build.endpoints.json`
```
standalone/hello.js.br Content-Type=text/javascript Content-Encoding=
paired/hello.js.br Content-Type=text/javascript Content-Encoding=br
```
3. Drop an uncompressed `hello.js` into `wwwroot/standalone/`, rebuild, and `standalone/hello.js.br` gains `Content-Encoding: br`. Remove it and rebuild to go back.
### Expected behavior
A standalone `foo.js.br` should be described consistently with the sibling case: recognised as a compressed representation, with `Content-Encoding: br` alongside the decompressed content type.
Failing that, it should not be given the decompressed content type at all. Emitting the decompressed content type *without* the encoding is the one combination that is clearly wrong, because it produces a response that misdescribes its own body.
### Actual behavior
The decompressed `Content-Type` with no `Content-Encoding`. Clients that depend on the headers receive compressed bytes labeled as the decompressed type.
### Other details
- Reproduced on SDK 10.0.400 and 9.0.317. Not applicable to 8.0.424, where `.br` received no content type at all.
- Relevant targets: `Microsoft.NET.Sdk.StaticWebAssets.Compression.targets`, targets `ResolveBuildCompressedStaticWebAssetsConfiguration` (calls `DiscoverPrecompressedAssets`) and `ResolveBuildCompressedStaticWebAssets` (calls `ApplyCompressionNegotiation`).
Contributor guide
No contributing guide indexed for this repository
Research direction
Start in Microsoft.NET.Sdk.StaticWebAssets.Compression.targets, focusing on ResolveBuildCompressedStaticWebAssetsConfiguration and ResolveBuildCompressedStaticWebAssets, which call DiscoverPrecompressedAssets and ApplyCompressionNegotiation. Run the described dotnet build and inspect staticwebassets.build.endpoints.json for standalone and paired assets. Done means a standalone .br or .gz asset has its decompressed Content-Type together with the matching Content-Encoding, consistent with the sibling case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- build-system, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100