parent
0fd9f27d60
commit
aca5f04922
|
|
@ -36,7 +36,8 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
IHttpMaxRequestBodySizeFeature,
|
IHttpMaxRequestBodySizeFeature,
|
||||||
IHttpBodyControlFeature,
|
IHttpBodyControlFeature,
|
||||||
IHttpSysRequestInfoFeature,
|
IHttpSysRequestInfoFeature,
|
||||||
IHttpResponseTrailersFeature
|
IHttpResponseTrailersFeature,
|
||||||
|
IHttpResetFeature
|
||||||
{
|
{
|
||||||
private RequestContext _requestContext;
|
private RequestContext _requestContext;
|
||||||
private IFeatureCollection _features;
|
private IFeatureCollection _features;
|
||||||
|
|
@ -350,7 +351,16 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
|
|
||||||
internal IHttpResponseTrailersFeature GetResponseTrailersFeature()
|
internal IHttpResponseTrailersFeature GetResponseTrailersFeature()
|
||||||
{
|
{
|
||||||
if (Request.ProtocolVersion >= HttpVersion.Version20 && ComNetOS.SupportsTrailers)
|
if (Request.ProtocolVersion >= HttpVersion.Version20 && HttpApi.SupportsTrailers)
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal IHttpResetFeature GetResetFeature()
|
||||||
|
{
|
||||||
|
if (Request.ProtocolVersion >= HttpVersion.Version20 && HttpApi.SupportsReset)
|
||||||
{
|
{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
@ -455,6 +465,12 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
|
|
||||||
Task IHttpResponseBodyFeature.CompleteAsync() => CompleteAsync();
|
Task IHttpResponseBodyFeature.CompleteAsync() => CompleteAsync();
|
||||||
|
|
||||||
|
void IHttpResetFeature.Reset(int errorCode)
|
||||||
|
{
|
||||||
|
_requestContext.SetResetCode(errorCode);
|
||||||
|
_requestContext.Abort();
|
||||||
|
}
|
||||||
|
|
||||||
internal async Task CompleteAsync()
|
internal async Task CompleteAsync()
|
||||||
{
|
{
|
||||||
if (!_responseStarted)
|
if (!_responseStarted)
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,9 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
_application.DisposeContext(context, ex);
|
_application.DisposeContext(context, ex);
|
||||||
if (requestContext.Response.HasStarted)
|
if (requestContext.Response.HasStarted)
|
||||||
{
|
{
|
||||||
|
// HTTP/2 INTERNAL_ERROR = 0x2 https://tools.ietf.org/html/rfc7540#section-7
|
||||||
|
// Otherwise the default is Cancel = 0x8.
|
||||||
|
requestContext.SetResetCode(2);
|
||||||
requestContext.Abort();
|
requestContext.Abort();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,11 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
// Minimum support for Windows 7 is assumed.
|
// Minimum support for Windows 7 is assumed.
|
||||||
internal static readonly bool IsWin8orLater;
|
internal static readonly bool IsWin8orLater;
|
||||||
|
|
||||||
internal static readonly bool SupportsTrailers;
|
|
||||||
|
|
||||||
static ComNetOS()
|
static ComNetOS()
|
||||||
{
|
{
|
||||||
var win8Version = new Version(6, 2);
|
var win8Version = new Version(6, 2);
|
||||||
|
|
||||||
IsWin8orLater = (Environment.OSVersion.Version >= win8Version);
|
IsWin8orLater = (Environment.OSVersion.Version >= win8Version);
|
||||||
|
|
||||||
SupportsTrailers = Environment.OSVersion.Version >= new Version(10, 0, 19505);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
[DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
|
[DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
|
||||||
internal static extern unsafe uint HttpCloseRequestQueue(IntPtr pReqQueueHandle);
|
internal static extern unsafe uint HttpCloseRequestQueue(IntPtr pReqQueueHandle);
|
||||||
|
|
||||||
|
internal delegate uint HttpSetRequestPropertyInvoker(SafeHandle requestQueueHandle, ulong requestId, HTTP_REQUEST_PROPERTY propertyId, void* input, uint inputSize, IntPtr overlapped);
|
||||||
|
|
||||||
private static HTTPAPI_VERSION version;
|
private static HTTPAPI_VERSION version;
|
||||||
|
|
||||||
|
|
@ -106,6 +107,11 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static SafeLibraryHandle HttpApiModule { get; private set; }
|
||||||
|
internal static HttpSetRequestPropertyInvoker HttpSetRequestProperty { get; private set; }
|
||||||
|
internal static bool SupportsTrailers { get; private set; }
|
||||||
|
internal static bool SupportsReset { get; private set; }
|
||||||
|
|
||||||
static HttpApi()
|
static HttpApi()
|
||||||
{
|
{
|
||||||
InitHttpApi(2, 0);
|
InitHttpApi(2, 0);
|
||||||
|
|
@ -119,6 +125,16 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
var statusCode = HttpInitialize(version, (uint)HTTP_FLAGS.HTTP_INITIALIZE_SERVER, null);
|
var statusCode = HttpInitialize(version, (uint)HTTP_FLAGS.HTTP_INITIALIZE_SERVER, null);
|
||||||
|
|
||||||
supported = statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS;
|
supported = statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if (supported)
|
||||||
|
{
|
||||||
|
HttpApiModule = SafeLibraryHandle.Open(HTTPAPI);
|
||||||
|
HttpSetRequestProperty = HttpApiModule.GetProcAddress<HttpSetRequestPropertyInvoker>("HttpSetRequestProperty", throwIfNotFound: false);
|
||||||
|
|
||||||
|
SupportsReset = HttpSetRequestProperty != null;
|
||||||
|
// Trailers support was added in the same release as Reset, but there's no method we can export to check it directly.
|
||||||
|
SupportsTrailers = SupportsReset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static volatile bool supported;
|
private static volatile bool supported;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
// 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.Runtime.ConstrainedExecution;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security;
|
||||||
|
using Microsoft.Win32.SafeHandles;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a handle to a Windows module (DLL).
|
||||||
|
/// </summary>
|
||||||
|
internal unsafe sealed class SafeLibraryHandle : SafeHandleZeroOrMinusOneIsInvalid
|
||||||
|
{
|
||||||
|
// Called by P/Invoke when returning SafeHandles
|
||||||
|
private SafeLibraryHandle()
|
||||||
|
: base(ownsHandle: true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a value stating whether the library exports a given proc.
|
||||||
|
/// </summary>
|
||||||
|
public bool DoesProcExist(string lpProcName)
|
||||||
|
{
|
||||||
|
IntPtr pfnProc = UnsafeNativeMethods.GetProcAddress(this, lpProcName);
|
||||||
|
return (pfnProc != IntPtr.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a delegate pointing to a given export from this library.
|
||||||
|
/// </summary>
|
||||||
|
public TDelegate GetProcAddress<TDelegate>(string lpProcName, bool throwIfNotFound = true) where TDelegate : class
|
||||||
|
{
|
||||||
|
IntPtr pfnProc = UnsafeNativeMethods.GetProcAddress(this, lpProcName);
|
||||||
|
if (pfnProc == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
if (throwIfNotFound)
|
||||||
|
{
|
||||||
|
UnsafeNativeMethods.ThrowExceptionForLastWin32Error();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Marshal.GetDelegateForFunctionPointer<TDelegate>(pfnProc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens a library. If 'filename' is not a fully-qualified path, the default search path is used.
|
||||||
|
/// </summary>
|
||||||
|
public static SafeLibraryHandle Open(string filename)
|
||||||
|
{
|
||||||
|
const uint LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800U; // from libloaderapi.h
|
||||||
|
|
||||||
|
SafeLibraryHandle handle = UnsafeNativeMethods.LoadLibraryEx(filename, IntPtr.Zero, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||||
|
if (handle == null || handle.IsInvalid)
|
||||||
|
{
|
||||||
|
UnsafeNativeMethods.ThrowExceptionForLastWin32Error();
|
||||||
|
}
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you.
|
||||||
|
protected override bool ReleaseHandle()
|
||||||
|
{
|
||||||
|
return UnsafeNativeMethods.FreeLibrary(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressUnmanagedCodeSecurity]
|
||||||
|
private static class UnsafeNativeMethods
|
||||||
|
{
|
||||||
|
// http://msdn.microsoft.com/en-us/library/ms683152(v=vs.85).aspx
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||||
|
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode)]
|
||||||
|
internal static extern bool FreeLibrary(IntPtr hModule);
|
||||||
|
|
||||||
|
// http://msdn.microsoft.com/en-us/library/ms683212(v=vs.85).aspx
|
||||||
|
[DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
|
||||||
|
internal static extern IntPtr GetProcAddress(
|
||||||
|
[In] SafeLibraryHandle hModule,
|
||||||
|
[In, MarshalAs(UnmanagedType.LPStr)] string lpProcName);
|
||||||
|
|
||||||
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx
|
||||||
|
[DllImport("kernel32.dll", EntryPoint = "LoadLibraryExW", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
|
||||||
|
internal static extern SafeLibraryHandle LoadLibraryEx(
|
||||||
|
[In, MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
|
||||||
|
[In] IntPtr hFile,
|
||||||
|
[In] uint dwFlags);
|
||||||
|
|
||||||
|
internal static void ThrowExceptionForLastWin32Error()
|
||||||
|
{
|
||||||
|
int hr = Marshal.GetHRForLastWin32Error();
|
||||||
|
Marshal.ThrowExceptionForHR(hr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Security.Authentication.ExtendedProtection;
|
using System.Security.Authentication.ExtendedProtection;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
|
|
@ -218,5 +219,25 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
// RequestQueueHandle may have been closed
|
// RequestQueueHandle may have been closed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// You must still call ForceCancelRequest after this.
|
||||||
|
internal unsafe void SetResetCode(int errorCode)
|
||||||
|
{
|
||||||
|
if (!HttpApi.SupportsReset)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var streamError = new HttpApiTypes.HTTP_REQUEST_PROPERTY_STREAM_ERROR() { ErrorCode = (uint)errorCode };
|
||||||
|
var statusCode = HttpApi.HttpSetRequestProperty(Server.RequestQueue.Handle, Request.RequestId, HttpApiTypes.HTTP_REQUEST_PROPERTY.HttpRequestPropertyStreamError, (void*)&streamError,
|
||||||
|
(uint)sizeof(HttpApiTypes.HTTP_REQUEST_PROPERTY_STREAM_ERROR), IntPtr.Zero);
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
// RequestQueueHandle may have been closed
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
// Trailers are supported on this OS, it's HTTP/2, and the app added a Trailer response header to announce trailers were intended.
|
// Trailers are supported on this OS, it's HTTP/2, and the app added a Trailer response header to announce trailers were intended.
|
||||||
// Needed to delay the completion of Content-Length responses.
|
// Needed to delay the completion of Content-Length responses.
|
||||||
internal bool TrailersExpected => HasTrailers
|
internal bool TrailersExpected => HasTrailers
|
||||||
|| (ComNetOS.SupportsTrailers && Request.ProtocolVersion >= HttpVersion.Version20
|
|| (HttpApi.SupportsTrailers && Request.ProtocolVersion >= HttpVersion.Version20
|
||||||
&& Headers.ContainsKey(HttpKnownHeaderNames.Trailer));
|
&& Headers.ContainsKey(HttpKnownHeaderNames.Trailer));
|
||||||
|
|
||||||
internal long ExpectedBodyLength
|
internal long ExpectedBodyLength
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
{ typeof(IHttpBodyControlFeature), _identityFunc },
|
{ typeof(IHttpBodyControlFeature), _identityFunc },
|
||||||
{ typeof(IHttpSysRequestInfoFeature), _identityFunc },
|
{ typeof(IHttpSysRequestInfoFeature), _identityFunc },
|
||||||
{ typeof(IHttpResponseTrailersFeature), ctx => ctx.GetResponseTrailersFeature() },
|
{ typeof(IHttpResponseTrailersFeature), ctx => ctx.GetResponseTrailersFeature() },
|
||||||
|
{ typeof(IHttpResetFeature), ctx => ctx.GetResetFeature() },
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly FeatureContext _featureContext;
|
private readonly FeatureContext _featureContext;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
// 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.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Http2Cat;
|
using Microsoft.AspNetCore.Http2Cat;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
|
||||||
using Microsoft.AspNetCore.Testing;
|
using Microsoft.AspNetCore.Testing;
|
||||||
|
|
@ -16,7 +22,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.FunctionalTests
|
||||||
{
|
{
|
||||||
[ConditionalFact(Skip = "https://github.com/aspnet/AspNetCore/issues/17420")]
|
[ConditionalFact(Skip = "https://github.com/aspnet/AspNetCore/issues/17420")]
|
||||||
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10, SkipReason = "Http2 requires Win10")]
|
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10, SkipReason = "Http2 requires Win10")]
|
||||||
[MaximumOSVersion(OperatingSystems.Windows, "10.0.18362.9999", SkipReason = "This is last version without GoAway support")]
|
[MaximumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_19H1, SkipReason = "This is last version without GoAway support")]
|
||||||
public async Task ConnectionClose_NoOSSupport_NoGoAway()
|
public async Task ConnectionClose_NoOSSupport_NoGoAway()
|
||||||
{
|
{
|
||||||
using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
||||||
|
|
@ -34,38 +40,23 @@ namespace Microsoft.AspNetCore.Server.HttpSys.FunctionalTests
|
||||||
|
|
||||||
await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
|
await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
|
||||||
|
|
||||||
var headersFrame = await h2Connection.ReceiveFrameAsync();
|
await h2Connection.ReceiveHeadersAsync(1, endStream: true, decodedHeaders =>
|
||||||
|
{
|
||||||
Assert.Equal(Http2FrameType.HEADERS, headersFrame.Type);
|
// HTTP/2 filters out the connection header
|
||||||
Assert.True((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_HEADERS) != 0);
|
Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
|
||||||
Assert.True((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_STREAM) != 0);
|
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
||||||
|
});
|
||||||
h2Connection.Logger.LogInformation("Received headers in a single frame.");
|
|
||||||
|
|
||||||
var decodedHeaders = h2Connection.DecodeHeaders(headersFrame);
|
|
||||||
|
|
||||||
// HTTP/2 filters out the connection header
|
|
||||||
Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
|
|
||||||
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
|
||||||
|
|
||||||
// Send and receive a second request to ensure there is no GoAway frame on the wire yet.
|
// Send and receive a second request to ensure there is no GoAway frame on the wire yet.
|
||||||
|
|
||||||
await h2Connection.StartStreamAsync(3, Http2Utilities.BrowserRequestHeaders, endStream: true);
|
await h2Connection.StartStreamAsync(3, Http2Utilities.BrowserRequestHeaders, endStream: true);
|
||||||
|
|
||||||
headersFrame = await h2Connection.ReceiveFrameAsync();
|
await h2Connection.ReceiveHeadersAsync(3, endStream: true, decodedHeaders =>
|
||||||
|
{
|
||||||
Assert.Equal(Http2FrameType.HEADERS, headersFrame.Type);
|
// HTTP/2 filters out the connection header
|
||||||
Assert.True((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_HEADERS) != 0);
|
Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
|
||||||
Assert.True((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_STREAM) != 0);
|
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
||||||
|
});
|
||||||
h2Connection.Logger.LogInformation("Received headers in a single frame.");
|
|
||||||
|
|
||||||
h2Connection.ResetHeaders();
|
|
||||||
decodedHeaders = h2Connection.DecodeHeaders(headersFrame);
|
|
||||||
|
|
||||||
// HTTP/2 filters out the connection header
|
|
||||||
Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
|
|
||||||
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
|
||||||
|
|
||||||
await h2Connection.StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
await h2Connection.StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
|
||||||
|
|
||||||
|
|
@ -96,23 +87,15 @@ namespace Microsoft.AspNetCore.Server.HttpSys.FunctionalTests
|
||||||
var goAwayFrame = await h2Connection.ReceiveFrameAsync();
|
var goAwayFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
h2Connection.VerifyGoAway(goAwayFrame, int.MaxValue, Http2ErrorCode.NO_ERROR);
|
h2Connection.VerifyGoAway(goAwayFrame, int.MaxValue, Http2ErrorCode.NO_ERROR);
|
||||||
|
|
||||||
var headersFrame = await h2Connection.ReceiveFrameAsync();
|
await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
|
||||||
|
{
|
||||||
Assert.Equal(Http2FrameType.HEADERS, headersFrame.Type);
|
// HTTP/2 filters out the connection header
|
||||||
Assert.Equal(Http2HeadersFrameFlags.END_HEADERS, headersFrame.HeadersFlags);
|
Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
|
||||||
|
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
||||||
h2Connection.Logger.LogInformation("Received headers in a single frame.");
|
});
|
||||||
|
|
||||||
var decodedHeaders = h2Connection.DecodeHeaders(headersFrame);
|
|
||||||
|
|
||||||
// HTTP/2 filters out the connection header
|
|
||||||
Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
|
|
||||||
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
|
||||||
|
|
||||||
var dataFrame = await h2Connection.ReceiveFrameAsync();
|
var dataFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
Assert.Equal(Http2FrameType.DATA, dataFrame.Type);
|
Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);
|
||||||
Assert.Equal(Http2DataFrameFlags.END_STREAM, dataFrame.DataFlags);
|
|
||||||
Assert.Equal(0, dataFrame.PayloadLength);
|
|
||||||
|
|
||||||
// Http.Sys doesn't send a final GoAway unless we ignore the first one and send 200 additional streams.
|
// Http.Sys doesn't send a final GoAway unless we ignore the first one and send 200 additional streams.
|
||||||
|
|
||||||
|
|
@ -144,26 +127,15 @@ namespace Microsoft.AspNetCore.Server.HttpSys.FunctionalTests
|
||||||
var goAwayFrame = await h2Connection.ReceiveFrameAsync();
|
var goAwayFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
h2Connection.VerifyGoAway(goAwayFrame, int.MaxValue, Http2ErrorCode.NO_ERROR);
|
h2Connection.VerifyGoAway(goAwayFrame, int.MaxValue, Http2ErrorCode.NO_ERROR);
|
||||||
|
|
||||||
var headersFrame = await h2Connection.ReceiveFrameAsync();
|
await h2Connection.ReceiveHeadersAsync(streamId, decodedHeaders =>
|
||||||
|
{
|
||||||
Assert.Equal(Http2FrameType.HEADERS, headersFrame.Type);
|
// HTTP/2 filters out the connection header
|
||||||
Assert.Equal(Http2HeadersFrameFlags.END_HEADERS, headersFrame.HeadersFlags);
|
Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
|
||||||
Assert.Equal(streamId, headersFrame.StreamId);
|
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
||||||
|
});
|
||||||
h2Connection.Logger.LogInformation("Received headers in a single frame.");
|
|
||||||
|
|
||||||
var decodedHeaders = h2Connection.DecodeHeaders(headersFrame);
|
|
||||||
|
|
||||||
// HTTP/2 filters out the connection header
|
|
||||||
Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
|
|
||||||
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
|
||||||
h2Connection.ResetHeaders();
|
|
||||||
|
|
||||||
var dataFrame = await h2Connection.ReceiveFrameAsync();
|
var dataFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
Assert.Equal(Http2FrameType.DATA, dataFrame.Type);
|
Http2Utilities.VerifyDataFrame(dataFrame, streamId, endOfStream: true, length: 0);
|
||||||
Assert.Equal(Http2DataFrameFlags.END_STREAM, dataFrame.DataFlags);
|
|
||||||
Assert.Equal(0, dataFrame.PayloadLength);
|
|
||||||
Assert.Equal(streamId, dataFrame.StreamId);
|
|
||||||
|
|
||||||
// Http.Sys doesn't send a final GoAway unless we ignore the first one and send 200 additional streams.
|
// Http.Sys doesn't send a final GoAway unless we ignore the first one and send 200 additional streams.
|
||||||
|
|
||||||
|
|
@ -172,26 +144,15 @@ namespace Microsoft.AspNetCore.Server.HttpSys.FunctionalTests
|
||||||
streamId = 1 + (i * 2); // Odds.
|
streamId = 1 + (i * 2); // Odds.
|
||||||
await h2Connection.StartStreamAsync(streamId, Http2Utilities.BrowserRequestHeaders, endStream: true);
|
await h2Connection.StartStreamAsync(streamId, Http2Utilities.BrowserRequestHeaders, endStream: true);
|
||||||
|
|
||||||
headersFrame = await h2Connection.ReceiveFrameAsync();
|
await h2Connection.ReceiveHeadersAsync(streamId, decodedHeaders =>
|
||||||
|
{
|
||||||
Assert.Equal(Http2FrameType.HEADERS, headersFrame.Type);
|
// HTTP/2 filters out the connection header
|
||||||
Assert.Equal(Http2HeadersFrameFlags.END_HEADERS, headersFrame.HeadersFlags);
|
Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
|
||||||
Assert.Equal(streamId, headersFrame.StreamId);
|
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
||||||
|
});
|
||||||
h2Connection.Logger.LogInformation("Received headers in a single frame.");
|
|
||||||
|
|
||||||
decodedHeaders = h2Connection.DecodeHeaders(headersFrame);
|
|
||||||
|
|
||||||
// HTTP/2 filters out the connection header
|
|
||||||
Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
|
|
||||||
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
|
||||||
h2Connection.ResetHeaders();
|
|
||||||
|
|
||||||
dataFrame = await h2Connection.ReceiveFrameAsync();
|
dataFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
Assert.Equal(Http2FrameType.DATA, dataFrame.Type);
|
Http2Utilities.VerifyDataFrame(dataFrame, streamId, endOfStream: true, length: 0);
|
||||||
Assert.Equal(Http2DataFrameFlags.END_STREAM, dataFrame.DataFlags);
|
|
||||||
Assert.Equal(0, dataFrame.PayloadLength);
|
|
||||||
Assert.Equal(streamId, dataFrame.StreamId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
streamId = 1 + (200 * 2); // Odds.
|
streamId = 1 + (200 * 2); // Odds.
|
||||||
|
|
@ -202,26 +163,495 @@ namespace Microsoft.AspNetCore.Server.HttpSys.FunctionalTests
|
||||||
h2Connection.VerifyGoAway(goAwayFrame, streamId, Http2ErrorCode.NO_ERROR);
|
h2Connection.VerifyGoAway(goAwayFrame, streamId, Http2ErrorCode.NO_ERROR);
|
||||||
|
|
||||||
// Normal response
|
// Normal response
|
||||||
headersFrame = await h2Connection.ReceiveFrameAsync();
|
await h2Connection.ReceiveHeadersAsync(streamId, decodedHeaders =>
|
||||||
|
{
|
||||||
Assert.Equal(Http2FrameType.HEADERS, headersFrame.Type);
|
// HTTP/2 filters out the connection header
|
||||||
Assert.Equal(Http2HeadersFrameFlags.END_HEADERS, headersFrame.HeadersFlags);
|
Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
|
||||||
Assert.Equal(streamId, headersFrame.StreamId);
|
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
||||||
|
});
|
||||||
h2Connection.Logger.LogInformation("Received headers in a single frame.");
|
|
||||||
|
|
||||||
decodedHeaders = h2Connection.DecodeHeaders(headersFrame);
|
|
||||||
|
|
||||||
// HTTP/2 filters out the connection header
|
|
||||||
Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
|
|
||||||
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
|
||||||
h2Connection.ResetHeaders();
|
|
||||||
|
|
||||||
dataFrame = await h2Connection.ReceiveFrameAsync();
|
dataFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
Assert.Equal(Http2FrameType.DATA, dataFrame.Type);
|
Http2Utilities.VerifyDataFrame(dataFrame, streamId, endOfStream: true, length: 0);
|
||||||
Assert.Equal(Http2DataFrameFlags.END_STREAM, dataFrame.DataFlags);
|
|
||||||
Assert.Equal(0, dataFrame.PayloadLength);
|
h2Connection.Logger.LogInformation("Connection stopped.");
|
||||||
Assert.Equal(streamId, dataFrame.StreamId);
|
})
|
||||||
|
.Build().RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10, SkipReason = "Http2 requires Win10")]
|
||||||
|
public async Task AppException_BeforeHeaders_500()
|
||||||
|
{
|
||||||
|
using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
||||||
|
{
|
||||||
|
throw new Exception("Application exception");
|
||||||
|
});
|
||||||
|
|
||||||
|
await new HostBuilder()
|
||||||
|
.UseHttp2Cat(address, async h2Connection =>
|
||||||
|
{
|
||||||
|
await h2Connection.InitializeConnectionAsync();
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
|
||||||
|
|
||||||
|
await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
|
||||||
|
|
||||||
|
await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
|
||||||
|
{
|
||||||
|
Assert.Equal("500", decodedHeaders[HeaderNames.Status]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var dataFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
|
Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Connection stopped.");
|
||||||
|
})
|
||||||
|
.Build().RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10, SkipReason = "Http2 requires Win10")]
|
||||||
|
[MaximumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_20H1, SkipReason = "This is last version without custom Reset support")]
|
||||||
|
public async Task AppException_AfterHeaders_PriorOSVersions_ResetCancel()
|
||||||
|
{
|
||||||
|
using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
|
||||||
|
{
|
||||||
|
await httpContext.Response.Body.FlushAsync();
|
||||||
|
throw new Exception("Application exception");
|
||||||
|
});
|
||||||
|
|
||||||
|
await new HostBuilder()
|
||||||
|
.UseHttp2Cat(address, async h2Connection =>
|
||||||
|
{
|
||||||
|
await h2Connection.InitializeConnectionAsync();
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
|
||||||
|
|
||||||
|
await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
|
||||||
|
|
||||||
|
await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
|
||||||
|
{
|
||||||
|
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var resetFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
|
Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, Http2ErrorCode.CANCEL);
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Connection stopped.");
|
||||||
|
})
|
||||||
|
.Build().RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Custom Reset support was added in Win10_20H2.")]
|
||||||
|
public async Task AppException_AfterHeaders_ResetInternalError()
|
||||||
|
{
|
||||||
|
using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
|
||||||
|
{
|
||||||
|
await httpContext.Response.Body.FlushAsync();
|
||||||
|
throw new Exception("Application exception");
|
||||||
|
});
|
||||||
|
|
||||||
|
await new HostBuilder()
|
||||||
|
.UseHttp2Cat(address, async h2Connection =>
|
||||||
|
{
|
||||||
|
await h2Connection.InitializeConnectionAsync();
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
|
||||||
|
|
||||||
|
await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
|
||||||
|
|
||||||
|
await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
|
||||||
|
{
|
||||||
|
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var resetFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
|
Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, Http2ErrorCode.INTERNAL_ERROR);
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Connection stopped.");
|
||||||
|
})
|
||||||
|
.Build().RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task Reset_Http1_NotSupported()
|
||||||
|
{
|
||||||
|
using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
||||||
|
{
|
||||||
|
Assert.Equal("HTTP/1.1", httpContext.Request.Protocol);
|
||||||
|
var feature = httpContext.Features.Get<IHttpResetFeature>();
|
||||||
|
Assert.Null(feature);
|
||||||
|
return httpContext.Response.WriteAsync("Hello World");
|
||||||
|
});
|
||||||
|
|
||||||
|
var handler = new HttpClientHandler();
|
||||||
|
handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
|
||||||
|
using HttpClient client = new HttpClient(handler);
|
||||||
|
client.DefaultRequestVersion = HttpVersion.Version11;
|
||||||
|
var response = await client.GetStringAsync(address);
|
||||||
|
Assert.Equal("Hello World", response);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10, SkipReason = "Http2 requires Win10")]
|
||||||
|
[MaximumOSVersion(OperatingSystems.Windows, WindowsVersions.Win10_20H1, SkipReason = "This is last version without Reset support")]
|
||||||
|
public async Task Reset_PriorOSVersions_NotSupported()
|
||||||
|
{
|
||||||
|
using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
||||||
|
{
|
||||||
|
Assert.Equal("HTTP/2", httpContext.Request.Protocol);
|
||||||
|
var feature = httpContext.Features.Get<IHttpResetFeature>();
|
||||||
|
Assert.Null(feature);
|
||||||
|
return httpContext.Response.WriteAsync("Hello World");
|
||||||
|
});
|
||||||
|
|
||||||
|
var handler = new HttpClientHandler();
|
||||||
|
handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
|
||||||
|
using HttpClient client = new HttpClient(handler);
|
||||||
|
client.DefaultRequestVersion = HttpVersion.Version20;
|
||||||
|
var response = await client.GetStringAsync(address);
|
||||||
|
Assert.Equal("Hello World", response);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Reset support was added in Win10_20H2.")]
|
||||||
|
public async Task Reset_BeforeResponse_Resets()
|
||||||
|
{
|
||||||
|
var appResult = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
using var server = Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assert.Equal("HTTP/2", httpContext.Request.Protocol);
|
||||||
|
var feature = httpContext.Features.Get<IHttpResetFeature>();
|
||||||
|
Assert.NotNull(feature);
|
||||||
|
feature.Reset(1111); // Custom
|
||||||
|
appResult.SetResult(0);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
appResult.SetException(ex);
|
||||||
|
}
|
||||||
|
return Task.FromResult(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
await new HostBuilder()
|
||||||
|
.UseHttp2Cat(address, async h2Connection =>
|
||||||
|
{
|
||||||
|
await h2Connection.InitializeConnectionAsync();
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
|
||||||
|
|
||||||
|
await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
|
||||||
|
|
||||||
|
var resetFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
|
Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111);
|
||||||
|
|
||||||
|
// Any app errors?
|
||||||
|
Assert.Equal(0, await appResult.Task.DefaultTimeout());
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Connection stopped.");
|
||||||
|
})
|
||||||
|
.Build().RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Reset support was added in Win10_20H2.")]
|
||||||
|
public async Task Reset_AfterResponseHeaders_Resets()
|
||||||
|
{
|
||||||
|
var appResult = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assert.Equal("HTTP/2", httpContext.Request.Protocol);
|
||||||
|
var feature = httpContext.Features.Get<IHttpResetFeature>();
|
||||||
|
Assert.NotNull(feature);
|
||||||
|
await httpContext.Response.Body.FlushAsync();
|
||||||
|
feature.Reset(1111); // Custom
|
||||||
|
appResult.SetResult(0);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
appResult.SetException(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await new HostBuilder()
|
||||||
|
.UseHttp2Cat(address, async h2Connection =>
|
||||||
|
{
|
||||||
|
await h2Connection.InitializeConnectionAsync();
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
|
||||||
|
|
||||||
|
await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
|
||||||
|
|
||||||
|
// Any app errors?
|
||||||
|
Assert.Equal(0, await appResult.Task.DefaultTimeout());
|
||||||
|
|
||||||
|
await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
|
||||||
|
{
|
||||||
|
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var resetFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
|
Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111);
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Connection stopped.");
|
||||||
|
})
|
||||||
|
.Build().RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Reset support was added in Win10_20H2.")]
|
||||||
|
public async Task Reset_DurringResponseBody_Resets()
|
||||||
|
{
|
||||||
|
var appResult = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assert.Equal("HTTP/2", httpContext.Request.Protocol);
|
||||||
|
var feature = httpContext.Features.Get<IHttpResetFeature>();
|
||||||
|
Assert.NotNull(feature);
|
||||||
|
await httpContext.Response.WriteAsync("Hello World");
|
||||||
|
feature.Reset(1111); // Custom
|
||||||
|
appResult.SetResult(0);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
appResult.SetException(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await new HostBuilder()
|
||||||
|
.UseHttp2Cat(address, async h2Connection =>
|
||||||
|
{
|
||||||
|
await h2Connection.InitializeConnectionAsync();
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
|
||||||
|
|
||||||
|
await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
|
||||||
|
|
||||||
|
// Any app errors?
|
||||||
|
Assert.Equal(0, await appResult.Task.DefaultTimeout());
|
||||||
|
|
||||||
|
await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
|
||||||
|
{
|
||||||
|
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var dataFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
|
Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: false, length: 11);
|
||||||
|
|
||||||
|
var resetFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
|
Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111);
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Connection stopped.");
|
||||||
|
})
|
||||||
|
.Build().RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Reset support was added in Win10_20H2.")]
|
||||||
|
public async Task Reset_AfterCompleteAsync_NoReset()
|
||||||
|
{
|
||||||
|
var appResult = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assert.Equal("HTTP/2", httpContext.Request.Protocol);
|
||||||
|
var feature = httpContext.Features.Get<IHttpResetFeature>();
|
||||||
|
Assert.NotNull(feature);
|
||||||
|
await httpContext.Response.WriteAsync("Hello World");
|
||||||
|
await httpContext.Response.CompleteAsync();
|
||||||
|
// The request and response are fully complete, the reset doesn't get sent.
|
||||||
|
feature.Reset(1111);
|
||||||
|
appResult.SetResult(0);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
appResult.SetException(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await new HostBuilder()
|
||||||
|
.UseHttp2Cat(address, async h2Connection =>
|
||||||
|
{
|
||||||
|
await h2Connection.InitializeConnectionAsync();
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
|
||||||
|
|
||||||
|
await h2Connection.StartStreamAsync(1, Http2Utilities.BrowserRequestHeaders, endStream: true);
|
||||||
|
|
||||||
|
// Any app errors?
|
||||||
|
Assert.Equal(0, await appResult.Task.DefaultTimeout());
|
||||||
|
|
||||||
|
await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
|
||||||
|
{
|
||||||
|
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var dataFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
|
Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: false, length: 11);
|
||||||
|
|
||||||
|
dataFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
|
Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Connection stopped.");
|
||||||
|
})
|
||||||
|
.Build().RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Reset support was added in Win10_20H2.")]
|
||||||
|
public async Task Reset_BeforeRequestBody_Resets()
|
||||||
|
{
|
||||||
|
var appResult = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assert.Equal("HTTP/2", httpContext.Request.Protocol);
|
||||||
|
var feature = httpContext.Features.Get<IHttpResetFeature>();
|
||||||
|
Assert.NotNull(feature);
|
||||||
|
var readTask = httpContext.Request.Body.ReadAsync(new byte[10], 0, 10);
|
||||||
|
|
||||||
|
feature.Reset(1111);
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<IOException>(() => readTask);
|
||||||
|
|
||||||
|
appResult.SetResult(0);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
appResult.SetException(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await new HostBuilder()
|
||||||
|
.UseHttp2Cat(address, async h2Connection =>
|
||||||
|
{
|
||||||
|
await h2Connection.InitializeConnectionAsync();
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
|
||||||
|
|
||||||
|
await h2Connection.StartStreamAsync(1, Http2Utilities.PostRequestHeaders, endStream: false);
|
||||||
|
|
||||||
|
// Any app errors?
|
||||||
|
Assert.Equal(0, await appResult.Task.DefaultTimeout());
|
||||||
|
|
||||||
|
var resetFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
|
Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111);
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Connection stopped.");
|
||||||
|
})
|
||||||
|
.Build().RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Reset support was added in Win10_20H2.")]
|
||||||
|
public async Task Reset_DurringRequestBody_Resets()
|
||||||
|
{
|
||||||
|
var appResult = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assert.Equal("HTTP/2", httpContext.Request.Protocol);
|
||||||
|
var feature = httpContext.Features.Get<IHttpResetFeature>();
|
||||||
|
Assert.NotNull(feature);
|
||||||
|
|
||||||
|
var read = await httpContext.Request.Body.ReadAsync(new byte[10], 0, 10);
|
||||||
|
Assert.Equal(10, read);
|
||||||
|
|
||||||
|
var readTask = httpContext.Request.Body.ReadAsync(new byte[10], 0, 10);
|
||||||
|
feature.Reset(1111);
|
||||||
|
await Assert.ThrowsAsync<IOException>(() => readTask);
|
||||||
|
|
||||||
|
appResult.SetResult(0);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
appResult.SetException(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await new HostBuilder()
|
||||||
|
.UseHttp2Cat(address, async h2Connection =>
|
||||||
|
{
|
||||||
|
await h2Connection.InitializeConnectionAsync();
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
|
||||||
|
|
||||||
|
await h2Connection.StartStreamAsync(1, Http2Utilities.PostRequestHeaders, endStream: false);
|
||||||
|
await h2Connection.SendDataAsync(1, new byte[10], endStream: false);
|
||||||
|
|
||||||
|
// Any app errors?
|
||||||
|
Assert.Equal(0, await appResult.Task.DefaultTimeout());
|
||||||
|
|
||||||
|
var resetFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
|
Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: (Http2ErrorCode)1111);
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Connection stopped.");
|
||||||
|
})
|
||||||
|
.Build().RunAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Reset support was added in Win10_20H2.")]
|
||||||
|
public async Task Reset_CompleteAsyncDurringRequestBody_Resets()
|
||||||
|
{
|
||||||
|
var appResult = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
using var server = Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assert.Equal("HTTP/2", httpContext.Request.Protocol);
|
||||||
|
var feature = httpContext.Features.Get<IHttpResetFeature>();
|
||||||
|
Assert.NotNull(feature);
|
||||||
|
|
||||||
|
var read = await httpContext.Request.Body.ReadAsync(new byte[10], 0, 10);
|
||||||
|
Assert.Equal(10, read);
|
||||||
|
|
||||||
|
var readTask = httpContext.Request.Body.ReadAsync(new byte[10], 0, 10);
|
||||||
|
await httpContext.Response.CompleteAsync();
|
||||||
|
feature.Reset((int)Http2ErrorCode.NO_ERROR); // GRPC does this
|
||||||
|
await Assert.ThrowsAsync<IOException>(() => readTask);
|
||||||
|
|
||||||
|
appResult.SetResult(0);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
appResult.SetException(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await new HostBuilder()
|
||||||
|
.UseHttp2Cat(address, async h2Connection =>
|
||||||
|
{
|
||||||
|
await h2Connection.InitializeConnectionAsync();
|
||||||
|
|
||||||
|
h2Connection.Logger.LogInformation("Initialized http2 connection. Starting stream 1.");
|
||||||
|
|
||||||
|
await h2Connection.StartStreamAsync(1, Http2Utilities.PostRequestHeaders, endStream: false);
|
||||||
|
await h2Connection.SendDataAsync(1, new byte[10], endStream: false);
|
||||||
|
|
||||||
|
// Any app errors?
|
||||||
|
Assert.Equal(0, await appResult.Task.DefaultTimeout());
|
||||||
|
|
||||||
|
await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
|
||||||
|
{
|
||||||
|
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var dataFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
|
Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);
|
||||||
|
|
||||||
|
var resetFrame = await h2Connection.ReceiveFrameAsync();
|
||||||
|
Http2Utilities.VerifyResetFrame(resetFrame, expectedStreamId: 1, expectedErrorCode: Http2ErrorCode.NO_ERROR);
|
||||||
|
|
||||||
h2Connection.Logger.LogInformation("Connection stopped.");
|
h2Connection.Logger.LogInformation("Connection stopped.");
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19505", SkipReason = "Requires HTTP/2 Trailers support.")]
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Requires HTTP/2 Trailers support.")]
|
||||||
public async Task ResponseTrailers_HTTP2_TrailersAvailable()
|
public async Task ResponseTrailers_HTTP2_TrailersAvailable()
|
||||||
{
|
{
|
||||||
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
||||||
|
|
@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19505", SkipReason = "Requires HTTP/2 Trailers support.")]
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Requires HTTP/2 Trailers support.")]
|
||||||
public async Task ResponseTrailers_ProhibitedTrailers_Blocked()
|
public async Task ResponseTrailers_ProhibitedTrailers_Blocked()
|
||||||
{
|
{
|
||||||
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
||||||
|
|
@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19505", SkipReason = "Requires HTTP/2 Trailers support.")]
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Requires HTTP/2 Trailers support.")]
|
||||||
public async Task ResponseTrailers_NoBody_TrailersSent()
|
public async Task ResponseTrailers_NoBody_TrailersSent()
|
||||||
{
|
{
|
||||||
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
||||||
|
|
@ -95,7 +95,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19505", SkipReason = "Requires HTTP/2 Trailers support.")]
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Requires HTTP/2 Trailers support.")]
|
||||||
public async Task ResponseTrailers_WithBody_TrailersSent()
|
public async Task ResponseTrailers_WithBody_TrailersSent()
|
||||||
{
|
{
|
||||||
using (Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
|
using (Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
|
||||||
|
|
@ -114,7 +114,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19505", SkipReason = "Requires HTTP/2 Trailers support.")]
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Requires HTTP/2 Trailers support.")]
|
||||||
public async Task ResponseTrailers_WithContentLengthBody_TrailersNotSent()
|
public async Task ResponseTrailers_WithContentLengthBody_TrailersNotSent()
|
||||||
{
|
{
|
||||||
var body = "Hello World";
|
var body = "Hello World";
|
||||||
|
|
@ -145,7 +145,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19505", SkipReason = "Requires HTTP/2 Trailers support.")]
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Requires HTTP/2 Trailers support.")]
|
||||||
public async Task ResponseTrailers_WithTrailersBeforeContentLengthBody_TrailersSent()
|
public async Task ResponseTrailers_WithTrailersBeforeContentLengthBody_TrailersSent()
|
||||||
{
|
{
|
||||||
var body = "Hello World";
|
var body = "Hello World";
|
||||||
|
|
@ -170,7 +170,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19505", SkipReason = "Requires HTTP/2 Trailers support.")]
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Requires HTTP/2 Trailers support.")]
|
||||||
public async Task ResponseTrailers_WithContentLengthBodyAndDeclared_TrailersSent()
|
public async Task ResponseTrailers_WithContentLengthBodyAndDeclared_TrailersSent()
|
||||||
{
|
{
|
||||||
var body = "Hello World";
|
var body = "Hello World";
|
||||||
|
|
@ -196,7 +196,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19505", SkipReason = "Requires HTTP/2 Trailers support.")]
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Requires HTTP/2 Trailers support.")]
|
||||||
public async Task ResponseTrailers_WithContentLengthBodyAndDeclaredButMissingTrailers_Completes()
|
public async Task ResponseTrailers_WithContentLengthBodyAndDeclaredButMissingTrailers_Completes()
|
||||||
{
|
{
|
||||||
var body = "Hello World";
|
var body = "Hello World";
|
||||||
|
|
@ -221,7 +221,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19505", SkipReason = "Requires HTTP/2 Trailers support.")]
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Requires HTTP/2 Trailers support.")]
|
||||||
public async Task ResponseTrailers_CompleteAsyncNoBody_TrailersSent()
|
public async Task ResponseTrailers_CompleteAsyncNoBody_TrailersSent()
|
||||||
{
|
{
|
||||||
var trailersReceived = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
var trailersReceived = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
@ -242,7 +242,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19505", SkipReason = "Requires HTTP/2 Trailers support.")]
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Requires HTTP/2 Trailers support.")]
|
||||||
public async Task ResponseTrailers_CompleteAsyncWithBody_TrailersSent()
|
public async Task ResponseTrailers_CompleteAsyncWithBody_TrailersSent()
|
||||||
{
|
{
|
||||||
var trailersReceived = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
var trailersReceived = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
|
@ -265,7 +265,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19505", SkipReason = "Requires HTTP/2 Trailers support.")]
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Requires HTTP/2 Trailers support.")]
|
||||||
public async Task ResponseTrailers_MultipleValues_SentAsSeperateHeaders()
|
public async Task ResponseTrailers_MultipleValues_SentAsSeperateHeaders()
|
||||||
{
|
{
|
||||||
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
||||||
|
|
@ -285,7 +285,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19505", SkipReason = "Requires HTTP/2 Trailers support.")]
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Requires HTTP/2 Trailers support.")]
|
||||||
public async Task ResponseTrailers_LargeTrailers_Success()
|
public async Task ResponseTrailers_LargeTrailers_Success()
|
||||||
{
|
{
|
||||||
var values = new[] {
|
var values = new[] {
|
||||||
|
|
@ -313,7 +313,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalTheory, MemberData(nameof(NullHeaderData))]
|
[ConditionalTheory, MemberData(nameof(NullHeaderData))]
|
||||||
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19505", SkipReason = "Requires HTTP/2 Trailers support.")]
|
[MinimumOSVersion(OperatingSystems.Windows, "10.0.19529", SkipReason = "Requires HTTP/2 Trailers support.")]
|
||||||
public async Task ResponseTrailers_NullValues_Ignored(string headerName, StringValues headerValue, StringValues expectedValue)
|
public async Task ResponseTrailers_NullValues_Ignored(string headerName, StringValues headerValue, StringValues expectedValue)
|
||||||
{
|
{
|
||||||
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ using System.IO.Pipelines;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.HPack;
|
using System.Net.Http.HPack;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -938,6 +939,31 @@ namespace Microsoft.AspNetCore.Http2Cat
|
||||||
return WaitForConnectionErrorAsync<Exception>(ignoreNonGoAwayFrames, expectedLastStreamId, Http2ErrorCode.NO_ERROR);
|
return WaitForConnectionErrorAsync<Exception>(ignoreNonGoAwayFrames, expectedLastStreamId, Http2ErrorCode.NO_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal Task ReceiveHeadersAsync(int expectedStreamId, Action<IDictionary<string, string>> verifyHeaders = null)
|
||||||
|
=> ReceiveHeadersAsync(expectedStreamId, endStream: false, verifyHeaders);
|
||||||
|
|
||||||
|
internal async Task ReceiveHeadersAsync(int expectedStreamId, bool endStream = false, Action<IDictionary<string, string>> verifyHeaders = null)
|
||||||
|
{
|
||||||
|
var headersFrame = await ReceiveFrameAsync();
|
||||||
|
Assert.Equal(Http2FrameType.HEADERS, headersFrame.Type);
|
||||||
|
Assert.Equal(expectedStreamId, headersFrame.StreamId);
|
||||||
|
Assert.True((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_HEADERS) != 0);
|
||||||
|
Assert.Equal(endStream, (headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_STREAM) != 0);
|
||||||
|
Logger.LogInformation("Received headers in a single frame.");
|
||||||
|
|
||||||
|
ResetHeaders();
|
||||||
|
DecodeHeaders(headersFrame);
|
||||||
|
verifyHeaders?.Invoke(_decodedHeaders);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void VerifyDataFrame(Http2Frame frame, int expectedStreamId, bool endOfStream, int length)
|
||||||
|
{
|
||||||
|
Assert.Equal(Http2FrameType.DATA, frame.Type);
|
||||||
|
Assert.Equal(expectedStreamId, frame.StreamId);
|
||||||
|
Assert.Equal(endOfStream ? Http2DataFrameFlags.END_STREAM : Http2DataFrameFlags.NONE, frame.DataFlags);
|
||||||
|
Assert.Equal(length, frame.PayloadLength);
|
||||||
|
}
|
||||||
|
|
||||||
internal void VerifyGoAway(Http2Frame frame, int expectedLastStreamId, Http2ErrorCode expectedErrorCode)
|
internal void VerifyGoAway(Http2Frame frame, int expectedLastStreamId, Http2ErrorCode expectedErrorCode)
|
||||||
{
|
{
|
||||||
Assert.Equal(Http2FrameType.GOAWAY, frame.Type);
|
Assert.Equal(Http2FrameType.GOAWAY, frame.Type);
|
||||||
|
|
@ -948,6 +974,15 @@ namespace Microsoft.AspNetCore.Http2Cat
|
||||||
Assert.Equal(expectedErrorCode, frame.GoAwayErrorCode);
|
Assert.Equal(expectedErrorCode, frame.GoAwayErrorCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void VerifyResetFrame(Http2Frame frame, int expectedStreamId, Http2ErrorCode expectedErrorCode)
|
||||||
|
{
|
||||||
|
Assert.Equal(Http2FrameType.RST_STREAM, frame.Type);
|
||||||
|
Assert.Equal(expectedStreamId, frame.StreamId);
|
||||||
|
Assert.Equal(expectedErrorCode, frame.RstStreamErrorCode);
|
||||||
|
Assert.Equal(4, frame.PayloadLength);
|
||||||
|
Assert.Equal(0, frame.Flags);
|
||||||
|
}
|
||||||
|
|
||||||
internal async Task WaitForConnectionErrorAsync<TException>(bool ignoreNonGoAwayFrames, int expectedLastStreamId, Http2ErrorCode expectedErrorCode)
|
internal async Task WaitForConnectionErrorAsync<TException>(bool ignoreNonGoAwayFrames, int expectedLastStreamId, Http2ErrorCode expectedErrorCode)
|
||||||
where TException : Exception
|
where TException : Exception
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,16 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
HttpResponseInfoTypeQosProperty,
|
HttpResponseInfoTypeQosProperty,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal enum HTTP_REQUEST_PROPERTY
|
||||||
|
{
|
||||||
|
HttpRequestPropertyIsb,
|
||||||
|
HttpRequestPropertyTcpInfoV0,
|
||||||
|
HttpRequestPropertyQuicStats,
|
||||||
|
HttpRequestPropertyTcpInfoV1,
|
||||||
|
HttpRequestPropertySni,
|
||||||
|
HttpRequestPropertyStreamError,
|
||||||
|
}
|
||||||
|
|
||||||
internal enum HTTP_TIMEOUT_TYPE
|
internal enum HTTP_TIMEOUT_TYPE
|
||||||
{
|
{
|
||||||
EntityBody,
|
EntityBody,
|
||||||
|
|
@ -61,6 +71,11 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
|
||||||
MinSendRate,
|
MinSendRate,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal struct HTTP_REQUEST_PROPERTY_STREAM_ERROR
|
||||||
|
{
|
||||||
|
internal uint ErrorCode;
|
||||||
|
}
|
||||||
|
|
||||||
internal const int MaxTimeout = 6;
|
internal const int MaxTimeout = 6;
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue