diff --git a/src/Servers/Kestrel/Core/src/Http2Limits.cs b/src/Servers/Kestrel/Core/src/Http2Limits.cs index 6833ec871ea9..1f5877e812d7 100644 --- a/src/Servers/Kestrel/Core/src/Http2Limits.cs +++ b/src/Servers/Kestrel/Core/src/Http2Limits.cs @@ -16,15 +16,15 @@ public class Http2Limits private int _headerTableSize = (int)Http2PeerSettings.DefaultHeaderTableSize; private int _maxFrameSize = (int)Http2PeerSettings.DefaultMaxFrameSize; private int _maxRequestHeaderFieldSize = (int)Http2PeerSettings.DefaultMaxFrameSize; - private int _initialConnectionWindowSize = 1024 * 128; // Larger than the default 64kb, and larger than any one single stream. - private int _initialStreamWindowSize = 1024 * 96; // Larger than the default 64kb + 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); /// /// Limits the number of concurrent request streams per HTTP/2 connection. Excess streams will be refused. /// - /// Value must be greater than 0, defaults to 100. + /// Value must be greater than 0, defaults to 100 streams. /// /// public int MaxStreamsPerConnection @@ -44,7 +44,7 @@ public int MaxStreamsPerConnection /// /// Limits the size of the header compression tables, in octets, the HPACK encoder and decoder on the server can use. /// - /// Value must be greater than or equal to 0, defaults to 4096. + /// Value must be greater than or equal to 0, defaults to 4096 octets (4 KiB). /// /// public int HeaderTableSize @@ -64,7 +64,7 @@ public int HeaderTableSize /// /// 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. /// - /// Value must be between 2^14 and 2^24, defaults to 2^14 (16,384). + /// Value must be between 2^14 and 2^24, defaults to 2^14 octets (16 KiB). /// /// public int MaxFrameSize @@ -82,9 +82,9 @@ public int MaxFrameSize } /// - /// Indicates the size of the maximum allowed size of a request header field sequence. This limit applies to both name and value sequences in their compressed and uncompressed representations. + /// 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. /// - /// Value must be greater than 0, defaults to 2^14 (16,384). + /// Value must be greater than 0, defaults to 2^14 octets (16 KiB). /// /// public int MaxRequestHeaderFieldSize @@ -102,10 +102,10 @@ public int MaxRequestHeaderFieldSize } /// - /// Indicates how much request body data the server is willing to receive and buffer at a time aggregated across all + /// 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 /// - /// Value must be greater than or equal to 65,535 and less than 2^31, defaults to 128 kb. + /// Value must be greater than or equal to 64 KiB and less than 2 GiB, defaults to 1 MiB. /// /// public int InitialConnectionWindowSize @@ -124,10 +124,11 @@ public int InitialConnectionWindowSize } /// - /// Indicates how much request body data the server is willing to receive and buffer at a time per stream. - /// Note connections are also limited by + /// 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 . There must be space in both the stream + /// window and connection window for a client to upload request body data. /// - /// Value must be greater than or equal to 65,535 and less than 2^31, defaults to 96 kb. + /// Value must be greater than or equal to 64 KiB and less than 2 GiB, defaults to 768 KiB. /// /// public int InitialStreamWindowSize diff --git a/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs b/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs index 3483cf03fbd7..a43c1ab7a646 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs +++ b/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs @@ -41,18 +41,32 @@ public class SocketTransportOptions /// The maximum length of the pending connection queue. /// /// - /// Defaults to 512. + /// Defaults to 512 pending connections. /// public int Backlog { get; set; } = 512; /// /// Gets or sets the maximum unconsumed incoming bytes the transport will buffer. + /// + /// A value of or 0 disables backpressure entirely allowing unlimited buffering. + /// Unlimited server buffering is a security risk given untrusted clients. + /// /// + /// + /// Defaults to 1 MiB. + /// public long? MaxReadBufferSize { get; set; } = 1024 * 1024; /// /// Gets or sets the maximum outgoing bytes the transport will buffer before applying write backpressure. + /// + /// A value of or 0 disables backpressure entirely allowing unlimited buffering. + /// Unlimited server buffering is a security risk given untrusted clients. + /// /// + /// + /// Defaults to 64 KiB. + /// public long? MaxWriteBufferSize { get; set; } = 64 * 1024; /// @@ -65,6 +79,9 @@ public class SocketTransportOptions /// This setting can make performance worse if there is expensive work that will end up holding onto the IO thread for longer than needed. /// Test to make sure this setting helps performance. /// + /// + /// Defaults to false. + /// public bool UnsafePreferInlineScheduling { get; set; } /// @@ -77,6 +94,9 @@ public class SocketTransportOptions /// calls as part of its implementation, so implementors /// using this method do not need to call it again. /// + /// + /// Defaults to . + /// public Func CreateBoundListenSocket { get; set; } = CreateDefaultBoundListenSocket; /// diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs index 0cacdb447eb7..e2d71746cb82 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs @@ -1345,6 +1345,10 @@ await ExpectAsync(Http2FrameType.HEADERS, [Fact] public async Task DATA_BufferRequestBodyLargerThanStreamSizeSmallerThanConnectionPipe_Works() { + // This test was not written to allow for arbitrary initial stream sizes. It was tuned to the old defaults. + _serviceContext.ServerOptions.Limits.Http2.InitialConnectionWindowSize = 128 * 1024; + _serviceContext.ServerOptions.Limits.Http2.InitialStreamWindowSize = 96 * 1024; + var initialStreamWindowSize = _serviceContext.ServerOptions.Limits.Http2.InitialStreamWindowSize; var framesInStreamWindow = initialStreamWindowSize / Http2PeerSettings.DefaultMaxFrameSize; var initialConnectionWindowSize = _serviceContext.ServerOptions.Limits.Http2.InitialConnectionWindowSize; @@ -3374,7 +3378,7 @@ public async Task SETTINGS_KestrelDefaults_Sent() setting = settings[1]; Assert.Equal(Http2SettingsParameter.SETTINGS_INITIAL_WINDOW_SIZE, setting.Parameter); - Assert.Equal(96 * 1024u, setting.Value); + Assert.Equal(768 * 1024u, setting.Value); setting = settings[2]; Assert.Equal(Http2SettingsParameter.SETTINGS_MAX_HEADER_LIST_SIZE, setting.Parameter); @@ -3389,7 +3393,7 @@ public async Task SETTINGS_KestrelDefaults_Sent() withFlags: (byte)Http2SettingsFrameFlags.NONE, withStreamId: 0); - Assert.Equal(1024 * 128 - (int)Http2PeerSettings.DefaultInitialWindowSize, update.WindowUpdateSizeIncrement); + Assert.Equal(1024 * 1024 - (int)Http2PeerSettings.DefaultInitialWindowSize, update.WindowUpdateSizeIncrement); await ExpectAsync(Http2FrameType.SETTINGS, withLength: 0, @@ -3448,7 +3452,7 @@ public async Task SETTINGS_Custom_Sent() withFlags: (byte)Http2SettingsFrameFlags.NONE, withStreamId: 0); - Assert.Equal(1024 * 128u - Http2PeerSettings.DefaultInitialWindowSize, (uint)update.WindowUpdateSizeIncrement); + Assert.Equal(1024 * 1024u - Http2PeerSettings.DefaultInitialWindowSize, (uint)update.WindowUpdateSizeIncrement); await ExpectAsync(Http2FrameType.SETTINGS, withLength: 0, diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http3/Http3TimeoutTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http3/Http3TimeoutTests.cs index 45a0dd5f1901..fb741c527cb3 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http3/Http3TimeoutTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http3/Http3TimeoutTests.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Features; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Logging; diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpProtocolSelectionTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpProtocolSelectionTests.cs index 753a1cc0e206..16a890852584 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpProtocolSelectionTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpProtocolSelectionTests.cs @@ -43,12 +43,12 @@ public Task Server_Http2Only_Cleartext_Success() 0x00, 0x00, 0x18, // Payload Length (6 * settings count) 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // SETTINGS frame (type 0x04) 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, // Connection limit (100) - 0x00, 0x04, 0x00, 0x01, 0x80, 0x00, // Initial stream window size (96 KiB) + 0x00, 0x04, 0x00, 0x0C, 0x00, 0x00, // Initial stream window size (768 KiB) 0x00, 0x06, 0x00, 0x00, 0x80, 0x00, // Header size limit (32 KiB) 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, // CONNECT enabled 0x00, 0x00, 0x04, // Payload Length (4) 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // WINDOW_UPDATE frame (type 0x08) - 0x00, 0x01, 0x00, 0x01, // Diff between configured and protocol default (128 KiB - 0XFFFF) + 0x00, 0x0F, 0x00, 0x01, // Diff between configured and protocol default (1 MiB - 0XFFFF) }; return TestSuccess(HttpProtocols.Http2,