Expose Http.Sys's Http/2 Reset error code API #17276 #16988 (#17645)

This commit is contained in:
Chris Ross 2019-12-09 06:17:38 -08:00 committed by GitHub
parent 0fd9f27d60
commit aca5f04922
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 753 additions and 117 deletions

View File

@ -36,7 +36,8 @@ namespace Microsoft.AspNetCore.Server.HttpSys
IHttpMaxRequestBodySizeFeature,
IHttpBodyControlFeature,
IHttpSysRequestInfoFeature,
IHttpResponseTrailersFeature
IHttpResponseTrailersFeature,
IHttpResetFeature
{
private RequestContext _requestContext;
private IFeatureCollection _features;
@ -350,7 +351,16 @@ namespace Microsoft.AspNetCore.Server.HttpSys
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;
}
@ -455,6 +465,12 @@ namespace Microsoft.AspNetCore.Server.HttpSys
Task IHttpResponseBodyFeature.CompleteAsync() => CompleteAsync();
void IHttpResetFeature.Reset(int errorCode)
{
_requestContext.SetResetCode(errorCode);
_requestContext.Abort();
}
internal async Task CompleteAsync()
{
if (!_responseStarted)

View File

@ -228,6 +228,9 @@ namespace Microsoft.AspNetCore.Server.HttpSys
_application.DisposeContext(context, ex);
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();
}
else

View File

@ -11,15 +11,11 @@ namespace Microsoft.AspNetCore.Server.HttpSys
// Minimum support for Windows 7 is assumed.
internal static readonly bool IsWin8orLater;
internal static readonly bool SupportsTrailers;
static ComNetOS()
{
var win8Version = new Version(6, 2);
IsWin8orLater = (Environment.OSVersion.Version >= win8Version);
SupportsTrailers = Environment.OSVersion.Version >= new Version(10, 0, 19505);
}
}
}

View File

@ -70,6 +70,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
[DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
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;
@ -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()
{
InitHttpApi(2, 0);
@ -119,6 +125,16 @@ namespace Microsoft.AspNetCore.Server.HttpSys
var statusCode = HttpInitialize(version, (uint)HTTP_FLAGS.HTTP_INITIALIZE_SERVER, null);
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;

View File

@ -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);
}
}
}
}

View File

@ -4,6 +4,7 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Security.Authentication.ExtendedProtection;
using System.Security.Claims;
using System.Security.Principal;
@ -218,5 +219,25 @@ namespace Microsoft.AspNetCore.Server.HttpSys
// 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
}
}
}
}

View File

@ -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.
// Needed to delay the completion of Content-Length responses.
internal bool TrailersExpected => HasTrailers
|| (ComNetOS.SupportsTrailers && Request.ProtocolVersion >= HttpVersion.Version20
|| (HttpApi.SupportsTrailers && Request.ProtocolVersion >= HttpVersion.Version20
&& Headers.ContainsKey(HttpKnownHeaderNames.Trailer));
internal long ExpectedBodyLength

View File

@ -28,6 +28,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
{ typeof(IHttpBodyControlFeature), _identityFunc },
{ typeof(IHttpSysRequestInfoFeature), _identityFunc },
{ typeof(IHttpResponseTrailersFeature), ctx => ctx.GetResponseTrailersFeature() },
{ typeof(IHttpResetFeature), ctx => ctx.GetResetFeature() },
};
private readonly FeatureContext _featureContext;

View File

@ -1,7 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http2Cat;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
using Microsoft.AspNetCore.Testing;
@ -16,7 +22,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys.FunctionalTests
{
[ConditionalFact(Skip = "https://github.com/aspnet/AspNetCore/issues/17420")]
[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()
{
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);
var headersFrame = await h2Connection.ReceiveFrameAsync();
Assert.Equal(Http2FrameType.HEADERS, headersFrame.Type);
Assert.True((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_HEADERS) != 0);
Assert.True((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_STREAM) != 0);
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]);
await h2Connection.ReceiveHeadersAsync(1, endStream: true, decodedHeaders =>
{
// 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.
await h2Connection.StartStreamAsync(3, Http2Utilities.BrowserRequestHeaders, endStream: true);
headersFrame = await h2Connection.ReceiveFrameAsync();
Assert.Equal(Http2FrameType.HEADERS, headersFrame.Type);
Assert.True((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_HEADERS) != 0);
Assert.True((headersFrame.Flags & (byte)Http2HeadersFrameFlags.END_STREAM) != 0);
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.ReceiveHeadersAsync(3, endStream: true, decodedHeaders =>
{
// 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);
@ -96,23 +87,15 @@ namespace Microsoft.AspNetCore.Server.HttpSys.FunctionalTests
var goAwayFrame = await h2Connection.ReceiveFrameAsync();
h2Connection.VerifyGoAway(goAwayFrame, int.MaxValue, Http2ErrorCode.NO_ERROR);
var headersFrame = await h2Connection.ReceiveFrameAsync();
Assert.Equal(Http2FrameType.HEADERS, headersFrame.Type);
Assert.Equal(Http2HeadersFrameFlags.END_HEADERS, headersFrame.HeadersFlags);
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]);
await h2Connection.ReceiveHeadersAsync(1, decodedHeaders =>
{
// HTTP/2 filters out the connection header
Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
});
var dataFrame = await h2Connection.ReceiveFrameAsync();
Assert.Equal(Http2FrameType.DATA, dataFrame.Type);
Assert.Equal(Http2DataFrameFlags.END_STREAM, dataFrame.DataFlags);
Assert.Equal(0, dataFrame.PayloadLength);
Http2Utilities.VerifyDataFrame(dataFrame, 1, endOfStream: true, length: 0);
// 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();
h2Connection.VerifyGoAway(goAwayFrame, int.MaxValue, Http2ErrorCode.NO_ERROR);
var headersFrame = await h2Connection.ReceiveFrameAsync();
Assert.Equal(Http2FrameType.HEADERS, headersFrame.Type);
Assert.Equal(Http2HeadersFrameFlags.END_HEADERS, headersFrame.HeadersFlags);
Assert.Equal(streamId, headersFrame.StreamId);
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();
await h2Connection.ReceiveHeadersAsync(streamId, decodedHeaders =>
{
// HTTP/2 filters out the connection header
Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
});
var dataFrame = await h2Connection.ReceiveFrameAsync();
Assert.Equal(Http2FrameType.DATA, dataFrame.Type);
Assert.Equal(Http2DataFrameFlags.END_STREAM, dataFrame.DataFlags);
Assert.Equal(0, dataFrame.PayloadLength);
Assert.Equal(streamId, dataFrame.StreamId);
Http2Utilities.VerifyDataFrame(dataFrame, streamId, endOfStream: true, length: 0);
// 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.
await h2Connection.StartStreamAsync(streamId, Http2Utilities.BrowserRequestHeaders, endStream: true);
headersFrame = await h2Connection.ReceiveFrameAsync();
Assert.Equal(Http2FrameType.HEADERS, headersFrame.Type);
Assert.Equal(Http2HeadersFrameFlags.END_HEADERS, headersFrame.HeadersFlags);
Assert.Equal(streamId, headersFrame.StreamId);
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();
await h2Connection.ReceiveHeadersAsync(streamId, decodedHeaders =>
{
// HTTP/2 filters out the connection header
Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
});
dataFrame = await h2Connection.ReceiveFrameAsync();
Assert.Equal(Http2FrameType.DATA, dataFrame.Type);
Assert.Equal(Http2DataFrameFlags.END_STREAM, dataFrame.DataFlags);
Assert.Equal(0, dataFrame.PayloadLength);
Assert.Equal(streamId, dataFrame.StreamId);
Http2Utilities.VerifyDataFrame(dataFrame, streamId, endOfStream: true, length: 0);
}
streamId = 1 + (200 * 2); // Odds.
@ -202,26 +163,495 @@ namespace Microsoft.AspNetCore.Server.HttpSys.FunctionalTests
h2Connection.VerifyGoAway(goAwayFrame, streamId, Http2ErrorCode.NO_ERROR);
// Normal response
headersFrame = await h2Connection.ReceiveFrameAsync();
Assert.Equal(Http2FrameType.HEADERS, headersFrame.Type);
Assert.Equal(Http2HeadersFrameFlags.END_HEADERS, headersFrame.HeadersFlags);
Assert.Equal(streamId, headersFrame.StreamId);
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();
await h2Connection.ReceiveHeadersAsync(streamId, decodedHeaders =>
{
// HTTP/2 filters out the connection header
Assert.False(decodedHeaders.ContainsKey(HeaderNames.Connection));
Assert.Equal("200", decodedHeaders[HeaderNames.Status]);
});
dataFrame = await h2Connection.ReceiveFrameAsync();
Assert.Equal(Http2FrameType.DATA, dataFrame.Type);
Assert.Equal(Http2DataFrameFlags.END_STREAM, dataFrame.DataFlags);
Assert.Equal(0, dataFrame.PayloadLength);
Assert.Equal(streamId, dataFrame.StreamId);
Http2Utilities.VerifyDataFrame(dataFrame, streamId, endOfStream: true, length: 0);
h2Connection.Logger.LogInformation("Connection stopped.");
})
.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.");
})

View File

@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
}
[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()
{
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
}
[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()
{
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
}
[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()
{
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
@ -95,7 +95,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
}
[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()
{
using (Utilities.CreateDynamicHttpsServer(out var address, async httpContext =>
@ -114,7 +114,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
}
[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()
{
var body = "Hello World";
@ -145,7 +145,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
}
[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()
{
var body = "Hello World";
@ -170,7 +170,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
}
[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()
{
var body = "Hello World";
@ -196,7 +196,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
}
[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()
{
var body = "Hello World";
@ -221,7 +221,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
}
[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()
{
var trailersReceived = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
@ -242,7 +242,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
}
[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()
{
var trailersReceived = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
@ -265,7 +265,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
}
[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()
{
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>
@ -285,7 +285,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
}
[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()
{
var values = new[] {
@ -313,7 +313,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
}
[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)
{
using (Utilities.CreateDynamicHttpsServer(out var address, httpContext =>

View File

@ -10,6 +10,7 @@ using System.IO.Pipelines;
using System.Linq;
using System.Net.Http;
using System.Net.Http.HPack;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -938,6 +939,31 @@ namespace Microsoft.AspNetCore.Http2Cat
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)
{
Assert.Equal(Http2FrameType.GOAWAY, frame.Type);
@ -948,6 +974,15 @@ namespace Microsoft.AspNetCore.Http2Cat
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)
where TException : Exception
{

View File

@ -51,6 +51,16 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
HttpResponseInfoTypeQosProperty,
}
internal enum HTTP_REQUEST_PROPERTY
{
HttpRequestPropertyIsb,
HttpRequestPropertyTcpInfoV0,
HttpRequestPropertyQuicStats,
HttpRequestPropertyTcpInfoV1,
HttpRequestPropertySni,
HttpRequestPropertyStreamError,
}
internal enum HTTP_TIMEOUT_TYPE
{
EntityBody,
@ -61,6 +71,11 @@ namespace Microsoft.AspNetCore.HttpSys.Internal
MinSendRate,
}
internal struct HTTP_REQUEST_PROPERTY_STREAM_ERROR
{
internal uint ErrorCode;
}
internal const int MaxTimeout = 6;
[StructLayout(LayoutKind.Sequential)]