| File: Http2Limits.cs | Web Access |
| Project: src\aspnetcore\src\Servers\Kestrel\Core\src\Microsoft.AspNetCore.Server.Kestrel.Core.csproj (Microsoft.AspNetCore.Server.Kestrel.Core) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Text.Json; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; namespace Microsoft.AspNetCore.Server.Kestrel.Core; /// <summary> /// Limits only applicable to HTTP/2 connections. /// </summary> public class Http2Limits { private int _maxStreamsPerConnection = 100; private int _headerTableSize = (int)Http2PeerSettings.DefaultHeaderTableSize; private int _maxFrameSize = (int)Http2PeerSettings.DefaultMaxFrameSize; private int _maxRequestHeaderFieldSize = 32 * 1024; // Matches MaxRequestHeadersTotalSize private int _initialConnectionWindowSize = 1024 * 1024; // Equal to SocketTransportOptions.MaxReadBufferSize and larger than any one single stream. private int _initialStreamWindowSize = 768 * 1024; // Larger than the default 64kb and able to use most (3/4ths) of the connection window by itself. private TimeSpan _keepAlivePingDelay = TimeSpan.MaxValue; private TimeSpan _keepAlivePingTimeout = TimeSpan.FromSeconds(20); /// <summary> /// Limits the number of concurrent request streams per HTTP/2 connection. Excess streams will be refused. /// <para> /// Value must be greater than 0, defaults to 100 streams. /// </para> /// </summary> public int MaxStreamsPerConnection { get => _maxStreamsPerConnection; set { if (value <= 0) { throw new ArgumentOutOfRangeException(nameof(value), value, CoreStrings.GreaterThanZeroRequired); } _maxStreamsPerConnection = value; } } /// <summary> /// Limits the size of the header compression tables, in octets, the HPACK encoder and decoder on the server can use. /// <para> /// Value must be greater than or equal to 0, defaults to 4096 octets (4 KiB). /// </para> /// </summary> public int HeaderTableSize { get => _headerTableSize; set { if (value < 0) { throw new ArgumentOutOfRangeException(nameof(value), value, CoreStrings.GreaterThanOrEqualToZeroRequired); } _headerTableSize = value; } } /// <summary> /// Indicates the size of the largest frame payload that is allowed to be received, in octets. The size must be between 2^14 and 2^24-1. /// <para> /// Value must be between 2^14 and 2^24, defaults to 2^14 octets (16 KiB). /// </para> /// </summary> public int MaxFrameSize { get => _maxFrameSize; set { if (value < Http2PeerSettings.MinAllowedMaxFrameSize || value > Http2PeerSettings.MaxAllowedMaxFrameSize) { throw new ArgumentOutOfRangeException(nameof(value), value, CoreStrings.FormatArgumentOutOfRange(Http2PeerSettings.MinAllowedMaxFrameSize, Http2PeerSettings.MaxAllowedMaxFrameSize)); } _maxFrameSize = value; } } /// <summary> /// Indicates the size of the maximum allowed size of a request header field sequence, in octets. This limit applies to both name and value sequences in their compressed and uncompressed representations. /// <para> /// Value must be greater than 0, defaults to 2^15 octets (32 KiB). /// </para> /// </summary> public int MaxRequestHeaderFieldSize { get => _maxRequestHeaderFieldSize; set { if (value <= 0) { throw new ArgumentOutOfRangeException(nameof(value), value, CoreStrings.GreaterThanZeroRequired); } _maxRequestHeaderFieldSize = value; } } /// <summary> /// Indicates how much request body data, in bytes, the server is willing to receive and buffer at a time aggregated across all /// requests (streams) per connection. Note requests are also limited by <see cref="InitialStreamWindowSize"/> /// <para> /// Value must be greater than or equal to 64 KiB and less than 2 GiB, defaults to 1 MiB. /// </para> /// </summary> public int InitialConnectionWindowSize { get => _initialConnectionWindowSize; set { if (value < Http2PeerSettings.DefaultInitialWindowSize || value > Http2PeerSettings.MaxWindowSize) { throw new ArgumentOutOfRangeException(nameof(value), value, CoreStrings.FormatArgumentOutOfRange(Http2PeerSettings.DefaultInitialWindowSize, Http2PeerSettings.MaxWindowSize)); } _initialConnectionWindowSize = value; } } /// <summary> /// Indicates how much request body data, in bytes, the server is willing to receive and buffer at a time per stream. /// Note connections are also limited by <see cref="InitialConnectionWindowSize"/>. There must be space in both the stream /// window and connection window for a client to upload request body data. /// <para> /// Value must be greater than or equal to 64 KiB and less than 2 GiB, defaults to 768 KiB. /// </para> /// </summary> public int InitialStreamWindowSize { get => _initialStreamWindowSize; set { if (value < Http2PeerSettings.DefaultInitialWindowSize || value > Http2PeerSettings.MaxWindowSize) { throw new ArgumentOutOfRangeException(nameof(value), value, CoreStrings.FormatArgumentOutOfRange(Http2PeerSettings.DefaultInitialWindowSize, Http2PeerSettings.MaxWindowSize)); } _initialStreamWindowSize = value; } } /// <summary> /// Gets or sets the keep alive ping delay. The server will send a keep alive ping to the client if it /// doesn't receive any frames on a connection for this period of time. This property is used together with /// <see cref="KeepAlivePingTimeout"/> to close broken connections. /// <para> /// Delay value must be greater than or equal to 1 second. Set to <see cref="TimeSpan.MaxValue"/> to /// disable the keep alive ping. /// Defaults to <see cref="TimeSpan.MaxValue"/>. /// </para> /// </summary> public TimeSpan KeepAlivePingDelay { get => _keepAlivePingDelay; set { // Keep alive uses Kestrel's system clock which has a 1 second resolution. Time is greater or equal to clock resolution. if (value < Heartbeat.Interval && value != Timeout.InfiniteTimeSpan) { throw new ArgumentOutOfRangeException(nameof(value), CoreStrings.FormatArgumentTimeSpanGreaterOrEqual(Heartbeat.Interval)); } _keepAlivePingDelay = value != Timeout.InfiniteTimeSpan ? value : TimeSpan.MaxValue; } } /// <summary> /// Gets or sets the keep alive ping timeout. Keep alive pings are sent when a period of inactivity exceeds /// the configured <see cref="KeepAlivePingDelay"/> value. The server will close the connection if it /// doesn't receive any frames within the timeout. /// <para> /// Timeout must be greater than or equal to 1 second. Set to <see cref="TimeSpan.MaxValue"/> to /// disable the keep alive ping timeout. /// Defaults to 20 seconds. /// </para> /// </summary> public TimeSpan KeepAlivePingTimeout { get => _keepAlivePingTimeout; set { // Keep alive uses Kestrel's system clock which has a 1 second resolution. Time is greater or equal to clock resolution. if (value < Heartbeat.Interval && value != Timeout.InfiniteTimeSpan) { throw new ArgumentOutOfRangeException(nameof(value), CoreStrings.FormatArgumentTimeSpanGreaterOrEqual(Heartbeat.Interval)); } _keepAlivePingTimeout = value != Timeout.InfiniteTimeSpan ? value : TimeSpan.MaxValue; } } internal void Serialize(Utf8JsonWriter writer) { writer.WritePropertyName(nameof(MaxStreamsPerConnection)); writer.WriteNumberValue(MaxStreamsPerConnection); writer.WritePropertyName(nameof(HeaderTableSize)); writer.WriteNumberValue(HeaderTableSize); writer.WritePropertyName(nameof(MaxFrameSize)); writer.WriteNumberValue(MaxFrameSize); writer.WritePropertyName(nameof(MaxRequestHeaderFieldSize)); writer.WriteNumberValue(MaxRequestHeaderFieldSize); writer.WritePropertyName(nameof(InitialConnectionWindowSize)); writer.WriteNumberValue(InitialConnectionWindowSize); writer.WritePropertyName(nameof(InitialStreamWindowSize)); writer.WriteNumberValue(InitialStreamWindowSize); writer.WriteString(nameof(KeepAlivePingDelay), KeepAlivePingDelay.ToString()); writer.WriteString(nameof(KeepAlivePingTimeout), KeepAlivePingTimeout.ToString()); } }