parent
0fd9f27d60
commit
aca5f04922
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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 =>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
Loading…
Reference in New Issue