Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions src/Servers/Kestrel/Core/src/Http2Limits.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/// <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.
/// Value must be greater than 0, defaults to 100 streams.
/// </para>
/// </summary>
public int MaxStreamsPerConnection
Expand All @@ -44,7 +44,7 @@ public int MaxStreamsPerConnection
/// <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.
/// Value must be greater than or equal to 0, defaults to 4096 octets (4 KiB).
/// </para>
/// </summary>
public int HeaderTableSize
Expand All @@ -64,7 +64,7 @@ public int HeaderTableSize
/// <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 (16,384).
/// Value must be between 2^14 and 2^24, defaults to 2^14 octets (16 KiB).
/// </para>
/// </summary>
public int MaxFrameSize
Expand All @@ -82,9 +82,9 @@ public int MaxFrameSize
}

/// <summary>
/// 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.
/// <para>
/// 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).
/// </para>
/// </summary>
public int MaxRequestHeaderFieldSize
Expand All @@ -102,10 +102,10 @@ public int MaxRequestHeaderFieldSize
}

/// <summary>
/// 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 <see cref="InitialStreamWindowSize"/>
/// <para>
/// 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.
/// </para>
/// </summary>
public int InitialConnectionWindowSize
Expand All @@ -124,10 +124,11 @@ public int InitialConnectionWindowSize
}

/// <summary>
/// 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 <see cref="InitialConnectionWindowSize"/>
/// 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 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.
/// </para>
/// </summary>
public int InitialStreamWindowSize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,32 @@ public class SocketTransportOptions
/// The maximum length of the pending connection queue.
/// </summary>
/// <remarks>
/// Defaults to 512.
/// Defaults to 512 pending connections.
/// </remarks>
public int Backlog { get; set; } = 512;

/// <summary>
/// Gets or sets the maximum unconsumed incoming bytes the transport will buffer.
/// <para>
/// A value of <see langword="null"/> or 0 disables backpressure entirely allowing unlimited buffering.
/// Unlimited server buffering is a security risk given untrusted clients.
/// </para>
/// </summary>
/// <remarks>
/// Defaults to 1 MiB.
/// </remarks>
public long? MaxReadBufferSize { get; set; } = 1024 * 1024;

/// <summary>
/// Gets or sets the maximum outgoing bytes the transport will buffer before applying write backpressure.
/// <para>
/// A value of <see langword="null"/> or 0 disables backpressure entirely allowing unlimited buffering.
/// Unlimited server buffering is a security risk given untrusted clients.
/// </para>
/// </summary>
/// <remarks>
/// Defaults to 64 KiB.
/// </remarks>
public long? MaxWriteBufferSize { get; set; } = 64 * 1024;

/// <summary>
Expand All @@ -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.
/// </remarks>
/// <remarks>
/// Defaults to false.
/// </remarks>
public bool UnsafePreferInlineScheduling { get; set; }

/// <summary>
Expand All @@ -77,6 +94,9 @@ public class SocketTransportOptions
/// calls <see cref="Socket.Bind"/> as part of its implementation, so implementors
/// using this method do not need to call it again.
/// </remarks>
/// <remarks>
/// Defaults to <see cref="CreateBoundListenSocket"/>.
/// </remarks>
public Func<EndPoint, Socket> CreateBoundListenSocket { get; set; } = CreateDefaultBoundListenSocket;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down