Skip to content

[API Proposal] Expose ZstandardDecompressionOptions #67476

Description

@BrennanConroy

Background and Motivation

The ASP.NET Core request decompression middleware (Microsoft.AspNetCore.RequestDecompression) supports the Zstandard (zstd) content encoding. Today its zstd provider constructs the decoder with default settings and offers no way to configure decompression behavior.
dotnet/runtime #129768 adds a new System.IO.Compression.ZstandardDecompressionOptions type that lets callers configure the Zstandard decoder, most notably:

  • MaxWindowLog — caps the maximum decompression window size (as a base‑2 logarithm). Limiting this helps mitigate decompression‑bomb denial‑of‑service attacks where a small payload declares a very large window and forces the server to allocate a large amount of memory.
  • Dictionary — enables dictionary‑based decompression.
    The response compression middleware already exposes a symmetric ZstandardCompressionProviderOptions. This proposal adds the decompression counterpart so servers can configure the zstd decoder used when decompressing request bodies, following the same IOptions<>/DI pattern.

Proposed API

namespace Microsoft.AspNetCore.RequestDecompression;
+ /// <summary>
+ /// Options for the Zstandard (zstd) request decompression provider.
+ /// </summary>
+ public class ZstandardDecompressionProviderOptions : IOptions<ZstandardDecompressionProviderOptions>
+ {
+     public ZstandardDecompressionProviderOptions();
+
+     /// <summary>
+     /// The decompression options to use for the stream.
+     /// </summary>
+     public System.IO.Compression.ZstandardDecompressionOptions DecompressionOptions { get; set; }
+ }

This depends on the new System.IO.Compression.ZstandardDecompressionOptions runtime type from dotnet/runtime #129768.
We'll make DecompressionOptions default to MaxWindowLog = 23 (8 MB) per RFC see below.

Usage Examples

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRequestDecompression();

// Raise the maximum Zstandard window size accepted from clients to 2^24 bytes (16 MB). Requests whose zstd
// frame declares a larger window are rejected with an InvalidDataException.
builder.Services.Configure<ZstandardDecompressionProviderOptions>(options =>
{
    options.DecompressionOptions.MaxWindowLog = 24;
});

var app = builder.Build();
app.UseRequestDecompression();
app.Run();

Alternative Designs

  • Expose the raw ZstandardDecompressionOptions directly on RequestDecompressionOptions (e.g., a ZstandardOptions property configured inside AddRequestDecompression(options => ...)). This is simpler but inconsistent with the existing Microsoft.AspNetCore.ResponseCompression.ZstandardCompressionProviderOptions, which is a dedicated provider-options type resolved from DI via IOptions<>. The proposed shape mirrors the response side so request and response zstd configuration look and behave the same, and so the default providers (and their options) can be resolved from DI through the provider factory.
  • Analogous APIs: This is the direct decompression analogue of the, shipping in 11.0, ZstandardCompressionProviderOptions (response compression), which wraps ZstandardCompressionOptions in the same way.

Risks

  • No breaking change. Zstandard request decompression is new in 11.0 and has not shipped, so introducing these options and defaulting MaxWindowLog to 23 (an 8 MB window) does not change any previously shipped behavior.
  • MaxWindowLog limits the advertised window, not the decompressed size. The window size is a property of how a payload was compressed and is recorded in the zstd frame header; it is independent of how large the data actually decompresses to. As a consequence, a request body that decompresses to well under the default request body size limit (30 MB) but was produced by an encoder that advertised a large window (for example 128 MB, i.e. window log 27) will be rejected with an InvalidDataException if the configured MaxWindowLog is lower. Picking a default therefore trades DoS protection against rejecting otherwise-small payloads from clients that use large windows - it is deliberately not derived from the request body size limit.
  • The default aligns with the HTTP standard and with browsers. RFC 9659 ("Window Sizing for Zstandard Content Encoding", which updates RFC 8878) makes an 8 MB window a requirement for the zstd HTTP content coding: decoders MUST support windows up to 8 MB and encoders MUST NOT generate frames requiring more. Browsers enforce exactly this - for example, Chromium sets ZSTD_d_windowLogMax = 23 in net/filter/zstd_source_stream.cc. Defaulting to 23 puts the middleware on the same contract: it accepts every standards-compliant client — and any encoder at default settings, which advertise windows of 2 MB or less - and rejects only deliberately oversized windows (such as zstd --long, which defaults to a 128 MB / window-log-27 window).

Activity

  1. added
    api-ready-for-reviewAPI is ready for formal API review - https://github.com/dotnet/apireviews
    and removed
    api-suggestionEarly API idea and discussion, it is NOT ready for implementation
    on Jun 29, 2026
  2. dotnet-policy-service commented on Jun 29, 2026

    @dotnet-policy-service
    Contributor

    Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:

    • The PR contains changes to the reference-assembly that describe the API change. Or, you have included a snippet of reference-assembly-style code that illustrates the API change.
    • The PR describes the impact to users, both positive (useful new APIs) and negative (breaking changes).
    • Someone is assigned to "champion" this change in the meeting, and they understand the impact and design of the change.
  3. Youssef1313 commented on Jun 30, 2026

    @Youssef1313
    Member

    class ZstandardDecompressionProviderOptions

    Any reason to not seal the class?

  4. added
    area-middlewareIncludes: URL rewrite, redirect, response cache/compression, session, and other general middlewares
    on Jun 30, 2026
  5. BrennanConroy commented on Jun 30, 2026

    @BrennanConroy
    MemberAuthor

    Probably should, looks like we didn't seal ZstandardCompressionProviderOptions. Should probably bring that up in API review as well.

  6. added
    api-needs-workAPI needs work before it is approved, it is NOT ready for implementation
    and removed
    api-ready-for-reviewAPI is ready for formal API review - https://github.com/dotnet/apireviews
    on Jul 6, 2026
  7. halter73 commented on Jul 6, 2026

    @halter73
    Member

    API Review Notes:

    • Do we need per-endpoint config? For the Zstandard dictionary in particular, it might be tuned per-endpoint. A global dictionary other than the default might hurt performance.
      • It might make sense to expose just MaxWindowLog (now renamed to MaxWindowLog2) globally, but we don't want to forward ZstandardDecompressionOptions one-at-a-time.
    • Could we put ZstandardDecompressionOptions directly in endpoint metadata?
      • What about non-endpoints?
      • How do we know the request decompression middleware is the only thing that wants these options?
      • Let's not hastily design a per-endpoint design in the API review meeting. If there's demand, we'll bring it back with per-endpoint config.

    API needs work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-needs-workAPI needs work before it is approved, it is NOT ready for implementationapi-proposalarea-middlewareIncludes: URL rewrite, redirect, response cache/compression, session, and other general middlewares

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions