Limit concurrent HTTP/2 Streams per connection #2815
This commit is contained in:
parent
43398482a5
commit
0c2923135b
|
|
@ -566,4 +566,10 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l
|
|||
<data name="Http2StreamErrorAfterHeaders" xml:space="preserve">
|
||||
<value>An error occured after the response headers were sent, a reset is being sent.</value>
|
||||
</data>
|
||||
<data name="Http2ErrorMaxStreams" xml:space="preserve">
|
||||
<value>A new stream was refused because this connection has reached its stream limit.</value>
|
||||
</data>
|
||||
<data name="GreaterThanZeroRequired" xml:space="preserve">
|
||||
<value>A value greater than zero is required.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Limits only applicable to HTTP/2 connections.
|
||||
/// </summary>
|
||||
public class Http2Limits
|
||||
{
|
||||
private int _maxStreamsPerConnection = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Limits the number of concurrent request streams per HTTP/2 connection. Excess streams will be refused.
|
||||
/// <para>
|
||||
/// Defaults to 100
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public int MaxStreamsPerConnection
|
||||
{
|
||||
get => _maxStreamsPerConnection;
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(value), value, CoreStrings.GreaterThanZeroRequired);
|
||||
}
|
||||
_maxStreamsPerConnection = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -88,6 +88,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
_context = context;
|
||||
_frameWriter = new Http2FrameWriter(context.Transport.Output, context.Application.Input, _outputFlowControl, this, context.ConnectionId, context.ServiceContext.Log);
|
||||
_hpackDecoder = new HPackDecoder((int)_serverSettings.HeaderTableSize);
|
||||
_serverSettings.MaxConcurrentStreams = (uint)context.ServiceContext.ServerOptions.Limits.Http2.MaxStreamsPerConnection;
|
||||
}
|
||||
|
||||
public string ConnectionId => _context.ConnectionId;
|
||||
|
|
@ -98,6 +99,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
public IFeatureCollection ConnectionFeatures => _context.ConnectionFeatures;
|
||||
|
||||
internal Http2PeerSettings ServerSettings => _serverSettings;
|
||||
|
||||
public void OnInputOrOutputCompleted()
|
||||
{
|
||||
lock (_stateLock)
|
||||
|
|
@ -172,7 +175,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
if (_state != Http2ConnectionState.Closed)
|
||||
{
|
||||
await _frameWriter.WriteSettingsAsync(_serverSettings);
|
||||
await _frameWriter.WriteSettingsAsync(_serverSettings.GetNonProtocolDefaults());
|
||||
}
|
||||
|
||||
while (_state != Http2ConnectionState.Closed)
|
||||
|
|
@ -617,10 +620,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
try
|
||||
{
|
||||
// ParseFrame will not parse an InitialWindowSize > int.MaxValue.
|
||||
// int.MaxValue is the largest allowed windows size.
|
||||
var previousInitialWindowSize = (int)_clientSettings.InitialWindowSize;
|
||||
|
||||
_clientSettings.ParseFrame(_incomingFrame);
|
||||
_clientSettings.Update(_incomingFrame.GetSettings());
|
||||
|
||||
var ackTask = _frameWriter.WriteSettingsAckAsync(); // Ack before we update the windows, they could send data immediately.
|
||||
|
||||
|
|
@ -838,6 +841,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorMissingMandatoryPseudoHeaderFields, Http2ErrorCode.PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
if (_streams.Count >= _serverSettings.MaxConcurrentStreams)
|
||||
{
|
||||
throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorMaxStreams, Http2ErrorCode.REFUSED_STREAM);
|
||||
}
|
||||
|
||||
// This must be initialized before we offload the request or else we may start processing request body frames without it.
|
||||
_currentHeadersStream.InputRemaining = _currentHeadersStream.RequestHeaders.ContentLength;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,42 +1,67 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||
{
|
||||
public partial class Http2Frame
|
||||
{
|
||||
private const int SettingSize = 6; // 2 bytes for the id, 4 bytes for the value.
|
||||
|
||||
public Http2SettingsFrameFlags SettingsFlags
|
||||
{
|
||||
get => (Http2SettingsFrameFlags)Flags;
|
||||
set => Flags = (byte)value;
|
||||
}
|
||||
|
||||
public void PrepareSettings(Http2SettingsFrameFlags flags, Http2PeerSettings settings = null)
|
||||
public int SettingsCount
|
||||
{
|
||||
var settingCount = settings?.Count() ?? 0;
|
||||
get => Length / SettingSize;
|
||||
set => Length = value * SettingSize;
|
||||
}
|
||||
|
||||
Length = 6 * settingCount;
|
||||
public IList<Http2PeerSetting> GetSettings()
|
||||
{
|
||||
var settings = new Http2PeerSetting[SettingsCount];
|
||||
for (int i = 0; i < settings.Length; i++)
|
||||
{
|
||||
settings[i] = GetSetting(i);
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
private Http2PeerSetting GetSetting(int index)
|
||||
{
|
||||
var offset = index * SettingSize;
|
||||
var payload = Payload.Slice(offset);
|
||||
var id = (Http2SettingsParameter)BinaryPrimitives.ReadUInt16BigEndian(payload);
|
||||
var value = BinaryPrimitives.ReadUInt32BigEndian(payload.Slice(2));
|
||||
|
||||
return new Http2PeerSetting(id, value);
|
||||
}
|
||||
|
||||
public void PrepareSettings(Http2SettingsFrameFlags flags, IList<Http2PeerSetting> settings = null)
|
||||
{
|
||||
var settingCount = settings?.Count ?? 0;
|
||||
SettingsCount = settingCount;
|
||||
Type = Http2FrameType.SETTINGS;
|
||||
SettingsFlags = flags;
|
||||
StreamId = 0;
|
||||
|
||||
if (settings != null)
|
||||
for (int i = 0; i < settingCount; i++)
|
||||
{
|
||||
Span<byte> payload = Payload;
|
||||
foreach (var setting in settings)
|
||||
{
|
||||
payload[0] = (byte)((ushort)setting.Parameter >> 8);
|
||||
payload[1] = (byte)(ushort)setting.Parameter;
|
||||
payload[2] = (byte)(setting.Value >> 24);
|
||||
payload[3] = (byte)(setting.Value >> 16);
|
||||
payload[4] = (byte)(setting.Value >> 8);
|
||||
payload[5] = (byte)setting.Value;
|
||||
payload = payload.Slice(6);
|
||||
}
|
||||
SetSetting(i, settings[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetSetting(int index, Http2PeerSetting setting)
|
||||
{
|
||||
var offset = index * SettingSize;
|
||||
var payload = Payload.Slice(offset);
|
||||
BinaryPrimitives.WriteUInt16BigEndian(payload, (ushort)setting.Parameter);
|
||||
BinaryPrimitives.WriteUInt32BigEndian(payload.Slice(2), setting.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -270,12 +270,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
}
|
||||
}
|
||||
|
||||
public Task WriteSettingsAsync(Http2PeerSettings settings)
|
||||
public Task WriteSettingsAsync(IList<Http2PeerSetting> settings)
|
||||
{
|
||||
lock (_writeLock)
|
||||
{
|
||||
// TODO: actually send settings
|
||||
_outgoingFrame.PrepareSettings(Http2SettingsFrameFlags.NONE);
|
||||
_outgoingFrame.PrepareSettings(Http2SettingsFrameFlags.NONE, settings);
|
||||
return WriteFrameUnsynchronizedAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||
{
|
||||
public class Http2PeerSettings : IEnumerable<Http2PeerSetting>
|
||||
public class Http2PeerSettings
|
||||
{
|
||||
// Note these are protocol defaults, not Kestrel defaults.
|
||||
public const uint DefaultHeaderTableSize = 4096;
|
||||
public const bool DefaultEnablePush = true;
|
||||
public const uint DefaultMaxConcurrentStreams = uint.MaxValue;
|
||||
|
|
@ -28,20 +28,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
public uint MaxHeaderListSize { get; set; } = DefaultMaxHeaderListSize;
|
||||
|
||||
public void ParseFrame(Http2Frame frame)
|
||||
// TODO: Return the diff so we can react
|
||||
public void Update(IList<Http2PeerSetting> settings)
|
||||
{
|
||||
var settingsCount = frame.Length / 6;
|
||||
|
||||
for (var i = 0; i < settingsCount; i++)
|
||||
foreach (var setting in settings)
|
||||
{
|
||||
var offset = i * 6;
|
||||
var id = (Http2SettingsParameter)((frame.Payload[offset] << 8) | frame.Payload[offset + 1]);
|
||||
var value = (uint)((frame.Payload[offset + 2] << 24)
|
||||
| (frame.Payload[offset + 3] << 16)
|
||||
| (frame.Payload[offset + 4] << 8)
|
||||
| frame.Payload[offset + 5]);
|
||||
var value = setting.Value;
|
||||
|
||||
switch (id)
|
||||
switch (setting.Parameter)
|
||||
{
|
||||
case Http2SettingsParameter.SETTINGS_HEADER_TABLE_SIZE:
|
||||
HeaderTableSize = value;
|
||||
|
|
@ -91,16 +85,42 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
}
|
||||
}
|
||||
|
||||
public IEnumerator<Http2PeerSetting> GetEnumerator()
|
||||
// Gets the settings that are different from the protocol defaults (as opposed to the server defaults).
|
||||
internal IList<Http2PeerSetting> GetNonProtocolDefaults()
|
||||
{
|
||||
yield return new Http2PeerSetting(Http2SettingsParameter.SETTINGS_HEADER_TABLE_SIZE, HeaderTableSize);
|
||||
yield return new Http2PeerSetting(Http2SettingsParameter.SETTINGS_ENABLE_PUSH, EnablePush ? 1u : 0);
|
||||
yield return new Http2PeerSetting(Http2SettingsParameter.SETTINGS_MAX_CONCURRENT_STREAMS, MaxConcurrentStreams);
|
||||
yield return new Http2PeerSetting(Http2SettingsParameter.SETTINGS_INITIAL_WINDOW_SIZE, InitialWindowSize);
|
||||
yield return new Http2PeerSetting(Http2SettingsParameter.SETTINGS_MAX_FRAME_SIZE, MaxFrameSize);
|
||||
yield return new Http2PeerSetting(Http2SettingsParameter.SETTINGS_MAX_HEADER_LIST_SIZE, MaxHeaderListSize);
|
||||
}
|
||||
var list = new List<Http2PeerSetting>(1);
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
if (HeaderTableSize != DefaultHeaderTableSize)
|
||||
{
|
||||
list.Add(new Http2PeerSetting(Http2SettingsParameter.SETTINGS_HEADER_TABLE_SIZE, HeaderTableSize));
|
||||
}
|
||||
|
||||
if (EnablePush != DefaultEnablePush)
|
||||
{
|
||||
list.Add(new Http2PeerSetting(Http2SettingsParameter.SETTINGS_ENABLE_PUSH, EnablePush ? 1u : 0));
|
||||
}
|
||||
|
||||
if (MaxConcurrentStreams != DefaultMaxConcurrentStreams)
|
||||
{
|
||||
list.Add(new Http2PeerSetting(Http2SettingsParameter.SETTINGS_MAX_CONCURRENT_STREAMS, MaxConcurrentStreams));
|
||||
}
|
||||
|
||||
if (InitialWindowSize != DefaultInitialWindowSize)
|
||||
{
|
||||
list.Add(new Http2PeerSetting(Http2SettingsParameter.SETTINGS_INITIAL_WINDOW_SIZE, InitialWindowSize));
|
||||
}
|
||||
|
||||
if (MaxFrameSize != DefaultMaxFrameSize)
|
||||
{
|
||||
list.Add(new Http2PeerSetting(Http2SettingsParameter.SETTINGS_MAX_FRAME_SIZE, MaxFrameSize));
|
||||
}
|
||||
|
||||
if (MaxHeaderListSize != DefaultMaxHeaderListSize)
|
||||
{
|
||||
list.Add(new Http2PeerSetting(Http2SettingsParameter.SETTINGS_MAX_HEADER_LIST_SIZE, MaxHeaderListSize));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -251,6 +251,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Limits only applicable to HTTP/2 connections.
|
||||
/// </summary>
|
||||
public Http2Limits Http2 { get; } = new Http2Limits();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the request body minimum data rate in bytes/second.
|
||||
/// Setting this property to null indicates no minimum data rate should be enforced.
|
||||
|
|
|
|||
|
|
@ -2100,6 +2100,34 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
internal static string FormatHttp2StreamErrorAfterHeaders()
|
||||
=> GetString("Http2StreamErrorAfterHeaders");
|
||||
|
||||
/// <summary>
|
||||
/// A new stream was refused because this connection has reached its stream limit.
|
||||
/// </summary>
|
||||
internal static string Http2ErrorMaxStreams
|
||||
{
|
||||
get => GetString("Http2ErrorMaxStreams");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A new stream was refused because this connection has reached its stream limit.
|
||||
/// </summary>
|
||||
internal static string FormatHttp2ErrorMaxStreams()
|
||||
=> GetString("Http2ErrorMaxStreams");
|
||||
|
||||
/// <summary>
|
||||
/// A value greater than zero is required.
|
||||
/// </summary>
|
||||
internal static string GreaterThanZeroRequired
|
||||
{
|
||||
get => GetString("GreaterThanZeroRequired");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A value greater than zero is required.
|
||||
/// </summary>
|
||||
internal static string FormatGreaterThanZeroRequired()
|
||||
=> GetString("GreaterThanZeroRequired");
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -1439,6 +1439,34 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HEADERS_OverMaxStreamLimit_Refused()
|
||||
{
|
||||
_connection.ServerSettings.MaxConcurrentStreams = 1;
|
||||
|
||||
var requestBlocker = new TaskCompletionSource<object>();
|
||||
await InitializeConnectionAsync(context => requestBlocker.Task);
|
||||
|
||||
await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
|
||||
|
||||
await StartStreamAsync(3, _browserRequestHeaders, endStream: true);
|
||||
|
||||
await WaitForStreamErrorAsync(3, Http2ErrorCode.REFUSED_STREAM, CoreStrings.Http2ErrorMaxStreams);
|
||||
|
||||
requestBlocker.SetResult(0);
|
||||
|
||||
await ExpectAsync(Http2FrameType.HEADERS,
|
||||
withLength: 55,
|
||||
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
|
||||
withStreamId: 1);
|
||||
await ExpectAsync(Http2FrameType.DATA,
|
||||
withLength: 0,
|
||||
withFlags: (byte)Http2DataFrameFlags.END_STREAM,
|
||||
withStreamId: 1);
|
||||
|
||||
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HEADERS_Received_StreamIdZero_ConnectionError()
|
||||
{
|
||||
|
|
@ -2198,6 +2226,66 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
expectedErrorMessage: CoreStrings.FormatHttp2ErrorHeadersInterleaved(Http2FrameType.RST_STREAM, streamId: 1, headersStreamId: 1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SETTINGS_KestrelDefaults_Sent()
|
||||
{
|
||||
_connectionTask = _connection.ProcessRequestsAsync(new DummyApplication(_noopApplication));
|
||||
|
||||
await SendPreambleAsync().ConfigureAwait(false);
|
||||
await SendSettingsAsync();
|
||||
|
||||
var frame = await ExpectAsync(Http2FrameType.SETTINGS,
|
||||
withLength: 6,
|
||||
withFlags: 0,
|
||||
withStreamId: 0);
|
||||
|
||||
// Only non protocol defaults are sent
|
||||
Assert.Equal(1, frame.SettingsCount);
|
||||
var settings = frame.GetSettings();
|
||||
Assert.Equal(1, settings.Count);
|
||||
var setting = settings[0];
|
||||
Assert.Equal(Http2SettingsParameter.SETTINGS_MAX_CONCURRENT_STREAMS, setting.Parameter);
|
||||
Assert.Equal(100u, setting.Value);
|
||||
|
||||
await ExpectAsync(Http2FrameType.SETTINGS,
|
||||
withLength: 0,
|
||||
withFlags: (byte)Http2SettingsFrameFlags.ACK,
|
||||
withStreamId: 0);
|
||||
|
||||
await StopConnectionAsync(expectedLastStreamId: 0, ignoreNonGoAwayFrames: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SETTINGS_Custom_Sent()
|
||||
{
|
||||
_connection.ServerSettings.MaxConcurrentStreams = 1;
|
||||
|
||||
_connectionTask = _connection.ProcessRequestsAsync(new DummyApplication(_noopApplication));
|
||||
|
||||
await SendPreambleAsync().ConfigureAwait(false);
|
||||
await SendSettingsAsync();
|
||||
|
||||
var frame = await ExpectAsync(Http2FrameType.SETTINGS,
|
||||
withLength: 6,
|
||||
withFlags: 0,
|
||||
withStreamId: 0);
|
||||
|
||||
// Only non protocol defaults are sent
|
||||
Assert.Equal(1, frame.SettingsCount);
|
||||
var settings = frame.GetSettings();
|
||||
Assert.Equal(1, settings.Count);
|
||||
var setting = settings[0];
|
||||
Assert.Equal(Http2SettingsParameter.SETTINGS_MAX_CONCURRENT_STREAMS, setting.Parameter);
|
||||
Assert.Equal(1u, setting.Value);
|
||||
|
||||
await ExpectAsync(Http2FrameType.SETTINGS,
|
||||
withLength: 0,
|
||||
withFlags: (byte)Http2SettingsFrameFlags.ACK,
|
||||
withStreamId: 0);
|
||||
|
||||
await StopConnectionAsync(expectedLastStreamId: 0, ignoreNonGoAwayFrames: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SETTINGS_Received_Sends_ACK()
|
||||
{
|
||||
|
|
@ -3499,7 +3587,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
await SendSettingsAsync();
|
||||
|
||||
await ExpectAsync(Http2FrameType.SETTINGS,
|
||||
withLength: 0,
|
||||
withLength: 6,
|
||||
withFlags: 0,
|
||||
withStreamId: 0);
|
||||
|
||||
|
|
@ -3637,7 +3725,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
private Task SendSettingsAsync()
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings);
|
||||
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings.GetNonProtocolDefaults());
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
|
|
@ -3652,7 +3740,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
private Task SendSettingsWithInvalidStreamIdAsync(int streamId)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings);
|
||||
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings.GetNonProtocolDefaults());
|
||||
frame.StreamId = streamId;
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
|
@ -3660,7 +3748,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
private Task SendSettingsWithInvalidLengthAsync(int length)
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings);
|
||||
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings.GetNonProtocolDefaults());
|
||||
frame.Length = length;
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1868,7 +1868,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
await SendSettingsAsync();
|
||||
|
||||
await ExpectAsync(Http2FrameType.SETTINGS,
|
||||
withLength: 0,
|
||||
withLength: 6,
|
||||
withFlags: 0,
|
||||
withStreamId: 0);
|
||||
|
||||
|
|
@ -1937,7 +1937,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
private Task SendSettingsAsync()
|
||||
{
|
||||
var frame = new Http2Frame();
|
||||
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings);
|
||||
frame.PrepareSettings(Http2SettingsFrameFlags.NONE, _clientSettings.GetNonProtocolDefaults());
|
||||
return SendAsync(frame.Raw);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,8 +37,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
[Fact]
|
||||
public Task Server_Http2Only_Cleartext_Success()
|
||||
{
|
||||
// Expect a SETTINGS frame (type 0x4) with no payload and no flags
|
||||
return TestSuccess(HttpProtocols.Http2, Encoding.ASCII.GetString(Http2Connection.ClientPreface), "\x00\x00\x00\x04\x00\x00\x00\x00\x00");
|
||||
// Expect a SETTINGS frame (type 0x4) with default settings
|
||||
return TestSuccess(HttpProtocols.Http2, Encoding.ASCII.GetString(Http2Connection.ClientPreface),
|
||||
"\x00\x00\x06\x04\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x64");
|
||||
}
|
||||
|
||||
private async Task TestSuccess(HttpProtocols serverProtocols, string request, string expectedResponse)
|
||||
|
|
|
|||
Loading…
Reference in New Issue