Add HPack dynamic compression (#20058)

This commit is contained in:
James Newton-King 2020-04-02 12:22:20 +13:00 committed by GitHub
parent c378f3f275
commit 0e4bcf602c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 2022 additions and 492 deletions

View File

@ -127,6 +127,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
{ {
public KestrelServerOptions() { } public KestrelServerOptions() { }
public bool AddServerHeader { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public bool AddServerHeader { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public bool AllowResponseHeaderCompression { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public bool AllowSynchronousIO { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public bool AllowSynchronousIO { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public System.IServiceProvider ApplicationServices { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public System.IServiceProvider ApplicationServices { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public Microsoft.AspNetCore.Server.Kestrel.KestrelConfigurationLoader ConfigurationLoader { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } } public Microsoft.AspNetCore.Server.Kestrel.KestrelConfigurationLoader ConfigurationLoader { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }

View File

@ -599,4 +599,7 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l
<data name="TransportNotFound" xml:space="preserve"> <data name="TransportNotFound" xml:space="preserve">
<value>Unable to resolve service for type 'Microsoft.AspNetCore.Connections.IConnectionListenerFactory' while attempting to activate 'Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer'.</value> <value>Unable to resolve service for type 'Microsoft.AspNetCore.Connections.IConnectionListenerFactory' while attempting to activate 'Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer'.</value>
</data> </data>
<data name="GreaterThanOrEqualToZeroRequired" xml:space="preserve">
<value>A value greater than or equal to zero is required.</value>
</data>
</root> </root>

View File

@ -39,9 +39,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
} }
/// <summary> /// <summary>
/// Limits the size of the header compression table, in octets, the HPACK decoder on the server can use. /// Limits the size of the header compression tables, in octets, the HPACK encoder and decoder on the server can use.
/// <para> /// <para>
/// Value must be greater than 0, defaults to 4096 /// Value must be greater than or equal to 0, defaults to 4096
/// </para> /// </para>
/// </summary> /// </summary>
public int HeaderTableSize public int HeaderTableSize
@ -49,9 +49,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
get => _headerTableSize; get => _headerTableSize;
set set
{ {
if (value <= 0) if (value < 0)
{ {
throw new ArgumentOutOfRangeException(nameof(value), value, CoreStrings.GreaterThanZeroRequired); throw new ArgumentOutOfRangeException(nameof(value), value, CoreStrings.GreaterThanOrEqualToZeroRequired);
} }
_headerTableSize = value; _headerTableSize = value;

View File

@ -4,7 +4,6 @@
using System; using System;
using System.Net.Http; using System.Net.Http;
using System.Net.Http.HPack; using System.Net.Http.HPack;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{ {
@ -13,57 +12,105 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
/// <summary> /// <summary>
/// Begin encoding headers in the first HEADERS frame. /// Begin encoding headers in the first HEADERS frame.
/// </summary> /// </summary>
public static bool BeginEncodeHeaders(int statusCode, Http2HeadersEnumerator headersEnumerator, Span<byte> buffer, out int length) public static bool BeginEncodeHeaders(int statusCode, HPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span<byte> buffer, out int length)
{ {
if (!HPackEncoder.EncodeStatusHeader(statusCode, buffer, out var statusCodeLength)) length = 0;
if (!hpackEncoder.EnsureDynamicTableSizeUpdate(buffer, out var sizeUpdateLength))
{ {
throw new HPackEncodingException(SR.net_http_hpack_encode_failure); throw new HPackEncodingException(SR.net_http_hpack_encode_failure);
} }
length += sizeUpdateLength;
if (!EncodeStatusHeader(statusCode, hpackEncoder, buffer.Slice(length), out var statusCodeLength))
{
throw new HPackEncodingException(SR.net_http_hpack_encode_failure);
}
length += statusCodeLength;
if (!headersEnumerator.MoveNext()) if (!headersEnumerator.MoveNext())
{ {
length = statusCodeLength;
return true; return true;
} }
// We're ok with not throwing if no headers were encoded because we've already encoded the status. // We're ok with not throwing if no headers were encoded because we've already encoded the status.
// There is a small chance that the header will encode if there is no other content in the next HEADERS frame. // There is a small chance that the header will encode if there is no other content in the next HEADERS frame.
var done = EncodeHeaders(headersEnumerator, buffer.Slice(statusCodeLength), throwIfNoneEncoded: false, out var headersLength); var done = EncodeHeadersCore(hpackEncoder, headersEnumerator, buffer.Slice(length), throwIfNoneEncoded: false, out var headersLength);
length = statusCodeLength + headersLength; length += headersLength;
return done; return done;
} }
/// <summary> /// <summary>
/// Begin encoding headers in the first HEADERS frame. /// Begin encoding headers in the first HEADERS frame.
/// </summary> /// </summary>
public static bool BeginEncodeHeaders(Http2HeadersEnumerator headersEnumerator, Span<byte> buffer, out int length) public static bool BeginEncodeHeaders(HPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span<byte> buffer, out int length)
{ {
length = 0;
if (!hpackEncoder.EnsureDynamicTableSizeUpdate(buffer, out var sizeUpdateLength))
{
throw new HPackEncodingException(SR.net_http_hpack_encode_failure);
}
length += sizeUpdateLength;
if (!headersEnumerator.MoveNext()) if (!headersEnumerator.MoveNext())
{ {
length = 0;
return true; return true;
} }
return EncodeHeaders(headersEnumerator, buffer, throwIfNoneEncoded: true, out length); var done = EncodeHeadersCore(hpackEncoder, headersEnumerator, buffer.Slice(length), throwIfNoneEncoded: true, out var headersLength);
length += headersLength;
return done;
} }
/// <summary> /// <summary>
/// Continue encoding headers in the next HEADERS frame. The enumerator should already have a current value. /// Continue encoding headers in the next HEADERS frame. The enumerator should already have a current value.
/// </summary> /// </summary>
public static bool ContinueEncodeHeaders(Http2HeadersEnumerator headersEnumerator, Span<byte> buffer, out int length) public static bool ContinueEncodeHeaders(HPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span<byte> buffer, out int length)
{ {
return EncodeHeaders(headersEnumerator, buffer, throwIfNoneEncoded: true, out length); return EncodeHeadersCore(hpackEncoder, headersEnumerator, buffer, throwIfNoneEncoded: true, out length);
} }
private static bool EncodeHeaders(Http2HeadersEnumerator headersEnumerator, Span<byte> buffer, bool throwIfNoneEncoded, out int length) private static bool EncodeStatusHeader(int statusCode, HPackEncoder hpackEncoder, Span<byte> buffer, out int length)
{
switch (statusCode)
{
case 200:
case 204:
case 206:
case 304:
case 400:
case 404:
case 500:
// Status codes which exist in the HTTP/2 StaticTable.
return HPackEncoder.EncodeIndexedHeaderField(H2StaticTable.StatusIndex[statusCode], buffer, out length);
default:
const string name = ":status";
var value = StatusCodes.ToStatusString(statusCode);
return hpackEncoder.EncodeHeader(buffer, H2StaticTable.Status200, HeaderEncodingHint.Index, name, value, out length);
}
}
private static bool EncodeHeadersCore(HPackEncoder hpackEncoder, Http2HeadersEnumerator headersEnumerator, Span<byte> buffer, bool throwIfNoneEncoded, out int length)
{ {
var currentLength = 0; var currentLength = 0;
do do
{ {
if (!EncodeHeader(headersEnumerator.KnownHeaderType, headersEnumerator.Current.Key, headersEnumerator.Current.Value, buffer.Slice(currentLength), out int headerLength)) var staticTableId = headersEnumerator.HPackStaticTableId;
var name = headersEnumerator.Current.Key;
var value = headersEnumerator.Current.Value;
var hint = ResolveHeaderEncodingHint(staticTableId, name);
if (!hpackEncoder.EncodeHeader(
buffer.Slice(currentLength),
staticTableId,
hint,
name,
value,
out var headerLength))
{ {
// The the header wasn't written and no headers have been written then the header is too large. // If the header wasn't written, and no headers have been written, then the header is too large.
// Throw an error to avoid an infinite loop of attempting to write large header. // Throw an error to avoid an infinite loop of attempting to write large header.
if (currentLength == 0 && throwIfNoneEncoded) if (currentLength == 0 && throwIfNoneEncoded)
{ {
@ -79,79 +126,48 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
while (headersEnumerator.MoveNext()); while (headersEnumerator.MoveNext());
length = currentLength; length = currentLength;
return true; return true;
} }
private static bool EncodeHeader(KnownHeaderType knownHeaderType, string name, string value, Span<byte> buffer, out int length) private static HeaderEncodingHint ResolveHeaderEncodingHint(int staticTableId, string name)
{ {
var hPackStaticTableId = GetResponseHeaderStaticTableId(knownHeaderType); HeaderEncodingHint hint;
if (IsSensitive(staticTableId, name))
if (hPackStaticTableId == -1)
{ {
return HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingNewName(name, value, buffer, out length); hint = HeaderEncodingHint.NeverIndex;
}
else if (IsNotDynamicallyIndexed(staticTableId))
{
hint = HeaderEncodingHint.IgnoreIndex;
} }
else else
{ {
return HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexing(hPackStaticTableId, value, buffer, out length); hint = HeaderEncodingHint.Index;
} }
return hint;
} }
private static int GetResponseHeaderStaticTableId(KnownHeaderType responseHeaderType) private static bool IsSensitive(int staticTableIndex, string name)
{ {
switch (responseHeaderType) // Set-Cookie could contain sensitive data.
if (staticTableIndex == H2StaticTable.SetCookie)
{ {
case KnownHeaderType.CacheControl: return true;
return H2StaticTable.CacheControl;
case KnownHeaderType.Date:
return H2StaticTable.Date;
case KnownHeaderType.TransferEncoding:
return H2StaticTable.TransferEncoding;
case KnownHeaderType.Via:
return H2StaticTable.Via;
case KnownHeaderType.Allow:
return H2StaticTable.Allow;
case KnownHeaderType.ContentType:
return H2StaticTable.ContentType;
case KnownHeaderType.ContentEncoding:
return H2StaticTable.ContentEncoding;
case KnownHeaderType.ContentLanguage:
return H2StaticTable.ContentLanguage;
case KnownHeaderType.ContentLocation:
return H2StaticTable.ContentLocation;
case KnownHeaderType.ContentRange:
return H2StaticTable.ContentRange;
case KnownHeaderType.Expires:
return H2StaticTable.Expires;
case KnownHeaderType.LastModified:
return H2StaticTable.LastModified;
case KnownHeaderType.AcceptRanges:
return H2StaticTable.AcceptRanges;
case KnownHeaderType.Age:
return H2StaticTable.Age;
case KnownHeaderType.ETag:
return H2StaticTable.ETag;
case KnownHeaderType.Location:
return H2StaticTable.Location;
case KnownHeaderType.ProxyAuthenticate:
return H2StaticTable.ProxyAuthenticate;
case KnownHeaderType.RetryAfter:
return H2StaticTable.RetryAfter;
case KnownHeaderType.Server:
return H2StaticTable.Server;
case KnownHeaderType.SetCookie:
return H2StaticTable.SetCookie;
case KnownHeaderType.Vary:
return H2StaticTable.Vary;
case KnownHeaderType.WWWAuthenticate:
return H2StaticTable.WwwAuthenticate;
case KnownHeaderType.AccessControlAllowOrigin:
return H2StaticTable.AccessControlAllowOrigin;
case KnownHeaderType.ContentLength:
return H2StaticTable.ContentLength;
default:
return -1;
} }
if (string.Equals(name, "Content-Disposition", StringComparison.OrdinalIgnoreCase))
{
return true;
}
return false;
}
private static bool IsNotDynamicallyIndexed(int staticTableIndex)
{
// Content-Length is added to static content. Content length is different for each
// file, and is unlikely to be reused because of browser caching.
return staticTableIndex == H2StaticTable.ContentLength;
} }
} }
} }

View File

@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
httpLimits.MinResponseDataRate, httpLimits.MinResponseDataRate,
context.ConnectionId, context.ConnectionId,
context.MemoryPool, context.MemoryPool,
context.ServiceContext.Log); context.ServiceContext);
var inputOptions = new PipeOptions(pool: context.MemoryPool, var inputOptions = new PipeOptions(pool: context.MemoryPool,
readerScheduler: context.ServiceContext.Scheduler, readerScheduler: context.ServiceContext.Scheduler,
@ -743,6 +743,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
} }
} }
// Maximum HPack encoder size is limited by Http2Limits.HeaderTableSize, configured max the server.
//
// Note that the client HPack decoder doesn't care about the ACK so we don't need to lock sending the
// ACK and updating the table size on the server together.
// The client will wait until a size agreed upon by it (sent in SETTINGS_HEADER_TABLE_SIZE) and the
// server (sent as a dynamic table size update in the next HEADERS frame) is received before applying
// the new size.
_frameWriter.UpdateMaxHeaderTableSize(Math.Min(_clientSettings.HeaderTableSize, (uint)Limits.Http2.HeaderTableSize));
return ackTask.AsTask(); return ackTask.AsTask();
} }
catch (Http2SettingsParameterOutOfRangeException ex) catch (Http2SettingsParameterOutOfRangeException ex)

View File

@ -38,6 +38,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
private readonly ITimeoutControl _timeoutControl; private readonly ITimeoutControl _timeoutControl;
private readonly MinDataRate _minResponseDataRate; private readonly MinDataRate _minResponseDataRate;
private readonly TimingPipeFlusher _flusher; private readonly TimingPipeFlusher _flusher;
private readonly HPackEncoder _hpackEncoder;
private uint _maxFrameSize = Http2PeerSettings.MinAllowedMaxFrameSize; private uint _maxFrameSize = Http2PeerSettings.MinAllowedMaxFrameSize;
private byte[] _headerEncodingBuffer; private byte[] _headerEncodingBuffer;
@ -55,7 +56,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
MinDataRate minResponseDataRate, MinDataRate minResponseDataRate,
string connectionId, string connectionId,
MemoryPool<byte> memoryPool, MemoryPool<byte> memoryPool,
IKestrelTrace log) ServiceContext serviceContext)
{ {
// Allow appending more data to the PipeWriter when a flush is pending. // Allow appending more data to the PipeWriter when a flush is pending.
_outputWriter = new ConcurrentPipeWriter(outputPipeWriter, memoryPool, _writeLock); _outputWriter = new ConcurrentPipeWriter(outputPipeWriter, memoryPool, _writeLock);
@ -63,12 +64,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_http2Connection = http2Connection; _http2Connection = http2Connection;
_connectionOutputFlowControl = connectionOutputFlowControl; _connectionOutputFlowControl = connectionOutputFlowControl;
_connectionId = connectionId; _connectionId = connectionId;
_log = log; _log = serviceContext.Log;
_timeoutControl = timeoutControl; _timeoutControl = timeoutControl;
_minResponseDataRate = minResponseDataRate; _minResponseDataRate = minResponseDataRate;
_flusher = new TimingPipeFlusher(_outputWriter, timeoutControl, log); _flusher = new TimingPipeFlusher(_outputWriter, timeoutControl, serviceContext.Log);
_outgoingFrame = new Http2Frame(); _outgoingFrame = new Http2Frame();
_headerEncodingBuffer = new byte[_maxFrameSize]; _headerEncodingBuffer = new byte[_maxFrameSize];
_hpackEncoder = new HPackEncoder(serviceContext.ServerOptions.AllowResponseHeaderCompression);
}
public void UpdateMaxHeaderTableSize(uint maxHeaderTableSize)
{
lock (_writeLock)
{
_hpackEncoder.UpdateMaxHeaderTableSize(maxHeaderTableSize);
}
} }
public void UpdateMaxFrameSize(uint maxFrameSize) public void UpdateMaxFrameSize(uint maxFrameSize)
@ -175,7 +186,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_headersEnumerator.Initialize(headers); _headersEnumerator.Initialize(headers);
_outgoingFrame.PrepareHeaders(headerFrameFlags, streamId); _outgoingFrame.PrepareHeaders(headerFrameFlags, streamId);
var buffer = _headerEncodingBuffer.AsSpan(); var buffer = _headerEncodingBuffer.AsSpan();
var done = HPackHeaderWriter.BeginEncodeHeaders(statusCode, _headersEnumerator, buffer, out var payloadLength); var done = HPackHeaderWriter.BeginEncodeHeaders(statusCode, _hpackEncoder, _headersEnumerator, buffer, out var payloadLength);
FinishWritingHeaders(streamId, payloadLength, done); FinishWritingHeaders(streamId, payloadLength, done);
} }
catch (HPackEncodingException hex) catch (HPackEncodingException hex)
@ -201,7 +212,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_headersEnumerator.Initialize(headers); _headersEnumerator.Initialize(headers);
_outgoingFrame.PrepareHeaders(Http2HeadersFrameFlags.END_STREAM, streamId); _outgoingFrame.PrepareHeaders(Http2HeadersFrameFlags.END_STREAM, streamId);
var buffer = _headerEncodingBuffer.AsSpan(); var buffer = _headerEncodingBuffer.AsSpan();
var done = HPackHeaderWriter.BeginEncodeHeaders(_headersEnumerator, buffer, out var payloadLength); var done = HPackHeaderWriter.BeginEncodeHeaders(_hpackEncoder, _headersEnumerator, buffer, out var payloadLength);
FinishWritingHeaders(streamId, payloadLength, done); FinishWritingHeaders(streamId, payloadLength, done);
} }
catch (HPackEncodingException hex) catch (HPackEncodingException hex)
@ -230,7 +241,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
{ {
_outgoingFrame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId); _outgoingFrame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId);
done = HPackHeaderWriter.ContinueEncodeHeaders(_headersEnumerator, buffer, out payloadLength); done = HPackHeaderWriter.ContinueEncodeHeaders(_hpackEncoder, _headersEnumerator, buffer, out payloadLength);
_outgoingFrame.PayloadLength = payloadLength; _outgoingFrame.PayloadLength = payloadLength;
if (done) if (done)

View File

@ -3,6 +3,7 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http.HPack;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
@ -15,8 +16,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
private HttpResponseTrailers.Enumerator _trailersEnumerator; private HttpResponseTrailers.Enumerator _trailersEnumerator;
private IEnumerator<KeyValuePair<string, StringValues>> _genericEnumerator; private IEnumerator<KeyValuePair<string, StringValues>> _genericEnumerator;
private StringValues.Enumerator _stringValuesEnumerator; private StringValues.Enumerator _stringValuesEnumerator;
private KnownHeaderType _knownHeaderType;
public KnownHeaderType KnownHeaderType { get; private set; } public int HPackStaticTableId => GetResponseHeaderStaticTableId(_knownHeaderType);
public KeyValuePair<string, string> Current { get; private set; } public KeyValuePair<string, string> Current { get; private set; }
object IEnumerator.Current => Current; object IEnumerator.Current => Current;
@ -33,7 +35,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_stringValuesEnumerator = default; _stringValuesEnumerator = default;
Current = default; Current = default;
KnownHeaderType = default; _knownHeaderType = default;
} }
public void Initialize(HttpResponseTrailers headers) public void Initialize(HttpResponseTrailers headers)
@ -45,7 +47,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_stringValuesEnumerator = default; _stringValuesEnumerator = default;
Current = default; Current = default;
KnownHeaderType = default; _knownHeaderType = default;
} }
public void Initialize(IDictionary<string, StringValues> headers) public void Initialize(IDictionary<string, StringValues> headers)
@ -57,7 +59,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_stringValuesEnumerator = default; _stringValuesEnumerator = default;
Current = default; Current = default;
KnownHeaderType = default; _knownHeaderType = default;
} }
public bool MoveNext() public bool MoveNext()
@ -110,7 +112,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
else else
{ {
enumerator = _genericEnumerator.Current.Value.GetEnumerator(); enumerator = _genericEnumerator.Current.Value.GetEnumerator();
KnownHeaderType = default; _knownHeaderType = default;
return true; return true;
} }
} }
@ -124,7 +126,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
else else
{ {
enumerator = _trailersEnumerator.Current.Value.GetEnumerator(); enumerator = _trailersEnumerator.Current.Value.GetEnumerator();
KnownHeaderType = _trailersEnumerator.CurrentKnownType; _knownHeaderType = _trailersEnumerator.CurrentKnownType;
return true; return true;
} }
} }
@ -138,7 +140,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
else else
{ {
enumerator = _headersEnumerator.Current.Value.GetEnumerator(); enumerator = _headersEnumerator.Current.Value.GetEnumerator();
KnownHeaderType = _headersEnumerator.CurrentKnownType; _knownHeaderType = _headersEnumerator.CurrentKnownType;
return true; return true;
} }
} }
@ -159,11 +161,68 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_headersEnumerator.Reset(); _headersEnumerator.Reset();
} }
_stringValuesEnumerator = default; _stringValuesEnumerator = default;
KnownHeaderType = default; _knownHeaderType = default;
} }
public void Dispose() public void Dispose()
{ {
} }
internal static int GetResponseHeaderStaticTableId(KnownHeaderType responseHeaderType)
{
switch (responseHeaderType)
{
case KnownHeaderType.CacheControl:
return H2StaticTable.CacheControl;
case KnownHeaderType.Date:
return H2StaticTable.Date;
case KnownHeaderType.TransferEncoding:
return H2StaticTable.TransferEncoding;
case KnownHeaderType.Via:
return H2StaticTable.Via;
case KnownHeaderType.Allow:
return H2StaticTable.Allow;
case KnownHeaderType.ContentType:
return H2StaticTable.ContentType;
case KnownHeaderType.ContentEncoding:
return H2StaticTable.ContentEncoding;
case KnownHeaderType.ContentLanguage:
return H2StaticTable.ContentLanguage;
case KnownHeaderType.ContentLocation:
return H2StaticTable.ContentLocation;
case KnownHeaderType.ContentRange:
return H2StaticTable.ContentRange;
case KnownHeaderType.Expires:
return H2StaticTable.Expires;
case KnownHeaderType.LastModified:
return H2StaticTable.LastModified;
case KnownHeaderType.AcceptRanges:
return H2StaticTable.AcceptRanges;
case KnownHeaderType.Age:
return H2StaticTable.Age;
case KnownHeaderType.ETag:
return H2StaticTable.ETag;
case KnownHeaderType.Location:
return H2StaticTable.Location;
case KnownHeaderType.ProxyAuthenticate:
return H2StaticTable.ProxyAuthenticate;
case KnownHeaderType.RetryAfter:
return H2StaticTable.RetryAfter;
case KnownHeaderType.Server:
return H2StaticTable.Server;
case KnownHeaderType.SetCookie:
return H2StaticTable.SetCookie;
case KnownHeaderType.Vary:
return H2StaticTable.Vary;
case KnownHeaderType.WWWAuthenticate:
return H2StaticTable.WwwAuthenticate;
case KnownHeaderType.AccessControlAllowOrigin:
return H2StaticTable.AccessControlAllowOrigin;
case KnownHeaderType.ContentLength:
return H2StaticTable.ContentLength;
default:
return -1;
}
}
} }
} }

View File

@ -38,6 +38,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
/// </remarks> /// </remarks>
public bool AddServerHeader { get; set; } = true; public bool AddServerHeader { get; set; } = true;
/// <summary>
/// Gets or sets a value that controls whether dynamic compression of response headers is allowed.
/// For more information about the security considerations of HPack dynamic header compression, visit
/// https://tools.ietf.org/html/rfc7541#section-7.
/// </summary>
/// <remarks>
/// Defaults to true.
/// </remarks>
public bool AllowResponseHeaderCompression { get; set; } = true;
/// <summary> /// <summary>
/// Gets or sets a value that controls whether synchronous IO is allowed for the <see cref="HttpContext.Request"/> and <see cref="HttpContext.Response"/> /// Gets or sets a value that controls whether synchronous IO is allowed for the <see cref="HttpContext.Request"/> and <see cref="HttpContext.Response"/>
/// </summary> /// </summary>

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>Core components of ASP.NET Core Kestrel cross-platform web server.</Description> <Description>Core components of ASP.NET Core Kestrel cross-platform web server.</Description>
@ -16,9 +16,10 @@
<Compile Include="$(SharedSourceRoot)CertificateGeneration\**\*.cs" /> <Compile Include="$(SharedSourceRoot)CertificateGeneration\**\*.cs" />
<Compile Include="$(SharedSourceRoot)ValueTaskExtensions\**\*.cs" /> <Compile Include="$(SharedSourceRoot)ValueTaskExtensions\**\*.cs" />
<Compile Include="$(SharedSourceRoot)UrlDecoder\**\*.cs" /> <Compile Include="$(SharedSourceRoot)UrlDecoder\**\*.cs" />
<Compile Include="$(SharedSourceRoot)runtime\*.cs" Link="Shared\runtime\%(Filename)%(Extension)"/> <Compile Include="$(SharedSourceRoot)runtime\*.cs" Link="Shared\runtime\%(Filename)%(Extension)" />
<Compile Include="$(SharedSourceRoot)runtime\Http2\**\*.cs" Link="Shared\runtime\Http2\%(Filename)%(Extension)"/> <Compile Include="$(SharedSourceRoot)runtime\Http2\**\*.cs" Link="Shared\runtime\Http2\%(Filename)%(Extension)" />
<Compile Include="$(SharedSourceRoot)runtime\Http3\**\*.cs" Link="Shared\runtime\Http3\%(Filename)%(Extension)"/> <Compile Include="$(SharedSourceRoot)runtime\Http3\**\*.cs" Link="Shared\runtime\Http3\%(Filename)%(Extension)" />
<Compile Include="$(SharedSourceRoot)Hpack\**\*.cs" Link="Shared\Hpack\%(Filename)%(Extension)" />
<Compile Include="$(SharedSourceRoot)ServerInfrastructure\**\*.cs" LinkBase="Shared\" /> <Compile Include="$(SharedSourceRoot)ServerInfrastructure\**\*.cs" LinkBase="Shared\" />
<Compile Include="$(RepoRoot)src\Shared\TaskToApm.cs" Link="Internal\TaskToApm.cs" /> <Compile Include="$(RepoRoot)src\Shared\TaskToApm.cs" Link="Internal\TaskToApm.cs" />
</ItemGroup> </ItemGroup>

View File

@ -1,199 +1,199 @@
// Copyright (c) .NET Foundation. All rights reserved. //// 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. //// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; //using System;
using System.Collections.Generic; //using System.Collections.Generic;
using System.Linq; //using System.Linq;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2; //using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
using Microsoft.Extensions.Primitives; //using Microsoft.Extensions.Primitives;
using Xunit; //using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests //namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{ //{
public class HPackHeaderWriterTests // public class HPackHeaderWriterTests
{ // {
public static TheoryData<KeyValuePair<string, string>[], byte[], int?> SinglePayloadData // public static TheoryData<KeyValuePair<string, string>[], byte[], int?> SinglePayloadData
{ // {
get // get
{ // {
var data = new TheoryData<KeyValuePair<string, string>[], byte[], int?>(); // var data = new TheoryData<KeyValuePair<string, string>[], byte[], int?>();
// Lowercase header name letters only // // Lowercase header name letters only
data.Add( // data.Add(
new[] // new[]
{ // {
new KeyValuePair<string, string>("CustomHeader", "CustomValue"), // new KeyValuePair<string, string>("CustomHeader", "CustomValue"),
}, // },
new byte[] // new byte[]
{ // {
// 0 12 c u s t o m // // 0 12 c u s t o m
0x00, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, // 0x00, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d,
// h e a d e r 11 C // // h e a d e r 11 C
0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x0b, 0x43, // 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x0b, 0x43,
// u s t o m V a l // // u s t o m V a l
0x75, 0x73, 0x74, 0x6f, 0x6d, 0x56, 0x61, 0x6c, // 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x56, 0x61, 0x6c,
// u e // // u e
0x75, 0x65 // 0x75, 0x65
}, // },
null); // null);
// Lowercase header name letters only // // Lowercase header name letters only
data.Add( // data.Add(
new[] // new[]
{ // {
new KeyValuePair<string, string>("CustomHeader!#$%&'*+-.^_`|~", "CustomValue"), // new KeyValuePair<string, string>("CustomHeader!#$%&'*+-.^_`|~", "CustomValue"),
}, // },
new byte[] // new byte[]
{ // {
// 0 27 c u s t o m // // 0 27 c u s t o m
0x00, 0x1b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, // 0x00, 0x1b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d,
// h e a d e r ! # // // h e a d e r ! #
0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x21, 0x23, // 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x21, 0x23,
// $ % & ' * + - . // // $ % & ' * + - .
0x24, 0x25, 0x26, 0x27, 0x2a, 0x2b, 0x2d, 0x2e, // 0x24, 0x25, 0x26, 0x27, 0x2a, 0x2b, 0x2d, 0x2e,
// ^ _ ` | ~ 11 C u // // ^ _ ` | ~ 11 C u
0x5e, 0x5f, 0x60, 0x7c, 0x7e, 0x0b, 0x43, 0x75, // 0x5e, 0x5f, 0x60, 0x7c, 0x7e, 0x0b, 0x43, 0x75,
// s t o m V a l u // // s t o m V a l u
0x73, 0x74, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75, // 0x73, 0x74, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75,
// e // // e
0x65 // 0x65
}, // },
null); // null);
// Single Payload // // Single Payload
data.Add( // data.Add(
new[] // new[]
{ // {
new KeyValuePair<string, string>("date", "Mon, 24 Jul 2017 19:22:30 GMT"), // new KeyValuePair<string, string>("date", "Mon, 24 Jul 2017 19:22:30 GMT"),
new KeyValuePair<string, string>("content-type", "text/html; charset=utf-8"), // new KeyValuePair<string, string>("content-type", "text/html; charset=utf-8"),
new KeyValuePair<string, string>("server", "Kestrel") // new KeyValuePair<string, string>("server", "Kestrel")
}, // },
new byte[] // new byte[]
{ // {
0x88, 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x1d, // 0x88, 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x1d,
0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x34, 0x20, // 0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x34, 0x20,
0x4a, 0x75, 0x6c, 0x20, 0x32, 0x30, 0x31, 0x37, // 0x4a, 0x75, 0x6c, 0x20, 0x32, 0x30, 0x31, 0x37,
0x20, 0x31, 0x39, 0x3a, 0x32, 0x32, 0x3a, 0x33, // 0x20, 0x31, 0x39, 0x3a, 0x32, 0x32, 0x3a, 0x33,
0x30, 0x20, 0x47, 0x4d, 0x54, 0x00, 0x0c, 0x63, // 0x30, 0x20, 0x47, 0x4d, 0x54, 0x00, 0x0c, 0x63,
0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74, // 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74,
0x79, 0x70, 0x65, 0x18, 0x74, 0x65, 0x78, 0x74, // 0x79, 0x70, 0x65, 0x18, 0x74, 0x65, 0x78, 0x74,
0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3b, 0x20, 0x63, // 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3b, 0x20, 0x63,
0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x75, // 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x75,
0x74, 0x66, 0x2d, 0x38, 0x00, 0x06, 0x73, 0x65, // 0x74, 0x66, 0x2d, 0x38, 0x00, 0x06, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x07, 0x4b, 0x65, 0x73, // 0x72, 0x76, 0x65, 0x72, 0x07, 0x4b, 0x65, 0x73,
0x74, 0x72, 0x65, 0x6c // 0x74, 0x72, 0x65, 0x6c
}, // },
200); // 200);
return data; // return data;
} // }
} // }
[Theory] // [Theory]
[MemberData(nameof(SinglePayloadData))] // [MemberData(nameof(SinglePayloadData))]
public void EncodesHeadersInSinglePayloadWhenSpaceAvailable(KeyValuePair<string, string>[] headers, byte[] expectedPayload, int? statusCode) // public void EncodesHeadersInSinglePayloadWhenSpaceAvailable(KeyValuePair<string, string>[] headers, byte[] expectedPayload, int? statusCode)
{ // {
var payload = new byte[1024]; // var payload = new byte[1024];
var length = 0; // var length = 0;
if (statusCode.HasValue) // if (statusCode.HasValue)
{ // {
Assert.True(HPackHeaderWriter.BeginEncodeHeaders(statusCode.Value, GetHeadersEnumerator(headers), payload, out length)); // Assert.True(HPackHeaderWriter.BeginEncodeHeaders(statusCode.Value, GetHeadersEnumerator(headers), payload, out length));
} // }
else // else
{ // {
Assert.True(HPackHeaderWriter.BeginEncodeHeaders(GetHeadersEnumerator(headers), payload, out length)); // Assert.True(HPackHeaderWriter.BeginEncodeHeaders(GetHeadersEnumerator(headers), payload, out length));
} // }
Assert.Equal(expectedPayload.Length, length); // Assert.Equal(expectedPayload.Length, length);
for (var i = 0; i < length; i++) // for (var i = 0; i < length; i++)
{ // {
Assert.True(expectedPayload[i] == payload[i], $"{expectedPayload[i]} != {payload[i]} at {i} (len {length})"); // Assert.True(expectedPayload[i] == payload[i], $"{expectedPayload[i]} != {payload[i]} at {i} (len {length})");
} // }
Assert.Equal(expectedPayload, new ArraySegment<byte>(payload, 0, length)); // Assert.Equal(expectedPayload, new ArraySegment<byte>(payload, 0, length));
} // }
[Theory] // [Theory]
[InlineData(true)] // [InlineData(true)]
[InlineData(false)] // [InlineData(false)]
public void EncodesHeadersInMultiplePayloadsWhenSpaceNotAvailable(bool exactSize) // public void EncodesHeadersInMultiplePayloadsWhenSpaceNotAvailable(bool exactSize)
{ // {
var statusCode = 200; // var statusCode = 200;
var headers = new[] // var headers = new[]
{ // {
new KeyValuePair<string, string>("date", "Mon, 24 Jul 2017 19:22:30 GMT"), // new KeyValuePair<string, string>("date", "Mon, 24 Jul 2017 19:22:30 GMT"),
new KeyValuePair<string, string>("content-type", "text/html; charset=utf-8"), // new KeyValuePair<string, string>("content-type", "text/html; charset=utf-8"),
new KeyValuePair<string, string>("server", "Kestrel") // new KeyValuePair<string, string>("server", "Kestrel")
}; // };
var expectedStatusCodePayload = new byte[] // var expectedStatusCodePayload = new byte[]
{ // {
0x88 // 0x88
}; // };
var expectedDateHeaderPayload = new byte[] // var expectedDateHeaderPayload = new byte[]
{ // {
0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x1d, 0x4d, // 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x1d, 0x4d,
0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x34, 0x20, 0x4a, // 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x34, 0x20, 0x4a,
0x75, 0x6c, 0x20, 0x32, 0x30, 0x31, 0x37, 0x20, // 0x75, 0x6c, 0x20, 0x32, 0x30, 0x31, 0x37, 0x20,
0x31, 0x39, 0x3a, 0x32, 0x32, 0x3a, 0x33, 0x30, // 0x31, 0x39, 0x3a, 0x32, 0x32, 0x3a, 0x33, 0x30,
0x20, 0x47, 0x4d, 0x54 // 0x20, 0x47, 0x4d, 0x54
}; // };
var expectedContentTypeHeaderPayload = new byte[] // var expectedContentTypeHeaderPayload = new byte[]
{ // {
0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x18, 0x74, // 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x18, 0x74,
0x65, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, // 0x65, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c,
0x3b, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // 0x3b, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,
0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38 // 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38
}; // };
var expectedServerHeaderPayload = new byte[] // var expectedServerHeaderPayload = new byte[]
{ // {
0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, // 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x07, 0x4b, 0x65, 0x73, 0x74, 0x72, 0x65, 0x6c // 0x07, 0x4b, 0x65, 0x73, 0x74, 0x72, 0x65, 0x6c
}; // };
Span<byte> payload = new byte[1024]; // Span<byte> payload = new byte[1024];
var offset = 0; // var offset = 0;
var headerEnumerator = GetHeadersEnumerator(headers); // var headerEnumerator = GetHeadersEnumerator(headers);
// When !exactSize, slices are one byte short of fitting the next header // // When !exactSize, slices are one byte short of fitting the next header
var sliceLength = expectedStatusCodePayload.Length + (exactSize ? 0 : expectedDateHeaderPayload.Length - 1); // var sliceLength = expectedStatusCodePayload.Length + (exactSize ? 0 : expectedDateHeaderPayload.Length - 1);
Assert.False(HPackHeaderWriter.BeginEncodeHeaders(statusCode, headerEnumerator, payload.Slice(offset, sliceLength), out var length)); // Assert.False(HPackHeaderWriter.BeginEncodeHeaders(statusCode, headerEnumerator, payload.Slice(offset, sliceLength), out var length));
Assert.Equal(expectedStatusCodePayload.Length, length); // Assert.Equal(expectedStatusCodePayload.Length, length);
Assert.Equal(expectedStatusCodePayload, payload.Slice(0, length).ToArray()); // Assert.Equal(expectedStatusCodePayload, payload.Slice(0, length).ToArray());
offset += length; // offset += length;
sliceLength = expectedDateHeaderPayload.Length + (exactSize ? 0 : expectedContentTypeHeaderPayload.Length - 1); // sliceLength = expectedDateHeaderPayload.Length + (exactSize ? 0 : expectedContentTypeHeaderPayload.Length - 1);
Assert.False(HPackHeaderWriter.ContinueEncodeHeaders(headerEnumerator, payload.Slice(offset, sliceLength), out length)); // Assert.False(HPackHeaderWriter.ContinueEncodeHeaders(headerEnumerator, payload.Slice(offset, sliceLength), out length));
Assert.Equal(expectedDateHeaderPayload.Length, length); // Assert.Equal(expectedDateHeaderPayload.Length, length);
Assert.Equal(expectedDateHeaderPayload, payload.Slice(offset, length).ToArray()); // Assert.Equal(expectedDateHeaderPayload, payload.Slice(offset, length).ToArray());
offset += length; // offset += length;
sliceLength = expectedContentTypeHeaderPayload.Length + (exactSize ? 0 : expectedServerHeaderPayload.Length - 1); // sliceLength = expectedContentTypeHeaderPayload.Length + (exactSize ? 0 : expectedServerHeaderPayload.Length - 1);
Assert.False(HPackHeaderWriter.ContinueEncodeHeaders(headerEnumerator, payload.Slice(offset, sliceLength), out length)); // Assert.False(HPackHeaderWriter.ContinueEncodeHeaders(headerEnumerator, payload.Slice(offset, sliceLength), out length));
Assert.Equal(expectedContentTypeHeaderPayload.Length, length); // Assert.Equal(expectedContentTypeHeaderPayload.Length, length);
Assert.Equal(expectedContentTypeHeaderPayload, payload.Slice(offset, length).ToArray()); // Assert.Equal(expectedContentTypeHeaderPayload, payload.Slice(offset, length).ToArray());
offset += length; // offset += length;
sliceLength = expectedServerHeaderPayload.Length; // sliceLength = expectedServerHeaderPayload.Length;
Assert.True(HPackHeaderWriter.ContinueEncodeHeaders(headerEnumerator, payload.Slice(offset, sliceLength), out length)); // Assert.True(HPackHeaderWriter.ContinueEncodeHeaders(headerEnumerator, payload.Slice(offset, sliceLength), out length));
Assert.Equal(expectedServerHeaderPayload.Length, length); // Assert.Equal(expectedServerHeaderPayload.Length, length);
Assert.Equal(expectedServerHeaderPayload, payload.Slice(offset, length).ToArray()); // Assert.Equal(expectedServerHeaderPayload, payload.Slice(offset, length).ToArray());
} // }
private static Http2HeadersEnumerator GetHeadersEnumerator(IEnumerable<KeyValuePair<string, string>> headers) // private static Http2HeadersEnumerator GetHeadersEnumerator(IEnumerable<KeyValuePair<string, string>> headers)
{ // {
var groupedHeaders = headers // var groupedHeaders = headers
.GroupBy(k => k.Key) // .GroupBy(k => k.Key)
.ToDictionary(g => g.Key, g => new StringValues(g.Select(gg => gg.Value).ToArray())); // .ToDictionary(g => g.Key, g => new StringValues(g.Select(gg => gg.Value).ToArray()));
var enumerator = new Http2HeadersEnumerator(); // var enumerator = new Http2HeadersEnumerator();
enumerator.Initialize(groupedHeaders); // enumerator.Initialize(groupedHeaders);
return enumerator; // return enumerator;
} // }
} // }
} //}

View File

@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{ {
// Arrange // Arrange
var pipe = new Pipe(new PipeOptions(_dirtyMemoryPool, PipeScheduler.Inline, PipeScheduler.Inline)); var pipe = new Pipe(new PipeOptions(_dirtyMemoryPool, PipeScheduler.Inline, PipeScheduler.Inline));
var frameWriter = new Http2FrameWriter(pipe.Writer, null, null, null, null, null, null, _dirtyMemoryPool, new Mock<IKestrelTrace>().Object); var frameWriter = CreateFrameWriter(pipe);
// Act // Act
await frameWriter.WriteWindowUpdateAsync(1, 1); await frameWriter.WriteWindowUpdateAsync(1, 1);
@ -52,12 +52,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Assert.Equal(new byte[] { 0x00, 0x00, 0x00, 0x01 }, payload.Skip(Http2FrameReader.HeaderLength).Take(4).ToArray()); Assert.Equal(new byte[] { 0x00, 0x00, 0x00, 0x01 }, payload.Skip(Http2FrameReader.HeaderLength).Take(4).ToArray());
} }
private Http2FrameWriter CreateFrameWriter(Pipe pipe)
{
var serviceContext = new Internal.ServiceContext
{
ServerOptions = new KestrelServerOptions(),
Log = new Mock<IKestrelTrace>().Object
};
return new Http2FrameWriter(pipe.Writer, null, null, null, null, null, null, _dirtyMemoryPool, serviceContext);
}
[Fact] [Fact]
public async Task WriteGoAway_UnsetsReservedBit() public async Task WriteGoAway_UnsetsReservedBit()
{ {
// Arrange // Arrange
var pipe = new Pipe(new PipeOptions(_dirtyMemoryPool, PipeScheduler.Inline, PipeScheduler.Inline)); var pipe = new Pipe(new PipeOptions(_dirtyMemoryPool, PipeScheduler.Inline, PipeScheduler.Inline));
var frameWriter = new Http2FrameWriter(pipe.Writer, null, null, null, null, null, null, _dirtyMemoryPool, new Mock<IKestrelTrace>().Object); var frameWriter = CreateFrameWriter(pipe);
// Act // Act
await frameWriter.WriteGoAwayAsync(1, Http2ErrorCode.NO_ERROR); await frameWriter.WriteGoAwayAsync(1, Http2ErrorCode.NO_ERROR);

View File

@ -0,0 +1,479 @@
// 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.Collections.Generic;
using System.Linq;
using System.Net.Http.HPack;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
using Microsoft.Extensions.Primitives;
using Xunit;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{
public class Http2HPackEncoderTests
{
[Fact]
public void BeginEncodeHeaders_Status302_NewIndexValue()
{
Span<byte> buffer = new byte[1024 * 16];
var headers = new HttpResponseHeaders();
var enumerator = new Http2HeadersEnumerator();
enumerator.Initialize(headers);
var hpackEncoder = new HPackEncoder();
Assert.True(HPackHeaderWriter.BeginEncodeHeaders(302, hpackEncoder, enumerator, buffer, out var length));
var result = buffer.Slice(0, length).ToArray();
var hex = BitConverter.ToString(result);
Assert.Equal("48-03-33-30-32", hex);
var statusHeader = GetHeaderEntry(hpackEncoder, 0);
Assert.Equal(":status", statusHeader.Name);
Assert.Equal("302", statusHeader.Value);
}
[Fact]
public void BeginEncodeHeaders_CacheControlPrivate_NewIndexValue()
{
Span<byte> buffer = new byte[1024 * 16];
var headers = new HttpResponseHeaders();
headers.HeaderCacheControl = "private";
var enumerator = new Http2HeadersEnumerator();
enumerator.Initialize(headers);
var hpackEncoder = new HPackEncoder();
Assert.True(HPackHeaderWriter.BeginEncodeHeaders(302, hpackEncoder, enumerator, buffer, out var length));
var result = buffer.Slice(5, length - 5).ToArray();
var hex = BitConverter.ToString(result);
Assert.Equal("58-07-70-72-69-76-61-74-65", hex);
var statusHeader = GetHeaderEntry(hpackEncoder, 0);
Assert.Equal("Cache-Control", statusHeader.Name);
Assert.Equal("private", statusHeader.Value);
}
[Fact]
public void BeginEncodeHeaders_MaxHeaderTableSizeExceeded_EvictionsToFit()
{
// Test follows example https://tools.ietf.org/html/rfc7541#appendix-C.5
Span<byte> buffer = new byte[1024 * 16];
var headers = new HttpResponseHeaders();
headers.HeaderCacheControl = "private";
headers.HeaderDate = "Mon, 21 Oct 2013 20:13:21 GMT";
headers.HeaderLocation = "https://www.example.com";
var enumerator = new Http2HeadersEnumerator();
var hpackEncoder = new HPackEncoder(maxHeaderTableSize: 256);
// First response
enumerator.Initialize(headers);
Assert.True(HPackHeaderWriter.BeginEncodeHeaders(302, hpackEncoder, enumerator, buffer, out var length));
var result = buffer.Slice(0, length).ToArray();
var hex = BitConverter.ToString(result);
Assert.Equal(
"48-03-33-30-32-58-07-70-72-69-76-61-74-65-61-1D-" +
"4D-6F-6E-2C-20-32-31-20-4F-63-74-20-32-30-31-33-" +
"20-32-30-3A-31-33-3A-32-31-20-47-4D-54-6E-17-68-" +
"74-74-70-73-3A-2F-2F-77-77-77-2E-65-78-61-6D-70-" +
"6C-65-2E-63-6F-6D", hex);
var entries = GetHeaderEntries(hpackEncoder);
Assert.Collection(entries,
e =>
{
Assert.Equal("Location", e.Name);
Assert.Equal("https://www.example.com", e.Value);
},
e =>
{
Assert.Equal("Date", e.Name);
Assert.Equal("Mon, 21 Oct 2013 20:13:21 GMT", e.Value);
},
e =>
{
Assert.Equal("Cache-Control", e.Name);
Assert.Equal("private", e.Value);
},
e =>
{
Assert.Equal(":status", e.Name);
Assert.Equal("302", e.Value);
});
// Second response
enumerator.Initialize(headers);
Assert.True(HPackHeaderWriter.BeginEncodeHeaders(307, hpackEncoder, enumerator, buffer, out length));
result = buffer.Slice(0, length).ToArray();
hex = BitConverter.ToString(result);
Assert.Equal("48-03-33-30-37-C1-C0-BF", hex);
entries = GetHeaderEntries(hpackEncoder);
Assert.Collection(entries,
e =>
{
Assert.Equal(":status", e.Name);
Assert.Equal("307", e.Value);
},
e =>
{
Assert.Equal("Location", e.Name);
Assert.Equal("https://www.example.com", e.Value);
},
e =>
{
Assert.Equal("Date", e.Name);
Assert.Equal("Mon, 21 Oct 2013 20:13:21 GMT", e.Value);
},
e =>
{
Assert.Equal("Cache-Control", e.Name);
Assert.Equal("private", e.Value);
});
// Third response
headers.HeaderDate = "Mon, 21 Oct 2013 20:13:22 GMT";
headers.HeaderContentEncoding = "gzip";
headers.HeaderSetCookie = "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1";
enumerator.Initialize(headers);
Assert.True(HPackHeaderWriter.BeginEncodeHeaders(200, hpackEncoder, enumerator, buffer, out length));
result = buffer.Slice(0, length).ToArray();
hex = BitConverter.ToString(result);
Assert.Equal(
"88-C1-61-1D-4D-6F-6E-2C-20-32-31-20-4F-63-74-20-" +
"32-30-31-33-20-32-30-3A-31-33-3A-32-32-20-47-4D-" +
"54-5A-04-67-7A-69-70-C1-1F-28-38-66-6F-6F-3D-41-" +
"53-44-4A-4B-48-51-4B-42-5A-58-4F-51-57-45-4F-50-" +
"49-55-41-58-51-57-45-4F-49-55-3B-20-6D-61-78-2D-" +
"61-67-65-3D-33-36-30-30-3B-20-76-65-72-73-69-6F-" +
"6E-3D-31", hex);
entries = GetHeaderEntries(hpackEncoder);
Assert.Collection(entries,
e =>
{
Assert.Equal("Content-Encoding", e.Name);
Assert.Equal("gzip", e.Value);
},
e =>
{
Assert.Equal("Date", e.Name);
Assert.Equal("Mon, 21 Oct 2013 20:13:22 GMT", e.Value);
},
e =>
{
Assert.Equal(":status", e.Name);
Assert.Equal("307", e.Value);
},
e =>
{
Assert.Equal("Location", e.Name);
Assert.Equal("https://www.example.com", e.Value);
});
}
[Theory]
[InlineData("Set-Cookie", true)]
[InlineData("Content-Disposition", true)]
[InlineData("Content-Length", false)]
public void BeginEncodeHeaders_ExcludedHeaders_NotAddedToTable(string headerName, bool neverIndex)
{
Span<byte> buffer = new byte[1024 * 16];
var headers = new HttpResponseHeaders();
headers.Append(headerName, "1");
var enumerator = new Http2HeadersEnumerator();
enumerator.Initialize(headers);
var hpackEncoder = new HPackEncoder(maxHeaderTableSize: Http2PeerSettings.DefaultHeaderTableSize);
Assert.True(HPackHeaderWriter.BeginEncodeHeaders(hpackEncoder, enumerator, buffer, out _));
if (neverIndex)
{
Assert.Equal(0x10, buffer[0] & 0x10);
}
else
{
Assert.Equal(0, buffer[0] & 0x40);
}
Assert.Empty(GetHeaderEntries(hpackEncoder));
}
[Fact]
public void BeginEncodeHeaders_HeaderExceedHeaderTableSize_NoIndexAndNoHeaderEntry()
{
Span<byte> buffer = new byte[1024 * 16];
var headers = new HttpResponseHeaders();
headers.Append("x-Custom", new string('!', (int)Http2PeerSettings.DefaultHeaderTableSize));
var enumerator = new Http2HeadersEnumerator();
enumerator.Initialize(headers);
var hpackEncoder = new HPackEncoder();
Assert.True(HPackHeaderWriter.BeginEncodeHeaders(200, hpackEncoder, enumerator, buffer, out var length));
Assert.Empty(GetHeaderEntries(hpackEncoder));
}
public static TheoryData<KeyValuePair<string, string>[], byte[], int?> SinglePayloadData
{
get
{
var data = new TheoryData<KeyValuePair<string, string>[], byte[], int?>();
// Lowercase header name letters only
data.Add(
new[]
{
new KeyValuePair<string, string>("CustomHeader", "CustomValue"),
},
new byte[]
{
// 12 c u s t o m
0x40, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d,
// h e a d e r 11 C
0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x0b, 0x43,
// u s t o m V a l
0x75, 0x73, 0x74, 0x6f, 0x6d, 0x56, 0x61, 0x6c,
// u e
0x75, 0x65
},
null);
// Lowercase header name letters only
data.Add(
new[]
{
new KeyValuePair<string, string>("CustomHeader!#$%&'*+-.^_`|~", "CustomValue"),
},
new byte[]
{
// 27 c u s t o m
0x40, 0x1b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d,
// h e a d e r ! #
0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x21, 0x23,
// $ % & ' * + - .
0x24, 0x25, 0x26, 0x27, 0x2a, 0x2b, 0x2d, 0x2e,
// ^ _ ` | ~ 11 C u
0x5e, 0x5f, 0x60, 0x7c, 0x7e, 0x0b, 0x43, 0x75,
// s t o m V a l u
0x73, 0x74, 0x6f, 0x6d, 0x56, 0x61, 0x6c, 0x75,
// e
0x65
},
null);
// Single Payload
data.Add(
new[]
{
new KeyValuePair<string, string>("date", "Mon, 24 Jul 2017 19:22:30 GMT"),
new KeyValuePair<string, string>("content-type", "text/html; charset=utf-8"),
new KeyValuePair<string, string>("server", "Kestrel")
},
new byte[]
{
0x88, 0x40, 0x04, 0x64, 0x61, 0x74, 0x65, 0x1d,
0x4d, 0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x34, 0x20,
0x4a, 0x75, 0x6c, 0x20, 0x32, 0x30, 0x31, 0x37,
0x20, 0x31, 0x39, 0x3a, 0x32, 0x32, 0x3a, 0x33,
0x30, 0x20, 0x47, 0x4d, 0x54, 0x40, 0x0c, 0x63,
0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74,
0x79, 0x70, 0x65, 0x18, 0x74, 0x65, 0x78, 0x74,
0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3b, 0x20, 0x63,
0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x75,
0x74, 0x66, 0x2d, 0x38, 0x40, 0x06, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x07, 0x4b, 0x65, 0x73,
0x74, 0x72, 0x65, 0x6c
},
200);
return data;
}
}
[Theory]
[MemberData(nameof(SinglePayloadData))]
public void EncodesHeadersInSinglePayloadWhenSpaceAvailable(KeyValuePair<string, string>[] headers, byte[] expectedPayload, int? statusCode)
{
HPackEncoder hpackEncoder = new HPackEncoder();
var payload = new byte[1024];
var length = 0;
if (statusCode.HasValue)
{
Assert.True(HPackHeaderWriter.BeginEncodeHeaders(statusCode.Value, hpackEncoder, GetHeadersEnumerator(headers), payload, out length));
}
else
{
Assert.True(HPackHeaderWriter.BeginEncodeHeaders(hpackEncoder, GetHeadersEnumerator(headers), payload, out length));
}
Assert.Equal(expectedPayload.Length, length);
for (var i = 0; i < length; i++)
{
Assert.True(expectedPayload[i] == payload[i], $"{expectedPayload[i]} != {payload[i]} at {i} (len {length})");
}
Assert.Equal(expectedPayload, new ArraySegment<byte>(payload, 0, length));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void EncodesHeadersInMultiplePayloadsWhenSpaceNotAvailable(bool exactSize)
{
var statusCode = 200;
var headers = new[]
{
new KeyValuePair<string, string>("date", "Mon, 24 Jul 2017 19:22:30 GMT"),
new KeyValuePair<string, string>("content-type", "text/html; charset=utf-8"),
new KeyValuePair<string, string>("server", "Kestrel")
};
var expectedStatusCodePayload = new byte[]
{
0x88
};
var expectedDateHeaderPayload = new byte[]
{
0x40, 0x04, 0x64, 0x61, 0x74, 0x65, 0x1d, 0x4d,
0x6f, 0x6e, 0x2c, 0x20, 0x32, 0x34, 0x20, 0x4a,
0x75, 0x6c, 0x20, 0x32, 0x30, 0x31, 0x37, 0x20,
0x31, 0x39, 0x3a, 0x32, 0x32, 0x3a, 0x33, 0x30,
0x20, 0x47, 0x4d, 0x54
};
var expectedContentTypeHeaderPayload = new byte[]
{
0x40, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x18, 0x74,
0x65, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c,
0x3b, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65,
0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38
};
var expectedServerHeaderPayload = new byte[]
{
0x40, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x07, 0x4b, 0x65, 0x73, 0x74, 0x72, 0x65, 0x6c
};
var hpackEncoder = new HPackEncoder();
Span<byte> payload = new byte[1024];
var offset = 0;
var headerEnumerator = GetHeadersEnumerator(headers);
// When !exactSize, slices are one byte short of fitting the next header
var sliceLength = expectedStatusCodePayload.Length + (exactSize ? 0 : expectedDateHeaderPayload.Length - 1);
Assert.False(HPackHeaderWriter.BeginEncodeHeaders(statusCode, hpackEncoder, headerEnumerator, payload.Slice(offset, sliceLength), out var length));
Assert.Equal(expectedStatusCodePayload.Length, length);
Assert.Equal(expectedStatusCodePayload, payload.Slice(0, length).ToArray());
offset += length;
sliceLength = expectedDateHeaderPayload.Length + (exactSize ? 0 : expectedContentTypeHeaderPayload.Length - 1);
Assert.False(HPackHeaderWriter.ContinueEncodeHeaders(hpackEncoder, headerEnumerator, payload.Slice(offset, sliceLength), out length));
Assert.Equal(expectedDateHeaderPayload.Length, length);
Assert.Equal(expectedDateHeaderPayload, payload.Slice(offset, length).ToArray());
offset += length;
sliceLength = expectedContentTypeHeaderPayload.Length + (exactSize ? 0 : expectedServerHeaderPayload.Length - 1);
Assert.False(HPackHeaderWriter.ContinueEncodeHeaders(hpackEncoder, headerEnumerator, payload.Slice(offset, sliceLength), out length));
Assert.Equal(expectedContentTypeHeaderPayload.Length, length);
Assert.Equal(expectedContentTypeHeaderPayload, payload.Slice(offset, length).ToArray());
offset += length;
sliceLength = expectedServerHeaderPayload.Length;
Assert.True(HPackHeaderWriter.ContinueEncodeHeaders(hpackEncoder, headerEnumerator, payload.Slice(offset, sliceLength), out length));
Assert.Equal(expectedServerHeaderPayload.Length, length);
Assert.Equal(expectedServerHeaderPayload, payload.Slice(offset, length).ToArray());
}
[Fact]
public void BeginEncodeHeaders_MaxHeaderTableSizeUpdated_SizeUpdateInHeaders()
{
Span<byte> buffer = new byte[1024 * 16];
var hpackEncoder = new HPackEncoder();
hpackEncoder.UpdateMaxHeaderTableSize(100);
var enumerator = new Http2HeadersEnumerator();
// First request
enumerator.Initialize(new Dictionary<string, StringValues>());
Assert.True(HPackHeaderWriter.BeginEncodeHeaders(hpackEncoder, enumerator, buffer, out var length));
Assert.Equal(2, length);
const byte DynamicTableSizeUpdateMask = 0xe0;
var integerDecoder = new IntegerDecoder();
Assert.False(integerDecoder.BeginTryDecode((byte)(buffer[0] & ~DynamicTableSizeUpdateMask), prefixLength: 5, out _));
Assert.True(integerDecoder.TryDecode(buffer[1], out var result));
Assert.Equal(100, result);
// Second request
enumerator.Initialize(new Dictionary<string, StringValues>());
Assert.True(HPackHeaderWriter.BeginEncodeHeaders(hpackEncoder, enumerator, buffer, out length));
Assert.Equal(0, length);
}
private static Http2HeadersEnumerator GetHeadersEnumerator(IEnumerable<KeyValuePair<string, string>> headers)
{
var groupedHeaders = headers
.GroupBy(k => k.Key)
.ToDictionary(g => g.Key, g => new StringValues(g.Select(gg => gg.Value).ToArray()));
var enumerator = new Http2HeadersEnumerator();
enumerator.Initialize(groupedHeaders);
return enumerator;
}
private EncoderHeaderEntry GetHeaderEntry(HPackEncoder encoder, int index)
{
var entry = encoder.Head;
while (index-- >= 0)
{
entry = entry.Before;
}
return entry;
}
private List<EncoderHeaderEntry> GetHeaderEntries(HPackEncoder encoder)
{
var headers = new List<EncoderHeaderEntry>();
var entry = encoder.Head;
while (entry.Before != encoder.Head)
{
entry = entry.Before;
headers.Add(entry);
};
return headers;
}
}
}

View File

@ -333,11 +333,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
[Theory] [Theory]
[InlineData(int.MinValue)] [InlineData(int.MinValue)]
[InlineData(-1)] [InlineData(-1)]
[InlineData(0)]
public void Http2HeaderTableSizeInvalid(int value) public void Http2HeaderTableSizeInvalid(int value)
{ {
var ex = Assert.Throws<ArgumentOutOfRangeException>(() => new KestrelServerLimits().Http2.HeaderTableSize = value); var ex = Assert.Throws<ArgumentOutOfRangeException>(() => new KestrelServerLimits().Http2.HeaderTableSize = value);
Assert.StartsWith(CoreStrings.GreaterThanZeroRequired, ex.Message); Assert.StartsWith(CoreStrings.GreaterThanOrEqualToZeroRequired, ex.Message);
} }
[Fact] [Fact]

View File

@ -8,6 +8,7 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Pipelines; using System.IO.Pipelines;
using System.Linq; using System.Linq;
using System.Net.Http.HPack;
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -24,28 +25,25 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Server.Kestrel.Performance namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{ {
public class Http2ConnectionBenchmark public abstract class Http2ConnectionBenchmarkBase
{ {
private MemoryPool<byte> _memoryPool; private MemoryPool<byte> _memoryPool;
private HttpRequestHeaders _httpRequestHeaders; private HttpRequestHeaders _httpRequestHeaders;
private Http2Connection _connection; private Http2Connection _connection;
private HPackEncoder _hpackEncoder;
private Http2HeadersEnumerator _requestHeadersEnumerator; private Http2HeadersEnumerator _requestHeadersEnumerator;
private int _currentStreamId; private int _currentStreamId;
private byte[] _headersBuffer; private byte[] _headersBuffer;
private DuplexPipe.DuplexPipePair _connectionPair; private DuplexPipe.DuplexPipePair _connectionPair;
private Http2Frame _httpFrame; private Http2Frame _httpFrame;
private string _responseData;
private int _dataWritten; private int _dataWritten;
[Params(0, 10, 1024 * 1024)] protected abstract Task ProcessRequest(HttpContext httpContext);
public int ResponseDataLength { get; set; }
[GlobalSetup] public virtual void GlobalSetup()
public void GlobalSetup()
{ {
_memoryPool = SlabMemoryPoolFactory.Create(); _memoryPool = SlabMemoryPoolFactory.Create();
_httpFrame = new Http2Frame(); _httpFrame = new Http2Frame();
_responseData = new string('!', ResponseDataLength);
var options = new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false); var options = new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false);
@ -58,6 +56,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
_httpRequestHeaders.Append(HeaderNames.Authority, new StringValues("localhost:80")); _httpRequestHeaders.Append(HeaderNames.Authority, new StringValues("localhost:80"));
_headersBuffer = new byte[1024 * 16]; _headersBuffer = new byte[1024 * 16];
_hpackEncoder = new HPackEncoder();
var serviceContext = new ServiceContext var serviceContext = new ServiceContext
{ {
@ -83,7 +82,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
_currentStreamId = 1; _currentStreamId = 1;
_ = _connection.ProcessRequestsAsync(new DummyApplication(c => ResponseDataLength == 0 ? Task.CompletedTask : c.Response.WriteAsync(_responseData), new MockHttpContextFactory())); _ = _connection.ProcessRequestsAsync(new DummyApplication(ProcessRequest, new MockHttpContextFactory()));
_connectionPair.Application.Output.Write(Http2Connection.ClientPreface); _connectionPair.Application.Output.Write(Http2Connection.ClientPreface);
_connectionPair.Application.Output.WriteSettings(new Http2PeerSettings _connectionPair.Application.Output.WriteSettings(new Http2PeerSettings
@ -102,11 +101,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
} }
[Benchmark] [Benchmark]
public async Task EmptyRequest() public async Task MakeRequest()
{ {
_requestHeadersEnumerator.Initialize(_httpRequestHeaders); _requestHeadersEnumerator.Initialize(_httpRequestHeaders);
_requestHeadersEnumerator.MoveNext(); _requestHeadersEnumerator.MoveNext();
_connectionPair.Application.Output.WriteStartStream(streamId: _currentStreamId, _requestHeadersEnumerator, _headersBuffer, endStream: true, frame: _httpFrame); _connectionPair.Application.Output.WriteStartStream(streamId: _currentStreamId, _hpackEncoder, _requestHeadersEnumerator, _headersBuffer, endStream: true, frame: _httpFrame);
await _connectionPair.Application.Output.FlushAsync(); await _connectionPair.Application.Output.FlushAsync();
while (true) while (true)

View File

@ -0,0 +1,29 @@
// 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.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{
public class Http2ConnectionBenchmark : Http2ConnectionBenchmarkBase
{
[Params(0, 128, 1024)]
public int ResponseDataLength { get; set; }
private string _responseData;
[GlobalSetup]
public override void GlobalSetup()
{
base.GlobalSetup();
_responseData = new string('!', ResponseDataLength);
}
protected override Task ProcessRequest(HttpContext httpContext)
{
return ResponseDataLength == 0 ? Task.CompletedTask : httpContext.Response.WriteAsync(_responseData);
}
}
}

View File

@ -0,0 +1,48 @@
// 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.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{
public class Http2ConnectionHeadersBenchmark : Http2ConnectionBenchmarkBase
{
[Params(1, 4, 32)]
public int HeadersCount { get; set; }
[Params(true, false)]
public bool HeadersChange { get; set; }
private int _headerIndex;
private string[] _headerNames;
[GlobalSetup]
public override void GlobalSetup()
{
base.GlobalSetup();
_headerNames = new string[HeadersCount * (HeadersChange ? 1000 : 1)];
for (var i = 0; i < _headerNames.Length; i++)
{
_headerNames[i] = "CustomHeader" + i;
}
}
protected override Task ProcessRequest(HttpContext httpContext)
{
for (var i = 0; i < HeadersCount; i++)
{
var headerName = _headerNames[_headerIndex % HeadersCount];
httpContext.Response.Headers[headerName] = "The quick brown fox jumps over the lazy dog.";
if (HeadersChange)
{
_headerIndex++;
}
}
return Task.CompletedTask;
}
}
}

View File

@ -36,7 +36,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
minResponseDataRate: null, minResponseDataRate: null,
"TestConnectionId", "TestConnectionId",
_memoryPool, _memoryPool,
new KestrelTrace(NullLogger.Instance)); new Core.Internal.ServiceContext());
_responseHeaders = new HttpResponseHeaders(); _responseHeaders = new HttpResponseHeaders();
_responseHeaders.HeaderContentType = "application/json"; _responseHeaders.HeaderContentType = "application/json";

View File

@ -25,13 +25,13 @@ namespace Microsoft.AspNetCore.Testing
writer.Write(payload); writer.Write(payload);
} }
public static void WriteStartStream(this PipeWriter writer, int streamId, Http2HeadersEnumerator headers, byte[] headerEncodingBuffer, bool endStream, Http2Frame frame = null) public static void WriteStartStream(this PipeWriter writer, int streamId, HPackEncoder hpackEncoder, Http2HeadersEnumerator headers, byte[] headerEncodingBuffer, bool endStream, Http2Frame frame = null)
{ {
frame ??= new Http2Frame(); frame ??= new Http2Frame();
frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId); frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId);
var buffer = headerEncodingBuffer.AsSpan(); var buffer = headerEncodingBuffer.AsSpan();
var done = HPackHeaderWriter.BeginEncodeHeaders(headers, buffer, out var length); var done = HPackHeaderWriter.BeginEncodeHeaders(hpackEncoder, headers, buffer, out var length);
frame.PayloadLength = length; frame.PayloadLength = length;
if (done) if (done)
@ -51,7 +51,7 @@ namespace Microsoft.AspNetCore.Testing
{ {
frame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId); frame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId);
done = HPackHeaderWriter.ContinueEncodeHeaders(headers, buffer, out length); done = HPackHeaderWriter.ContinueEncodeHeaders(hpackEncoder, headers, buffer, out length);
frame.PayloadLength = length; frame.PayloadLength = length;
if (done) if (done)

View File

@ -14,10 +14,12 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Moq; using Moq;
using Xunit; using Xunit;
@ -58,7 +60,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendWindowUpdateAsync(streamId: 1, 65535); await SendWindowUpdateAsync(streamId: 1, 65535);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -90,7 +92,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(3, GetHeaders(responseBodySize: 3), endStream: true); await StartStreamAsync(3, GetHeaders(responseBodySize: 3), endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 2,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 3); withStreamId: 3);
@ -101,7 +103,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(5, GetHeaders(responseBodySize: 3), endStream: true); await StartStreamAsync(5, GetHeaders(responseBodySize: 3), endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 2,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 5); withStreamId: 5);
@ -197,7 +199,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -274,7 +276,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, requestHeaders, endStream: true); await StartStreamAsync(1, requestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -293,7 +295,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(3, requestHeaders, endStream: true); await StartStreamAsync(3, requestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 6,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 3); withStreamId: 3);
@ -323,7 +325,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
serverTcs.SetResult(null); serverTcs.SetResult(null);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -356,7 +358,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _helloBytes, endStream: true); await SendDataAsync(1, _helloBytes, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -371,7 +373,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(3, _helloBytes, endStream: true); await SendDataAsync(3, _helloBytes, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 2,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 3); withStreamId: 3);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -415,7 +417,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
appDelegateTcs.TrySetResult(null); appDelegateTcs.TrySetResult(null);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -438,7 +440,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
appDelegateTcs.TrySetResult(null); appDelegateTcs.TrySetResult(null);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 6,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 3); withStreamId: 3);
@ -475,7 +477,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
serverTcs.SetResult(null); serverTcs.SetResult(null);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -593,7 +595,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, new byte[length], endStream: true); await SendDataAsync(1, new byte[length], endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
// The client's settings is still defaulted to Http2PeerSettings.MinAllowedMaxFrameSize so the echo response will come back in two separate frames // The client's settings is still defaulted to Http2PeerSettings.MinAllowedMaxFrameSize so the echo response will come back in two separate frames
@ -622,7 +624,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _helloWorldBytes, endStream: true); await SendDataAsync(1, _helloWorldBytes, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
var dataFrame = await ExpectAsync(Http2FrameType.DATA, var dataFrame = await ExpectAsync(Http2FrameType.DATA,
@ -648,7 +650,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _maxData, endStream: true); await SendDataAsync(1, _maxData, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -691,7 +693,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -812,7 +814,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _noData, endStream: true); await SendDataAsync(1, _noData, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
var dataFrame = await ExpectAsync(Http2FrameType.DATA, var dataFrame = await ExpectAsync(Http2FrameType.DATA,
@ -840,7 +842,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _helloBytes, endStream: false); await SendDataAsync(1, _helloBytes, endStream: false);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
var stream1DataFrame1 = await ExpectAsync(Http2FrameType.DATA, var stream1DataFrame1 = await ExpectAsync(Http2FrameType.DATA,
@ -851,7 +853,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(3, _helloBytes, endStream: false); await SendDataAsync(3, _helloBytes, endStream: false);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 2,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 3); withStreamId: 3);
var stream3DataFrame1 = await ExpectAsync(Http2FrameType.DATA, var stream3DataFrame1 = await ExpectAsync(Http2FrameType.DATA,
@ -920,7 +922,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -972,7 +974,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withStreamId: 0); withStreamId: 0);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 2,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 3); withStreamId: 3);
@ -1050,7 +1052,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
stream3ReadFinished.TrySetResult(null); stream3ReadFinished.TrySetResult(null);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 3); withStreamId: 3);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -1065,7 +1067,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
stream1ReadFinished.TrySetResult(null); stream1ReadFinished.TrySetResult(null);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 2,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -1092,7 +1094,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataWithPaddingAsync(1, _helloWorldBytes, padLength, endStream: true); await SendDataWithPaddingAsync(1, _helloWorldBytes, padLength, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
var dataFrame = await ExpectAsync(Http2FrameType.DATA, var dataFrame = await ExpectAsync(Http2FrameType.DATA,
@ -1137,7 +1139,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -1196,7 +1198,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1265,7 +1267,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _maxData, endStream: false); await SendDataAsync(1, _maxData, endStream: false);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -1440,7 +1442,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _postRequestHeaders, endStream: true); await StartStreamAsync(1, _postRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1482,7 +1484,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(3, _browserRequestHeaders, endStream: true); await StartStreamAsync(3, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 3); withStreamId: 3);
@ -1532,7 +1534,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(3, _browserRequestHeaders, endStream: true); await StartStreamAsync(3, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 3); withStreamId: 3);
@ -1636,7 +1638,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -1657,7 +1659,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(3, _browserRequestHeaders, endStream: true); await StartStreamAsync(3, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 6,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 3); withStreamId: 3);
@ -1691,7 +1693,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1706,7 +1708,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1726,7 +1728,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersWithPaddingAsync(1, _browserRequestHeaders, padLength, endStream: true); await SendHeadersWithPaddingAsync(1, _browserRequestHeaders, padLength, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1743,7 +1745,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersWithPriorityAsync(1, _browserRequestHeaders, priority: 42, streamDependency: 0, endStream: true); await SendHeadersWithPriorityAsync(1, _browserRequestHeaders, priority: 42, streamDependency: 0, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1763,7 +1765,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersWithPaddingAndPriorityAsync(1, _browserRequestHeaders, padLength, priority: 42, streamDependency: 0, endStream: true); await SendHeadersWithPaddingAndPriorityAsync(1, _browserRequestHeaders, padLength, priority: 42, streamDependency: 0, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1789,7 +1791,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// The second stream should end first, since the first one is waiting for the request body. // The second stream should end first, since the first one is waiting for the request body.
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 3); withStreamId: 3);
@ -1801,7 +1803,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, _requestTrailers); await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, _requestTrailers);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 6,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1835,7 +1837,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _helloBytes, endStream: true); await SendDataAsync(1, _helloBytes, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -1885,17 +1887,163 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
finishSecondRequest.TrySetResult(null); finishSecondRequest.TrySetResult(null);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 3); withStreamId: 3);
finishFirstRequest.TrySetResult(null); finishFirstRequest.TrySetResult(null);
await ExpectAsync(Http2FrameType.HEADERS,
withLength: 6,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1);
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
}
[Fact]
public async Task HEADERS_HeaderTableSizeLimitZero_Received_DynamicTableUpdate()
{
_serviceContext.ServerOptions.Limits.Http2.HeaderTableSize = 0;
await InitializeConnectionAsync(_noopApplication, expectedSettingsCount: 4);
await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
_hpackEncoder.UpdateMaxHeaderTableSize(0);
var headerFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 38,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1);
const byte DynamicTableSizeUpdateMask = 0xe0;
var integerDecoder = new IntegerDecoder();
Assert.True(integerDecoder.BeginTryDecode((byte)(headerFrame.Payload.Span[0] & ~DynamicTableSizeUpdateMask), prefixLength: 5, out var result));
// Dynamic table update from the server
Assert.Equal(0, result);
await StartStreamAsync(3, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 3);
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
}
[Fact]
public async Task HEADERS_ResponseSetsIgnoreIndexAndNeverIndexValues_HeadersParsed()
{
await InitializeConnectionAsync(c =>
{
c.Response.ContentLength = 0;
c.Response.Headers[HeaderNames.SetCookie] = "SetCookie!";
c.Response.Headers[HeaderNames.ContentDisposition] = "ContentDisposition!";
return Task.CompletedTask;
});
await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
var frame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 90,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1);
var handler = new TestHttpHeadersHandler();
var hpackDecoder = new HPackDecoder();
hpackDecoder.Decode(new ReadOnlySequence<byte>(frame.Payload), endHeaders: true, handler);
hpackDecoder.CompleteDecode();
Assert.Equal("200", handler.Headers[":status"]);
Assert.Equal("SetCookie!", handler.Headers[HeaderNames.SetCookie]);
Assert.Equal("ContentDisposition!", handler.Headers[HeaderNames.ContentDisposition]);
Assert.Equal("0", handler.Headers[HeaderNames.ContentLength]);
await StartStreamAsync(3, _browserRequestHeaders, endStream: true);
frame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 60,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 3);
handler = new TestHttpHeadersHandler();
hpackDecoder.Decode(new ReadOnlySequence<byte>(frame.Payload), endHeaders: true, handler);
hpackDecoder.CompleteDecode();
Assert.Equal("200", handler.Headers[":status"]);
Assert.Equal("SetCookie!", handler.Headers[HeaderNames.SetCookie]);
Assert.Equal("ContentDisposition!", handler.Headers[HeaderNames.ContentDisposition]);
Assert.Equal("0", handler.Headers[HeaderNames.ContentLength]);
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
}
private class TestHttpHeadersHandler : IHttpHeadersHandler
{
public readonly Dictionary<string, StringValues> Headers = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase);
public void OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
{
var nameString = Encoding.ASCII.GetString(name);
var valueString = Encoding.ASCII.GetString(value);
if (Headers.TryGetValue(nameString, out var values))
{
var l = values.ToList();
l.Add(valueString);
Headers[nameString] = new StringValues(l.ToArray());
}
else
{
Headers[nameString] = new StringValues(valueString);
}
}
public void OnHeadersComplete(bool endStream)
{
throw new NotImplementedException();
}
public void OnStaticIndexedHeader(int index)
{
throw new NotImplementedException();
}
public void OnStaticIndexedHeader(int index, ReadOnlySpan<byte> value)
{
throw new NotImplementedException();
}
}
[Fact]
public async Task HEADERS_DisableDynamicHeaderCompression_HeadersNotCompressed()
{
_serviceContext.ServerOptions.AllowResponseHeaderCompression = false;
await InitializeConnectionAsync(_noopApplication);
await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 37,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
await StartStreamAsync(3, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 3);
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false); await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
} }
@ -1918,7 +2066,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
requestBlocker.SetResult(0); requestBlocker.SetResult(0);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1961,7 +2109,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -2004,7 +2152,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(3, _browserRequestHeaders, endStream: true); await StartStreamAsync(3, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 3); withStreamId: 3);
@ -2228,7 +2376,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, headers); await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, headers);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -2373,7 +2521,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, headers); await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, headers);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -2511,7 +2659,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -2536,7 +2684,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// The headers, but not the data for stream 3, can be sent prior to any window updates. // The headers, but not the data for stream 3, can be sent prior to any window updates.
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 2,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 3); withStreamId: 3);
@ -2615,12 +2763,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
}); });
async Task VerifyStreamBackpressure(int streamId) async Task VerifyStreamBackpressure(int streamId, int headersLength)
{ {
await StartStreamAsync(streamId, _browserRequestHeaders, endStream: true); await StartStreamAsync(streamId, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: headersLength,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: streamId); withStreamId: streamId);
@ -2633,9 +2781,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Assert.False(writeTasks[streamId].IsCompleted); Assert.False(writeTasks[streamId].IsCompleted);
} }
await VerifyStreamBackpressure(1); await VerifyStreamBackpressure(1, 32);
await VerifyStreamBackpressure(3); await VerifyStreamBackpressure(3, 2);
await VerifyStreamBackpressure(5); await VerifyStreamBackpressure(5, 2);
await SendRstStreamAsync(1); await SendRstStreamAsync(1);
await writeTasks[1].DefaultTimeout(); await writeTasks[1].DefaultTimeout();
@ -2913,6 +3061,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
{ {
CreateConnection(); CreateConnection();
_connection.ServerSettings.HeaderTableSize = 0;
_connection.ServerSettings.MaxConcurrentStreams = 1; _connection.ServerSettings.MaxConcurrentStreams = 1;
_connection.ServerSettings.MaxHeaderListSize = 4 * 1024; _connection.ServerSettings.MaxHeaderListSize = 4 * 1024;
_connection.ServerSettings.InitialWindowSize = 1024 * 1024 * 10; _connection.ServerSettings.InitialWindowSize = 1024 * 1024 * 10;
@ -2923,23 +3072,27 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendSettingsAsync(); await SendSettingsAsync();
var frame = await ExpectAsync(Http2FrameType.SETTINGS, var frame = await ExpectAsync(Http2FrameType.SETTINGS,
withLength: Http2FrameReader.SettingSize * 3, withLength: Http2FrameReader.SettingSize * 4,
withFlags: 0, withFlags: 0,
withStreamId: 0); withStreamId: 0);
// Only non protocol defaults are sent // Only non protocol defaults are sent
var settings = Http2FrameReader.ReadSettings(frame.PayloadSequence); var settings = Http2FrameReader.ReadSettings(frame.PayloadSequence);
Assert.Equal(3, settings.Count); Assert.Equal(4, settings.Count);
var setting = settings[0]; var setting = settings[0];
Assert.Equal(Http2SettingsParameter.SETTINGS_HEADER_TABLE_SIZE, setting.Parameter);
Assert.Equal(0u, setting.Value);
setting = settings[1];
Assert.Equal(Http2SettingsParameter.SETTINGS_MAX_CONCURRENT_STREAMS, setting.Parameter); Assert.Equal(Http2SettingsParameter.SETTINGS_MAX_CONCURRENT_STREAMS, setting.Parameter);
Assert.Equal(1u, setting.Value); Assert.Equal(1u, setting.Value);
setting = settings[1]; setting = settings[2];
Assert.Equal(Http2SettingsParameter.SETTINGS_INITIAL_WINDOW_SIZE, setting.Parameter); Assert.Equal(Http2SettingsParameter.SETTINGS_INITIAL_WINDOW_SIZE, setting.Parameter);
Assert.Equal(1024 * 1024 * 10u, setting.Value); Assert.Equal(1024 * 1024 * 10u, setting.Value);
setting = settings[2]; setting = settings[3];
Assert.Equal(Http2SettingsParameter.SETTINGS_MAX_HEADER_LIST_SIZE, setting.Parameter); Assert.Equal(Http2SettingsParameter.SETTINGS_MAX_HEADER_LIST_SIZE, setting.Parameter);
Assert.Equal(4 * 1024u, setting.Value); Assert.Equal(4 * 1024u, setting.Value);
@ -3100,7 +3253,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_connection.ServerSettings.MaxFrameSize = Http2PeerSettings.MaxAllowedMaxFrameSize; _connection.ServerSettings.MaxFrameSize = Http2PeerSettings.MaxAllowedMaxFrameSize;
// This includes the default response headers such as :status, etc // This includes the default response headers such as :status, etc
var defaultResponseHeaderLength = 33; var defaultResponseHeaderLength = 32;
var headerValueLength = Http2PeerSettings.MinAllowedMaxFrameSize; var headerValueLength = Http2PeerSettings.MinAllowedMaxFrameSize;
// First byte is always 0 // First byte is always 0
// Second byte is the length of header name which is 1 // Second byte is the length of header name which is 1
@ -3170,7 +3323,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -3204,7 +3357,56 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withFlags: (byte)Http2SettingsFrameFlags.ACK, withFlags: (byte)Http2SettingsFrameFlags.ACK,
withStreamId: 0); withStreamId: 0);
await StopConnectionAsync(expectedLastStreamId: 0, ignoreNonGoAwayFrames: false); // Start request
await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
var headerFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1);
// Headers start with :status = 200
Assert.Equal(0x88, headerFrame.Payload.Span[0]);
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
}
[Fact]
public async Task SETTINGS_Received_WithLargeHeaderTableSizeLimit_ChangesHeaderTableSize()
{
_serviceContext.ServerOptions.Limits.Http2.HeaderTableSize = 40000;
await InitializeConnectionAsync(_noopApplication, expectedSettingsCount: 4);
// Update client settings
_clientSettings.HeaderTableSize = 65536; // Chrome's default, larger than the 4kb spec default
await SendSettingsAsync();
// ACK
await ExpectAsync(Http2FrameType.SETTINGS,
withLength: 0,
withFlags: (byte)Http2SettingsFrameFlags.ACK,
withStreamId: 0);
// Start request
await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
var headerFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 40,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1);
const byte DynamicTableSizeUpdateMask = 0xe0;
var integerDecoder = new IntegerDecoder();
Assert.False(integerDecoder.BeginTryDecode((byte)(headerFrame.Payload.Span[0] & ~DynamicTableSizeUpdateMask), prefixLength: 5, out _));
Assert.False(integerDecoder.TryDecode(headerFrame.Payload.Span[1], out _));
Assert.False(integerDecoder.TryDecode(headerFrame.Payload.Span[2], out _));
Assert.True(integerDecoder.TryDecode(headerFrame.Payload.Span[3], out var result));
Assert.Equal(40000, result);
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
} }
[Fact] [Fact]
@ -3319,7 +3521,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _helloBytes, true); await SendDataAsync(1, _helloBytes, true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -3332,7 +3534,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withStreamId: 1); withStreamId: 1);
await SendDataAsync(3, _helloBytes, true); await SendDataAsync(3, _helloBytes, true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 2,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 3); withStreamId: 3);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -3405,7 +3607,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -3428,13 +3630,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// The headers, but not the data for the stream, can still be sent. // The headers, but not the data for the stream, can still be sent.
await StartStreamAsync(3, _browserRequestHeaders, endStream: true); await StartStreamAsync(3, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 2,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 3); withStreamId: 3);
await StartStreamAsync(5, _browserRequestHeaders, endStream: true); await StartStreamAsync(5, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 2,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 5); withStreamId: 5);
@ -3493,12 +3695,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
}); });
async Task VerifyStreamBackpressure(int streamId) async Task VerifyStreamBackpressure(int streamId, int headersLength)
{ {
await StartStreamAsync(streamId, _browserRequestHeaders, endStream: true); await StartStreamAsync(streamId, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: headersLength,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: streamId); withStreamId: streamId);
@ -3511,9 +3713,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
Assert.False(writeTasks[streamId].IsCompleted); Assert.False(writeTasks[streamId].IsCompleted);
} }
await VerifyStreamBackpressure(1); await VerifyStreamBackpressure(1, 32);
await VerifyStreamBackpressure(3); await VerifyStreamBackpressure(3, 2);
await VerifyStreamBackpressure(5); await VerifyStreamBackpressure(5, 2);
// Close all pipes and wait for response to drain // Close all pipes and wait for response to drain
_pair.Application.Output.Complete(); _pair.Application.Output.Complete();
@ -3731,7 +3933,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -3788,7 +3990,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _helloWorldBytes, endStream: true); await SendDataAsync(1, _helloWorldBytes, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -3827,7 +4029,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _helloWorldBytes, endStream: true); await SendDataAsync(1, _helloWorldBytes, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -3879,7 +4081,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _twoContinuationsRequestHeaders, endStream: true); await StartStreamAsync(1, _twoContinuationsRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -3906,7 +4108,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// The second stream should end first, since the first one is waiting for the request body. // The second stream should end first, since the first one is waiting for the request body.
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 3); withStreamId: 3);
@ -3929,7 +4131,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendContinuationAsync(1, Http2ContinuationFrameFlags.END_HEADERS, trailers); await SendContinuationAsync(1, Http2ContinuationFrameFlags.END_HEADERS, trailers);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 6,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -4027,7 +4229,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendEmptyContinuationFrameAsync(1, Http2ContinuationFrameFlags.END_HEADERS); await SendEmptyContinuationFrameAsync(1, Http2ContinuationFrameFlags.END_HEADERS);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -4042,7 +4244,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 12343, withLength: 12342,
withFlags: (byte)Http2HeadersFrameFlags.END_STREAM, withFlags: (byte)Http2HeadersFrameFlags.END_STREAM,
withStreamId: 1); withStreamId: 1);
var continuationFrame1 = await ExpectAsync(Http2FrameType.CONTINUATION, var continuationFrame1 = await ExpectAsync(Http2FrameType.CONTINUATION,
@ -4201,7 +4403,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -4251,7 +4453,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _helloBytes, true); await SendDataAsync(1, _helloBytes, true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -4284,8 +4486,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(3, _browserRequestHeaders, endStream: false); await StartStreamAsync(3, _browserRequestHeaders, endStream: false);
await SendDataAsync(1, _helloBytes, true); await SendDataAsync(1, _helloBytes, true);
await ExpectAsync(Http2FrameType.HEADERS, var f = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -4298,7 +4500,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withStreamId: 1); withStreamId: 1);
await SendDataAsync(3, _helloBytes, true); await SendDataAsync(3, _helloBytes, true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 2,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 3); withStreamId: 3);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -4388,7 +4590,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: false); await StartStreamAsync(1, headers, endStream: false);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);

View File

@ -79,7 +79,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 52, withLength: 51,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -104,7 +104,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, headers); await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, headers);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 53, withLength: 52,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -131,7 +131,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, headers); await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, headers);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 57, withLength: 56,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -159,7 +159,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, headers); await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, headers);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 58, withLength: 57,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -193,7 +193,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, headers); await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, headers);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 100, withLength: 99,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -235,7 +235,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, headers); await SendHeadersAsync(1, Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM, headers);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -297,7 +297,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -326,7 +326,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -355,7 +355,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 47, withLength: 46,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -386,7 +386,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 47, withLength: 46,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -417,7 +417,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 47, withLength: 46,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -448,7 +448,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 47, withLength: 46,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -570,7 +570,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, new byte[12], endStream: true); await SendDataAsync(1, new byte[12], endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -611,7 +611,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, new byte[12], endStream: true); await SendDataAsync(1, new byte[12], endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -655,7 +655,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, new byte[8], endStream: true); await SendDataAsync(1, new byte[8], endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -698,7 +698,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, new byte[8], endStream: true); await SendDataAsync(1, new byte[8], endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -751,7 +751,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, new byte[8], endStream: true); await SendDataAsync(1, new byte[8], endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -983,7 +983,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, new byte[12], endStream: true); await SendDataAsync(1, new byte[12], endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1015,7 +1015,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 38, withLength: 37,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.RST_STREAM, await ExpectAsync(Http2FrameType.RST_STREAM,
@ -1054,7 +1054,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 38, withLength: 37,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -1092,7 +1092,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1125,7 +1125,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -1160,7 +1160,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -1198,7 +1198,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -1236,7 +1236,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -1276,7 +1276,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -1323,7 +1323,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -1361,7 +1361,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -1397,7 +1397,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -1441,7 +1441,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 38, withLength: 37,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -1475,7 +1475,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -1508,7 +1508,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 38, withLength: 37,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -1552,7 +1552,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, new byte[12], endStream: true); await SendDataAsync(1, new byte[12], endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1591,7 +1591,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: false); await StartStreamAsync(1, headers, endStream: false);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 41, withLength: 40,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1634,7 +1634,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, new byte[12], endStream: true); await SendDataAsync(1, new byte[12], endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1674,7 +1674,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, new byte[6], endStream: false); await SendDataAsync(1, new byte[6], endStream: false);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 41, withLength: 40,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1733,7 +1733,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, new byte[6], endStream: false); await SendDataAsync(1, new byte[6], endStream: false);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 41, withLength: 40,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1788,7 +1788,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, new byte[12], endStream: true); await SendDataAsync(1, new byte[12], endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -1814,7 +1814,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -1852,7 +1852,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_STREAM | Http2HeadersFrameFlags.END_HEADERS), withFlags: (byte)(Http2HeadersFrameFlags.END_STREAM | Http2HeadersFrameFlags.END_HEADERS),
withStreamId: 1); withStreamId: 1);
@ -1883,7 +1883,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
var headersFrame1 = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame1 = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
withStreamId: 1); withStreamId: 1);
var trailersFrame1 = await ExpectAsync(Http2FrameType.HEADERS, var trailersFrame1 = await ExpectAsync(Http2FrameType.HEADERS,
@ -1894,12 +1894,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(3, _browserRequestHeaders, endStream: true); await StartStreamAsync(3, _browserRequestHeaders, endStream: true);
var headersFrame2 = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame2 = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 6,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
withStreamId: 3); withStreamId: 3);
var trailersFrame2 = await ExpectAsync(Http2FrameType.HEADERS, var trailersFrame2 = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 25, withLength: 1,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 3); withStreamId: 3);
@ -1930,7 +1930,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -1980,7 +1980,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -2039,7 +2039,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -2074,7 +2074,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -2124,7 +2124,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true).DefaultTimeout(); await StartStreamAsync(1, headers, endStream: true).DefaultTimeout();
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1).DefaultTimeout(); withStreamId: 1).DefaultTimeout();
@ -2189,7 +2189,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -2235,7 +2235,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -2269,7 +2269,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -2532,7 +2532,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -2623,7 +2623,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, _browserRequestHeaders, endStream: true); await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -2671,7 +2671,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Just the StatusCode gets written before aborting in the continuation frame // Just the StatusCode gets written before aborting in the continuation frame
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.NONE, withFlags: (byte)Http2HeadersFrameFlags.NONE,
withStreamId: 1); withStreamId: 1);
@ -2700,7 +2700,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -2743,7 +2743,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -2789,7 +2789,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
var dataFrame = await ExpectAsync(Http2FrameType.DATA, var dataFrame = await ExpectAsync(Http2FrameType.DATA,
@ -2835,7 +2835,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
var dataFrame = await ExpectAsync(Http2FrameType.DATA, var dataFrame = await ExpectAsync(Http2FrameType.DATA,
@ -2884,7 +2884,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
var dataFrame = await ExpectAsync(Http2FrameType.DATA, var dataFrame = await ExpectAsync(Http2FrameType.DATA,
@ -2937,7 +2937,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
var dataFrame = await ExpectAsync(Http2FrameType.DATA, var dataFrame = await ExpectAsync(Http2FrameType.DATA,
@ -2987,7 +2987,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
var dataFrame = await ExpectAsync(Http2FrameType.DATA, var dataFrame = await ExpectAsync(Http2FrameType.DATA,
@ -3037,7 +3037,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
var dataFrame = await ExpectAsync(Http2FrameType.DATA, var dataFrame = await ExpectAsync(Http2FrameType.DATA,
@ -3080,7 +3080,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
var dataFrame = await ExpectAsync(Http2FrameType.DATA, var dataFrame = await ExpectAsync(Http2FrameType.DATA,
@ -3126,7 +3126,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
var dataFrame = await ExpectAsync(Http2FrameType.DATA, var dataFrame = await ExpectAsync(Http2FrameType.DATA,
@ -3168,7 +3168,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -3213,7 +3213,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -3279,7 +3279,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 38, withLength: 37,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -3325,7 +3325,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 38, withLength: 37,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -3361,7 +3361,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 38, withLength: 37,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -3413,7 +3413,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 38, withLength: 37,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -3465,7 +3465,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -3498,7 +3498,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
// Don't receive content length because we called WriteAsync which caused an invalid response // Don't receive content length because we called WriteAsync which caused an invalid response
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS | (byte)Http2HeadersFrameFlags.END_STREAM, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS | (byte)Http2HeadersFrameFlags.END_STREAM,
withStreamId: 1); withStreamId: 1);
@ -3531,7 +3531,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -3583,7 +3583,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -3639,7 +3639,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
withStreamId: 1); withStreamId: 1);
var trailersFrame = await ExpectAsync(Http2FrameType.HEADERS, var trailersFrame = await ExpectAsync(Http2FrameType.HEADERS,
@ -3705,7 +3705,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -3761,7 +3761,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
withStreamId: 1); withStreamId: 1);
var bodyFrame = await ExpectAsync(Http2FrameType.DATA, var bodyFrame = await ExpectAsync(Http2FrameType.DATA,
@ -3826,7 +3826,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -3885,7 +3885,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
withStreamId: 1); withStreamId: 1);
var bodyFrame = await ExpectAsync(Http2FrameType.DATA, var bodyFrame = await ExpectAsync(Http2FrameType.DATA,
@ -3941,7 +3941,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -4003,7 +4003,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
withStreamId: 1); withStreamId: 1);
var bodyFrame = await ExpectAsync(Http2FrameType.DATA, var bodyFrame = await ExpectAsync(Http2FrameType.DATA,
@ -4077,7 +4077,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
withStreamId: 1); withStreamId: 1);
var bodyFrame = await ExpectAsync(Http2FrameType.DATA, var bodyFrame = await ExpectAsync(Http2FrameType.DATA,
@ -4153,7 +4153,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 38, withLength: 37,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
withStreamId: 1); withStreamId: 1);
var bodyFrame = await ExpectAsync(Http2FrameType.DATA, var bodyFrame = await ExpectAsync(Http2FrameType.DATA,
@ -4224,7 +4224,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 38, withLength: 37,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
withStreamId: 1); withStreamId: 1);
var bodyFrame = await ExpectAsync(Http2FrameType.DATA, var bodyFrame = await ExpectAsync(Http2FrameType.DATA,
@ -4296,7 +4296,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
withStreamId: 1); withStreamId: 1);
var bodyFrame = await ExpectAsync(Http2FrameType.DATA, var bodyFrame = await ExpectAsync(Http2FrameType.DATA,
@ -4380,7 +4380,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: false); await StartStreamAsync(1, headers, endStream: false);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
withStreamId: 1); withStreamId: 1);
var bodyFrame = await ExpectAsync(Http2FrameType.DATA, var bodyFrame = await ExpectAsync(Http2FrameType.DATA,
@ -4461,7 +4461,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: true); await StartStreamAsync(1, headers, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
withStreamId: 1); withStreamId: 1);
var bodyFrame = await ExpectAsync(Http2FrameType.DATA, var bodyFrame = await ExpectAsync(Http2FrameType.DATA,
@ -4548,7 +4548,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, headers, endStream: false); await StartStreamAsync(1, headers, endStream: false);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS),
withStreamId: 1); withStreamId: 1);
var bodyFrame = await ExpectAsync(Http2FrameType.DATA, var bodyFrame = await ExpectAsync(Http2FrameType.DATA,
@ -4608,7 +4608,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await StartStreamAsync(1, LatinHeaderData, endStream: true); await StartStreamAsync(1, LatinHeaderData, endStream: true);
var headersFrame = await ExpectAsync(Http2FrameType.HEADERS, var headersFrame = await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);

View File

@ -121,6 +121,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
internal readonly Http2PeerSettings _clientSettings = new Http2PeerSettings(); internal readonly Http2PeerSettings _clientSettings = new Http2PeerSettings();
internal readonly HPackDecoder _hpackDecoder; internal readonly HPackDecoder _hpackDecoder;
internal readonly HPackEncoder _hpackEncoder;
private readonly byte[] _headerEncodingBuffer = new byte[Http2PeerSettings.MinAllowedMaxFrameSize]; private readonly byte[] _headerEncodingBuffer = new byte[Http2PeerSettings.MinAllowedMaxFrameSize];
internal readonly TimeoutControl _timeoutControl; internal readonly TimeoutControl _timeoutControl;
@ -165,6 +166,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
public Http2TestBase() public Http2TestBase()
{ {
_hpackDecoder = new HPackDecoder((int)_clientSettings.HeaderTableSize, MaxRequestHeaderFieldSize); _hpackDecoder = new HPackDecoder((int)_clientSettings.HeaderTableSize, MaxRequestHeaderFieldSize);
_hpackEncoder = new HPackEncoder();
_timeoutControl = new TimeoutControl(_mockTimeoutHandler.Object); _timeoutControl = new TimeoutControl(_mockTimeoutHandler.Object);
_mockTimeoutControl = new Mock<MockTimeoutControlBase>(_timeoutControl) { CallBase = true }; _mockTimeoutControl = new Mock<MockTimeoutControlBase>(_timeoutControl) { CallBase = true };
@ -501,7 +503,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously); var tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
_runningStreams[streamId] = tcs; _runningStreams[streamId] = tcs;
writableBuffer.WriteStartStream(streamId, GetHeadersEnumerator(headers), _headerEncodingBuffer, endStream); writableBuffer.WriteStartStream(streamId, _hpackEncoder, GetHeadersEnumerator(headers), _headerEncodingBuffer, endStream);
return FlushAsync(writableBuffer); return FlushAsync(writableBuffer);
} }
@ -541,7 +543,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
extendedHeader[0] = padLength; extendedHeader[0] = padLength;
var payload = buffer.Slice(extendedHeaderLength, buffer.Length - padLength - extendedHeaderLength); var payload = buffer.Slice(extendedHeaderLength, buffer.Length - padLength - extendedHeaderLength);
HPackHeaderWriter.BeginEncodeHeaders(GetHeadersEnumerator(headers), payload, out var length); HPackHeaderWriter.BeginEncodeHeaders(_hpackEncoder, GetHeadersEnumerator(headers), payload, out var length);
var padding = buffer.Slice(extendedHeaderLength + length, padLength); var padding = buffer.Slice(extendedHeaderLength + length, padLength);
padding.Fill(0); padding.Fill(0);
@ -584,7 +586,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
extendedHeader[4] = priority; extendedHeader[4] = priority;
var payload = buffer.Slice(extendedHeaderLength); var payload = buffer.Slice(extendedHeaderLength);
HPackHeaderWriter.BeginEncodeHeaders(GetHeadersEnumerator(headers), payload, out var length); HPackHeaderWriter.BeginEncodeHeaders(_hpackEncoder, GetHeadersEnumerator(headers), payload, out var length);
frame.PayloadLength = extendedHeaderLength + length; frame.PayloadLength = extendedHeaderLength + length;
@ -631,7 +633,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
extendedHeader[5] = priority; extendedHeader[5] = priority;
var payload = buffer.Slice(extendedHeaderLength, buffer.Length - padLength - extendedHeaderLength); var payload = buffer.Slice(extendedHeaderLength, buffer.Length - padLength - extendedHeaderLength);
HPackHeaderWriter.BeginEncodeHeaders(GetHeadersEnumerator(headers), payload, out var length); HPackHeaderWriter.BeginEncodeHeaders(_hpackEncoder, GetHeadersEnumerator(headers), payload, out var length);
var padding = buffer.Slice(extendedHeaderLength + length, padLength); var padding = buffer.Slice(extendedHeaderLength + length, padLength);
padding.Fill(0); padding.Fill(0);
@ -745,7 +747,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
frame.PrepareHeaders(flags, streamId); frame.PrepareHeaders(flags, streamId);
var buffer = _headerEncodingBuffer.AsMemory(); var buffer = _headerEncodingBuffer.AsMemory();
var done = HPackHeaderWriter.BeginEncodeHeaders(headersEnumerator, buffer.Span, out var length); var done = HPackHeaderWriter.BeginEncodeHeaders(_hpackEncoder, headersEnumerator, buffer.Span, out var length);
frame.PayloadLength = length; frame.PayloadLength = length;
Http2FrameWriter.WriteHeader(frame, outputWriter); Http2FrameWriter.WriteHeader(frame, outputWriter);
@ -815,7 +817,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
frame.PrepareContinuation(flags, streamId); frame.PrepareContinuation(flags, streamId);
var buffer = _headerEncodingBuffer.AsMemory(); var buffer = _headerEncodingBuffer.AsMemory();
var done = HPackHeaderWriter.ContinueEncodeHeaders(headersEnumerator, buffer.Span, out var length); var done = HPackHeaderWriter.ContinueEncodeHeaders(_hpackEncoder, headersEnumerator, buffer.Span, out var length);
frame.PayloadLength = length; frame.PayloadLength = length;
Http2FrameWriter.WriteHeader(frame, outputWriter); Http2FrameWriter.WriteHeader(frame, outputWriter);
@ -843,7 +845,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
frame.PrepareContinuation(flags, streamId); frame.PrepareContinuation(flags, streamId);
var buffer = _headerEncodingBuffer.AsMemory(); var buffer = _headerEncodingBuffer.AsMemory();
var done = HPackHeaderWriter.BeginEncodeHeaders(GetHeadersEnumerator(headers), buffer.Span, out var length); var done = HPackHeaderWriter.BeginEncodeHeaders(_hpackEncoder, GetHeadersEnumerator(headers), buffer.Span, out var length);
frame.PayloadLength = length; frame.PayloadLength = length;
Http2FrameWriter.WriteHeader(frame, outputWriter); Http2FrameWriter.WriteHeader(frame, outputWriter);

View File

@ -101,7 +101,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
_mockTimeoutControl.Verify(c => c.SetTimeout(It.IsAny<long>(), TimeoutReason.RequestHeaders), Times.Once); _mockTimeoutControl.Verify(c => c.SetTimeout(It.IsAny<long>(), TimeoutReason.RequestHeaders), Times.Once);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 36,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);
@ -283,7 +283,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _helloWorldBytes, endStream: true); await SendDataAsync(1, _helloWorldBytes, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -336,7 +336,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _maxData, endStream: true); await SendDataAsync(1, _maxData, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -390,7 +390,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _helloWorldBytes, endStream: true); await SendDataAsync(1, _helloWorldBytes, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -445,7 +445,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _maxData, endStream: true); await SendDataAsync(1, _maxData, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -501,7 +501,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _maxData, endStream: true); await SendDataAsync(1, _maxData, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -513,7 +513,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(3, _maxData, endStream: true); await SendDataAsync(3, _maxData, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 3); withStreamId: 3);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -567,7 +567,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _helloWorldBytes, endStream: false); await SendDataAsync(1, _helloWorldBytes, endStream: false);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -616,7 +616,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _maxData, endStream: false); await SendDataAsync(1, _maxData, endStream: false);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -669,7 +669,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _maxData, endStream: false); await SendDataAsync(1, _maxData, endStream: false);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -682,7 +682,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(3, _maxData, endStream: false); await SendDataAsync(3, _maxData, endStream: false);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 2,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 3); withStreamId: 3);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -738,7 +738,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _maxData, endStream: true); await SendDataAsync(1, _maxData, endStream: true);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -756,7 +756,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(3, _maxData, endStream: false); await SendDataAsync(3, _maxData, endStream: false);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 2,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 3); withStreamId: 3);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -813,7 +813,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(1, _helloWorldBytes, endStream: false); await SendDataAsync(1, _helloWorldBytes, endStream: false);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 1); withStreamId: 1);
@ -885,7 +885,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await SendDataAsync(3, _helloWorldBytes, endStream: false); await SendDataAsync(3, _helloWorldBytes, endStream: false);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 33, withLength: 32,
withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS,
withStreamId: 3); withStreamId: 3);
await ExpectAsync(Http2FrameType.DATA, await ExpectAsync(Http2FrameType.DATA,
@ -902,7 +902,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
backpressureTcs.SetResult(null); backpressureTcs.SetResult(null);
await ExpectAsync(Http2FrameType.HEADERS, await ExpectAsync(Http2FrameType.HEADERS,
withLength: 37, withLength: 6,
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM), withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1); withStreamId: 1);

View File

@ -1118,7 +1118,7 @@ namespace Interop.FunctionalTests
Assert.Equal(oneKbString + i, response.Headers.GetValues("header" + i).Single()); Assert.Equal(oneKbString + i, response.Headers.GetValues("header" + i).Single());
} }
Assert.Single(TestSink.Writes.Where(context => context.Message.Contains("sending HEADERS frame for stream ID 1 with length 15612 and flags END_STREAM"))); Assert.Single(TestSink.Writes.Where(context => context.Message.Contains("sending HEADERS frame for stream ID 1 with length 15610 and flags END_STREAM")));
Assert.Equal(2, TestSink.Writes.Where(context => context.Message.Contains("sending CONTINUATION frame for stream ID 1 with length 15585 and flags NONE")).Count()); Assert.Equal(2, TestSink.Writes.Where(context => context.Message.Contains("sending CONTINUATION frame for stream ID 1 with length 15585 and flags NONE")).Count());
Assert.Single(TestSink.Writes.Where(context => context.Message.Contains("sending CONTINUATION frame for stream ID 1 with length 14546 and flags END_HEADERS"))); Assert.Single(TestSink.Writes.Where(context => context.Message.Contains("sending CONTINUATION frame for stream ID 1 with length 14546 and flags END_HEADERS")));

View File

@ -0,0 +1,73 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
namespace System.Net.Http.HPack
{
[DebuggerDisplay("Name = {Name} Value = {Value}")]
internal class EncoderHeaderEntry
{
// Header name and value
public string Name;
public string Value;
// Chained list of headers in the same bucket
public EncoderHeaderEntry Next;
public int Hash;
// Compute dynamic table index
public int Index;
// Doubly linked list
public EncoderHeaderEntry Before;
public EncoderHeaderEntry After;
/// <summary>
/// Initialize header values. An entry will be reinitialized when reused.
/// </summary>
public void Initialize(int hash, string name, string value, int index, EncoderHeaderEntry next)
{
Debug.Assert(name != null);
Debug.Assert(value != null);
Name = name;
Value = value;
Index = index;
Hash = hash;
Next = next;
}
public uint CalculateSize()
{
return (uint)HeaderField.GetLength(Name.Length, Value.Length);
}
/// <summary>
/// Remove entry from the linked list and reset header values.
/// </summary>
public void Remove()
{
Before.After = After;
After.Before = Before;
Before = null;
After = null;
Next = null;
Hash = 0;
Name = null;
Value = null;
}
/// <summary>
/// Add before an entry in the linked list.
/// </summary>
public void AddBefore(EncoderHeaderEntry existingEntry)
{
After = existingEntry;
Before = existingEntry.Before;
Before.After = this;
After.Before = this;
}
}
}

View File

@ -0,0 +1,295 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System.Diagnostics;
namespace System.Net.Http.HPack
{
internal partial class HPackEncoder
{
public const int DefaultHeaderTableSize = 4096;
// Internal for testing
internal readonly EncoderHeaderEntry Head;
private readonly bool _allowDynamicCompression;
private readonly EncoderHeaderEntry[] _headerBuckets;
private readonly byte _hashMask;
private uint _headerTableSize;
private uint _maxHeaderTableSize;
private bool _pendingTableSizeUpdate;
private EncoderHeaderEntry? _removed;
public HPackEncoder(bool allowDynamicCompression = true, uint maxHeaderTableSize = DefaultHeaderTableSize)
{
_allowDynamicCompression = allowDynamicCompression;
_maxHeaderTableSize = maxHeaderTableSize;
Head = new EncoderHeaderEntry();
Head.Initialize(-1, string.Empty, string.Empty, int.MaxValue, null);
// Bucket count balances memory usage and the expected low number of headers (constrained by the header table size).
// Performance with different bucket counts hasn't been measured in detail.
_headerBuckets = new EncoderHeaderEntry[16];
_hashMask = (byte)(_headerBuckets.Length - 1);
Head.Before = Head.After = Head;
}
public void UpdateMaxHeaderTableSize(uint maxHeaderTableSize)
{
if (_maxHeaderTableSize != maxHeaderTableSize)
{
_maxHeaderTableSize = maxHeaderTableSize;
// Dynamic table size update will be written next HEADERS frame
_pendingTableSizeUpdate = true;
// Check capacity and remove entries that exceed the new capacity
EnsureCapacity(0);
}
}
public bool EnsureDynamicTableSizeUpdate(Span<byte> buffer, out int length)
{
// Check if there is a table size update that should be encoded
if (_pendingTableSizeUpdate)
{
bool success = EncodeDynamicTableSizeUpdate((int)_maxHeaderTableSize, buffer, out length);
_pendingTableSizeUpdate = false;
return success;
}
length = 0;
return true;
}
public bool EncodeHeader(Span<byte> buffer, int staticTableIndex, HeaderEncodingHint encodingHint, string name, string value, out int bytesWritten)
{
Debug.Assert(!_pendingTableSizeUpdate, "Dynamic table size update should be encoded before headers.");
// Never index sensitive value.
if (encodingHint == HeaderEncodingHint.NeverIndex)
{
int index = ResolveDynamicTableIndex(staticTableIndex, name);
return index == -1
? EncodeLiteralHeaderFieldNeverIndexingNewName(name, value, buffer, out bytesWritten)
: EncodeLiteralHeaderFieldNeverIndexing(index, value, buffer, out bytesWritten);
}
// No dynamic table. Only use the static table.
if (!_allowDynamicCompression || _maxHeaderTableSize == 0 || encodingHint == HeaderEncodingHint.IgnoreIndex)
{
return staticTableIndex == -1
? EncodeLiteralHeaderFieldWithoutIndexingNewName(name, value, buffer, out bytesWritten)
: EncodeLiteralHeaderFieldWithoutIndexing(staticTableIndex, value, buffer, out bytesWritten);
}
// Header is greater than the maximum table size.
// Don't attempt to add dynamic header as all existing dynamic headers will be removed.
if (HeaderField.GetLength(name.Length, value.Length) > _maxHeaderTableSize)
{
int index = ResolveDynamicTableIndex(staticTableIndex, name);
return index == -1
? EncodeLiteralHeaderFieldWithoutIndexingNewName(name, value, buffer, out bytesWritten)
: EncodeLiteralHeaderFieldWithoutIndexing(index, value, buffer, out bytesWritten);
}
return EncodeDynamicHeader(buffer, staticTableIndex, name, value, out bytesWritten);
}
private int ResolveDynamicTableIndex(int staticTableIndex, string name)
{
if (staticTableIndex != -1)
{
// Prefer static table index.
return staticTableIndex;
}
return CalculateDynamicTableIndex(name);
}
private bool EncodeDynamicHeader(Span<byte> buffer, int staticTableIndex, string name, string value, out int bytesWritten)
{
EncoderHeaderEntry? headerField = GetEntry(name, value);
if (headerField != null)
{
// Already exists in dynamic table. Write index.
int index = CalculateDynamicTableIndex(headerField.Index);
return EncodeIndexedHeaderField(index, buffer, out bytesWritten);
}
else
{
// Doesn't exist in dynamic table. Add new entry to dynamic table.
uint headerSize = (uint)HeaderField.GetLength(name.Length, value.Length);
int index = ResolveDynamicTableIndex(staticTableIndex, name);
bool success = index == -1
? EncodeLiteralHeaderFieldIndexingNewName(name, value, buffer, out bytesWritten)
: EncodeLiteralHeaderFieldIndexing(index, value, buffer, out bytesWritten);
if (success)
{
EnsureCapacity(headerSize);
AddHeaderEntry(name, value, headerSize);
}
return success;
}
}
/// <summary>
/// Ensure there is capacity for the new header. If there is not enough capacity then remove
/// existing headers until space is available.
/// </summary>
private void EnsureCapacity(uint headerSize)
{
Debug.Assert(headerSize <= _maxHeaderTableSize, "Header is bigger than dynamic table size.");
while (_maxHeaderTableSize - _headerTableSize < headerSize)
{
EncoderHeaderEntry? removed = RemoveHeaderEntry();
Debug.Assert(removed != null);
// Removed entries are tracked to be reused.
PushRemovedEntry(removed);
}
}
private EncoderHeaderEntry? GetEntry(string name, string value)
{
if (_headerTableSize == 0)
{
return null;
}
int hash = name.GetHashCode();
int bucketIndex = CalculateBucketIndex(hash);
for (EncoderHeaderEntry? e = _headerBuckets[bucketIndex]; e != null; e = e.Next)
{
// We've already looked up entries based on a hash of the name.
// Compare value before name as it is more likely to be different.
if (e.Hash == hash &&
string.Equals(value, e.Value, StringComparison.Ordinal) &&
string.Equals(name, e.Name, StringComparison.Ordinal))
{
return e;
}
}
return null;
}
private int CalculateDynamicTableIndex(string name)
{
if (_headerTableSize == 0)
{
return -1;
}
int hash = name.GetHashCode();
int bucketIndex = CalculateBucketIndex(hash);
for (EncoderHeaderEntry? e = _headerBuckets[bucketIndex]; e != null; e = e.Next)
{
if (e.Hash == hash && string.Equals(name, e.Name, StringComparison.Ordinal))
{
return CalculateDynamicTableIndex(e.Index);
}
}
return -1;
}
private int CalculateDynamicTableIndex(int index)
{
return index == -1 ? -1 : index - Head.Before.Index + 1 + H2StaticTable.Count;
}
private void AddHeaderEntry(string name, string value, uint headerSize)
{
Debug.Assert(headerSize <= _maxHeaderTableSize, "Header is bigger than dynamic table size.");
Debug.Assert(headerSize <= _maxHeaderTableSize - _headerTableSize, "Not enough room in dynamic table.");
int hash = name.GetHashCode();
int bucketIndex = CalculateBucketIndex(hash);
EncoderHeaderEntry? oldEntry = _headerBuckets[bucketIndex];
// Attempt to reuse removed entry
EncoderHeaderEntry? newEntry = PopRemovedEntry() ?? new EncoderHeaderEntry();
newEntry.Initialize(hash, name, value, Head.Before.Index - 1, oldEntry);
_headerBuckets[bucketIndex] = newEntry;
newEntry.AddBefore(Head);
_headerTableSize += headerSize;
}
private void PushRemovedEntry(EncoderHeaderEntry removed)
{
if (_removed != null)
{
removed.Next = _removed;
}
_removed = removed;
}
private EncoderHeaderEntry? PopRemovedEntry()
{
if (_removed != null)
{
EncoderHeaderEntry? removed = _removed;
_removed = _removed.Next;
return removed;
}
return null;
}
/// <summary>
/// Remove the oldest entry.
/// </summary>
private EncoderHeaderEntry? RemoveHeaderEntry()
{
if (_headerTableSize == 0)
{
return null;
}
EncoderHeaderEntry? eldest = Head.After;
int hash = eldest.Hash;
int bucketIndex = CalculateBucketIndex(hash);
EncoderHeaderEntry? prev = _headerBuckets[bucketIndex];
EncoderHeaderEntry? e = prev;
while (e != null)
{
EncoderHeaderEntry next = e.Next;
if (e == eldest)
{
if (prev == eldest)
{
_headerBuckets[bucketIndex] = next;
}
else
{
prev.Next = next;
}
_headerTableSize -= eldest.CalculateSize();
eldest.Remove();
return eldest;
}
prev = e;
e = next;
}
return null;
}
private int CalculateBucketIndex(int hash)
{
return hash & _hashMask;
}
}
/// <summary>
/// Hint for how the header should be encoded as HPack. This value can be overriden.
/// For example, a header that is larger than the dynamic table won't be indexed.
/// </summary>
internal enum HeaderEncodingHint
{
Index,
IgnoreIndex,
NeverIndex
}
}

View File

@ -0,0 +1,3 @@
HPack dynamic compression. These files are kept separate to help avoid ASP.NET Core dependencies being added to them.
Runtime currently doesn't implement HPack dynamic compression. These files will move into runtime shareable code in the future when support is added to runtime.

View File

@ -0,0 +1,151 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Globalization;
using System.Text;
namespace System.Net.Http.HPack
{
internal static partial class StatusCodes
{
public static string ToStatusString(int statusCode)
{
switch (statusCode)
{
case (int)HttpStatusCode.Continue:
return "100";
case (int)HttpStatusCode.SwitchingProtocols:
return "101";
case (int)HttpStatusCode.Processing:
return "102";
case (int)HttpStatusCode.OK:
return "200";
case (int)HttpStatusCode.Created:
return "201";
case (int)HttpStatusCode.Accepted:
return "202";
case (int)HttpStatusCode.NonAuthoritativeInformation:
return "203";
case (int)HttpStatusCode.NoContent:
return "204";
case (int)HttpStatusCode.ResetContent:
return "205";
case (int)HttpStatusCode.PartialContent:
return "206";
case (int)HttpStatusCode.MultiStatus:
return "207";
case (int)HttpStatusCode.AlreadyReported:
return "208";
case (int)HttpStatusCode.IMUsed:
return "226";
case (int)HttpStatusCode.MultipleChoices:
return "300";
case (int)HttpStatusCode.MovedPermanently:
return "301";
case (int)HttpStatusCode.Found:
return "302";
case (int)HttpStatusCode.SeeOther:
return "303";
case (int)HttpStatusCode.NotModified:
return "304";
case (int)HttpStatusCode.UseProxy:
return "305";
case (int)HttpStatusCode.Unused:
return "306";
case (int)HttpStatusCode.TemporaryRedirect:
return "307";
case (int)HttpStatusCode.PermanentRedirect:
return "308";
case (int)HttpStatusCode.BadRequest:
return "400";
case (int)HttpStatusCode.Unauthorized:
return "401";
case (int)HttpStatusCode.PaymentRequired:
return "402";
case (int)HttpStatusCode.Forbidden:
return "403";
case (int)HttpStatusCode.NotFound:
return "404";
case (int)HttpStatusCode.MethodNotAllowed:
return "405";
case (int)HttpStatusCode.NotAcceptable:
return "406";
case (int)HttpStatusCode.ProxyAuthenticationRequired:
return "407";
case (int)HttpStatusCode.RequestTimeout:
return "408";
case (int)HttpStatusCode.Conflict:
return "409";
case (int)HttpStatusCode.Gone:
return "410";
case (int)HttpStatusCode.LengthRequired:
return "411";
case (int)HttpStatusCode.PreconditionFailed:
return "412";
case (int)HttpStatusCode.RequestEntityTooLarge:
return "413";
case (int)HttpStatusCode.RequestUriTooLong:
return "414";
case (int)HttpStatusCode.UnsupportedMediaType:
return "415";
case (int)HttpStatusCode.RequestedRangeNotSatisfiable:
return "416";
case (int)HttpStatusCode.ExpectationFailed:
return "417";
case (int)418:
return "418";
case (int)419:
return "419";
case (int)HttpStatusCode.MisdirectedRequest:
return "421";
case (int)HttpStatusCode.UnprocessableEntity:
return "422";
case (int)HttpStatusCode.Locked:
return "423";
case (int)HttpStatusCode.FailedDependency:
return "424";
case (int)HttpStatusCode.UpgradeRequired:
return "426";
case (int)HttpStatusCode.PreconditionRequired:
return "428";
case (int)HttpStatusCode.TooManyRequests:
return "429";
case (int)HttpStatusCode.RequestHeaderFieldsTooLarge:
return "431";
case (int)HttpStatusCode.UnavailableForLegalReasons:
return "451";
case (int)HttpStatusCode.InternalServerError:
return "500";
case (int)HttpStatusCode.NotImplemented:
return "501";
case (int)HttpStatusCode.BadGateway:
return "502";
case (int)HttpStatusCode.ServiceUnavailable:
return "503";
case (int)HttpStatusCode.GatewayTimeout:
return "504";
case (int)HttpStatusCode.HttpVersionNotSupported:
return "505";
case (int)HttpStatusCode.VariantAlsoNegotiates:
return "506";
case (int)HttpStatusCode.InsufficientStorage:
return "507";
case (int)HttpStatusCode.LoopDetected:
return "508";
case (int)HttpStatusCode.NotExtended:
return "510";
case (int)HttpStatusCode.NetworkAuthenticationRequired:
return "511";
default:
return statusCode.ToString(CultureInfo.InvariantCulture);
}
}
}
}

View File

@ -8,7 +8,7 @@ using System.Diagnostics;
namespace System.Net.Http.HPack namespace System.Net.Http.HPack
{ {
internal static class HPackEncoder internal partial class HPackEncoder
{ {
// Things we should add: // Things we should add:
// * Huffman encoding // * Huffman encoding
@ -109,6 +109,70 @@ namespace System.Net.Http.HPack
return false; return false;
} }
/// <summary>Encodes a "Literal Header Field never Indexing".</summary>
public static bool EncodeLiteralHeaderFieldNeverIndexing(int index, string value, Span<byte> destination, out int bytesWritten)
{
// From https://tools.ietf.org/html/rfc7541#section-6.2.3
// ------------------------------------------------------
// 0 1 2 3 4 5 6 7
// +---+---+---+---+---+---+---+---+
// | 0 | 0 | 0 | 1 | Index (4+) |
// +---+---+-----------------------+
// | H | Value Length (7+) |
// +---+---------------------------+
// | Value String (Length octets) |
// +-------------------------------+
if ((uint)destination.Length >= 2)
{
destination[0] = 0x10;
if (IntegerEncoder.Encode(index, 4, destination, out int indexLength))
{
Debug.Assert(indexLength >= 1);
if (EncodeStringLiteral(value, destination.Slice(indexLength), out int nameLength))
{
bytesWritten = indexLength + nameLength;
return true;
}
}
}
bytesWritten = 0;
return false;
}
/// <summary>Encodes a "Literal Header Field with Indexing".</summary>
public static bool EncodeLiteralHeaderFieldIndexing(int index, string value, Span<byte> destination, out int bytesWritten)
{
// From https://tools.ietf.org/html/rfc7541#section-6.2.2
// ------------------------------------------------------
// 0 1 2 3 4 5 6 7
// +---+---+---+---+---+---+---+---+
// | 0 | 1 | Index (6+) |
// +---+---+-----------------------+
// | H | Value Length (7+) |
// +---+---------------------------+
// | Value String (Length octets) |
// +-------------------------------+
if ((uint)destination.Length >= 2)
{
destination[0] = 0x40;
if (IntegerEncoder.Encode(index, 6, destination, out int indexLength))
{
Debug.Assert(indexLength >= 1);
if (EncodeStringLiteral(value, destination.Slice(indexLength), out int nameLength))
{
bytesWritten = indexLength + nameLength;
return true;
}
}
}
bytesWritten = 0;
return false;
}
/// <summary> /// <summary>
/// Encodes a "Literal Header Field without Indexing", but only the index portion; /// Encodes a "Literal Header Field without Indexing", but only the index portion;
/// a subsequent call to <c>EncodeStringLiteral</c> must be used to encode the associated value. /// a subsequent call to <c>EncodeStringLiteral</c> must be used to encode the associated value.
@ -144,6 +208,27 @@ namespace System.Net.Http.HPack
return false; return false;
} }
/// <summary>Encodes a "Literal Header Field with Indexing - New Name".</summary>
public static bool EncodeLiteralHeaderFieldIndexingNewName(string name, string value, Span<byte> destination, out int bytesWritten)
{
// From https://tools.ietf.org/html/rfc7541#section-6.2.2
// ------------------------------------------------------
// 0 1 2 3 4 5 6 7
// +---+---+---+---+---+---+---+---+
// | 0 | 1 | 0 |
// +---+---+-----------------------+
// | H | Name Length (7+) |
// +---+---------------------------+
// | Name String (Length octets) |
// +---+---------------------------+
// | H | Value Length (7+) |
// +---+---------------------------+
// | Value String (Length octets) |
// +-------------------------------+
return EncodeLiteralHeaderNewNameCore(0x40, name, value, destination, out bytesWritten);
}
/// <summary>Encodes a "Literal Header Field without Indexing - New Name".</summary> /// <summary>Encodes a "Literal Header Field without Indexing - New Name".</summary>
public static bool EncodeLiteralHeaderFieldWithoutIndexingNewName(string name, string value, Span<byte> destination, out int bytesWritten) public static bool EncodeLiteralHeaderFieldWithoutIndexingNewName(string name, string value, Span<byte> destination, out int bytesWritten)
{ {
@ -162,9 +247,35 @@ namespace System.Net.Http.HPack
// | Value String (Length octets) | // | Value String (Length octets) |
// +-------------------------------+ // +-------------------------------+
return EncodeLiteralHeaderNewNameCore(0, name, value, destination, out bytesWritten);
}
/// <summary>Encodes a "Literal Header Field never Indexing - New Name".</summary>
public static bool EncodeLiteralHeaderFieldNeverIndexingNewName(string name, string value, Span<byte> destination, out int bytesWritten)
{
// From https://tools.ietf.org/html/rfc7541#section-6.2.3
// ------------------------------------------------------
// 0 1 2 3 4 5 6 7
// +---+---+---+---+---+---+---+---+
// | 0 | 0 | 0 | 1 | 0 |
// +---+---+-----------------------+
// | H | Name Length (7+) |
// +---+---------------------------+
// | Name String (Length octets) |
// +---+---------------------------+
// | H | Value Length (7+) |
// +---+---------------------------+
// | Value String (Length octets) |
// +-------------------------------+
return EncodeLiteralHeaderNewNameCore(0x10, name, value, destination, out bytesWritten);
}
private static bool EncodeLiteralHeaderNewNameCore(byte mask, string name, string value, Span<byte> destination, out int bytesWritten)
{
if ((uint)destination.Length >= 3) if ((uint)destination.Length >= 3)
{ {
destination[0] = 0; destination[0] = mask;
if (EncodeLiteralHeaderName(name, destination.Slice(1), out int nameLength) && if (EncodeLiteralHeaderName(name, destination.Slice(1), out int nameLength) &&
EncodeStringLiteral(value, destination.Slice(1 + nameLength), out int valueLength)) EncodeStringLiteral(value, destination.Slice(1 + nameLength), out int valueLength))
{ {
@ -372,6 +483,25 @@ namespace System.Net.Http.HPack
return false; return false;
} }
public static bool EncodeDynamicTableSizeUpdate(int value, Span<byte> destination, out int bytesWritten)
{
// From https://tools.ietf.org/html/rfc7541#section-6.3
// ----------------------------------------------------
// 0 1 2 3 4 5 6 7
// +---+---+---+---+---+---+---+---+
// | 0 | 0 | 1 | Max size (5+) |
// +---+---------------------------+
if (destination.Length != 0)
{
destination[0] = 0x20;
return IntegerEncoder.Encode(value, 5, destination, out bytesWritten);
}
bytesWritten = 0;
return false;
}
public static bool EncodeStringLiterals(ReadOnlySpan<string> values, string? separator, Span<byte> destination, out int bytesWritten) public static bool EncodeStringLiterals(ReadOnlySpan<string> values, string? separator, Span<byte> destination, out int bytesWritten)
{ {
bytesWritten = 0; bytesWritten = 0;

View File

@ -7,7 +7,7 @@ using System.Text;
namespace System.Net.Http.HPack namespace System.Net.Http.HPack
{ {
internal static class StatusCodes internal static partial class StatusCodes
{ {
// This uses C# compiler's ability to refer to static data directly. For more information see https://vcsjones.dev/2019/02/01/csharp-readonly-span-bytes-static // This uses C# compiler's ability to refer to static data directly. For more information see https://vcsjones.dev/2019/02/01/csharp-readonly-span-bytes-static