Skip to content

Read KestrelServerOptions and limits from configuration - #69340

Open
marcominerva wants to merge 3 commits into
dotnet:mainfrom
marcominerva:kestrel-options
Open

marcominerva wants to merge 3 commits into
dotnet:mainfrom
marcominerva:kestrel-options

Conversation

@marcominerva

Copy link
Copy Markdown
Contributor

Only endpoints, certificates and endpoint defaults were read from the Kestrel configuration section. Everything else had to be set in code, so apps that wanted to tune limits from appsettings.json resorted to reflection-based workarounds copying properties off a bound instance.

Add ServerOptionsConfigurationBinder, a hand-written binder (no reflection, so it stays trim/AOT friendly) covering the non-endpoint part of the section:

  • AddServerHeader, AllowAlternateSchemes, AllowHostHeaderOverride, AllowResponseHeaderCompression, AllowSynchronousIO, DisableStringReuse
  • Limits: MaxResponseBufferSize, MaxRequestBufferSize, MaxRequestLineSize, MaxRequestHeadersTotalSize, MaxRequestHeaderCount, MaxRequestBodySize, KeepAliveTimeout, RequestHeadersTimeout, MaxConcurrentConnections,
    MaxConcurrentUpgradedConnections, MinRequestBodyDataRate and MinResponseDataRate
  • Limits:Http2 (all public properties) and Limits:Http3:MaxRequestHeaderFieldSize

Binding rules:

  • Only keys present in configuration are applied, so an option the app never mentions in configuration keeps its existing value.
  • A key explicitly set to null resets the corresponding nullable option, which is how MinDataRate and the unbounded limits are disabled.
  • Keys are case-insensitive and values are parsed with the invariant culture; an unparsable value throws InvalidOperationException naming the configuration path, and an out-of-range value still throws from the property setter.
  • A MinDataRate section may specify only BytesPerSecond or only GracePeriod: the unspecified half is taken from the value the option already has. If that value is null, because the rate was disabled, there is nothing to fall back on and the missing half throws.

Configuration does not win over code. The binder runs from the KestrelConfigurationLoader constructor, i.e. while the IConfigureOptions<KestrelServerOptions> pipeline is still executing, so a services.Configure<KestrelServerOptions> delegate registered after the one that calls Configure(IConfiguration) overwrites what was bound.

Reload keeps re-reading endpoints only, so a configuration change cannot clobber values the app set in code.

Fixes #4765

Copilot AI lite review requested due to automatic review settings September 16, 2026 08:15
@github-actions github-actions Bot added the area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions label Sep 16, 2026
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Sep 16, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Thanks for your PR, @marcominerva. Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Two unresolved moderate configuration-validation issues must be addressed before approval.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds trim-friendly Kestrel configuration binding for server options and limits while preserving code-over-configuration precedence and endpoint-only reload behavior.

Changes:

  • Adds manual binding for server flags, limits, HTTP/2, HTTP/3, and data rates.
  • Applies binding during loader construction and preserves endpoint-only reloads.
  • Adds tests, documentation, and configuration error messages.

Two unresolved moderate findings remain in ServerOptionsConfigurationBinder.cs: empty strings may be treated as null instead of rejected (1 vote), and scalar MinRequestBodyDataRate values may be silently ignored (3 votes).

File summaries
File Summary
src/Servers/Kestrel/Kestrel/test/KestrelConfigurationLoaderTests.cs Tests binding, parsing, precedence, null handling, and reload behavior.
src/Servers/Kestrel/Core/src/KestrelServerOptions.cs Documents configuration timing, precedence, and reload semantics.
src/Servers/Kestrel/Core/src/KestrelConfigurationLoader.cs Applies server-option binding during construction and limits reloads to endpoints.
src/Servers/Kestrel/Core/src/Internal/ServerOptionsConfigurationBinder.cs Implements manual binding; contains the two unresolved moderate findings.
src/Servers/Kestrel/Core/src/CoreStrings.resx Adds configuration error messages.
Review details

Suppressed comments (1)

src/Servers/Kestrel/Core/src/Internal/ServerOptionsConfigurationBinder.cs:220

  • IConfiguration preserves an empty string as a configured value, so converting string.Empty to null makes values such as Limits:MaxRequestHeaderCount: "" silently retain their existing option instead of reaching the parser and throwing the required InvalidOperationException for an unparsable value. Please distinguish an explicit null from an empty string (including the nullable MinDataRate parent case).
        value = configuration[key];
        if (string.IsNullOrEmpty(value))
        {
            value = null;
        }
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

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

Labels

area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Read additional KestrelServerOptions from config

2 participants