diff --git a/eng/Dependencies.props b/eng/Dependencies.props index 24a51f35c23d..c7eb8b903136 100644 --- a/eng/Dependencies.props +++ b/eng/Dependencies.props @@ -200,6 +200,7 @@ and are generated based on the last package release. + diff --git a/eng/Versions.props b/eng/Versions.props index fa088883126d..dcc63e67ff9e 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -302,6 +302,7 @@ 2.13.4 2.13.4 2.13.4 + 0.3.46-beta $(MessagePackVersion) 4.10.0 0.11.2 diff --git a/src/Servers/HttpSys/HttpSysServer.slnf b/src/Servers/HttpSys/HttpSysServer.slnf index f8072017e440..492b2dfc1077 100644 --- a/src/Servers/HttpSys/HttpSysServer.slnf +++ b/src/Servers/HttpSys/HttpSysServer.slnf @@ -2,6 +2,7 @@ "solution": { "path": "..\\..\\..\\AspNetCore.sln", "projects": [ + "src\\Antiforgery\\src\\Microsoft.AspNetCore.Antiforgery.csproj", "src\\DataProtection\\Abstractions\\src\\Microsoft.AspNetCore.DataProtection.Abstractions.csproj", "src\\DataProtection\\Cryptography.Internal\\src\\Microsoft.AspNetCore.Cryptography.Internal.csproj", "src\\DataProtection\\DataProtection\\src\\Microsoft.AspNetCore.DataProtection.csproj", diff --git a/src/Servers/HttpSys/perf/Microbenchmarks/RequestHeaderBenchmarks.cs b/src/Servers/HttpSys/perf/Microbenchmarks/RequestHeaderBenchmarks.cs index 50b62ec61f2d..848354b30bc3 100644 --- a/src/Servers/HttpSys/perf/Microbenchmarks/RequestHeaderBenchmarks.cs +++ b/src/Servers/HttpSys/perf/Microbenchmarks/RequestHeaderBenchmarks.cs @@ -6,6 +6,8 @@ using System.Text; using BenchmarkDotNet.Attributes; using Microsoft.AspNetCore.HttpSys.Internal; +using Windows.Win32.Foundation; +using Windows.Win32.Networking.HttpServer; using RequestHeaders = Microsoft.AspNetCore.HttpSys.Internal.RequestHeaders; [SimpleJob, MemoryDiagnoser] @@ -54,7 +56,7 @@ private unsafe RequestHeaders CreateRequestHeader(int unknowHeaderCount) var nativeContext = new NativeRequestContext(MemoryPool.Shared, null, 0, false); var nativeMemory = new Span(nativeContext.NativeRequest, (int)nativeContext.Size + 8); - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); var remainingMemory = SetUnknownHeaders(nativeMemory, ref requestStructure, GenerateUnknownHeaders(unknowHeaderCount)); SetHostHeader(remainingMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -64,15 +66,15 @@ private unsafe RequestHeaders CreateRequestHeader(int unknowHeaderCount) return requestHeaders; } - private unsafe Span SetHostHeader(Span nativeMemory, ref HttpApiTypes.HTTP_REQUEST requestStructure) + private unsafe Span SetHostHeader(Span nativeMemory, ref HTTP_REQUEST_V1 requestStructure) { // Writing localhost to Host header - var dataDestination = nativeMemory.Slice(Marshal.SizeOf()); - int length = Encoding.ASCII.GetBytes("localhost:5001", dataDestination); + var dataDestination = nativeMemory[Marshal.SizeOf()..]; + var length = Encoding.ASCII.GetBytes("localhost:5001", dataDestination); fixed (byte* address = &MemoryMarshal.GetReference(dataDestination)) { - requestStructure.Headers.KnownHeaders_29.pRawValue = address; - requestStructure.Headers.KnownHeaders_29.RawValueLength = (ushort)length; + requestStructure.Headers.KnownHeaders._28.pRawValue = (PCSTR)address; + requestStructure.Headers.KnownHeaders._28.RawValueLength = (ushort)length; } return dataDestination; } @@ -80,48 +82,48 @@ private unsafe Span SetHostHeader(Span nativeMemory, ref HttpApiType /// /// Writes an array HTTP_UNKNOWN_HEADER and an array of header key-value pairs to nativeMemory. Pointers in the HTTP_UNKNOWN_HEADER structure points to the corresponding key-value pair. /// - private unsafe Span SetUnknownHeaders(Span nativeMemory, ref HttpApiTypes.HTTP_REQUEST requestStructure, IReadOnlyCollection<(string Key, string Value)> headerNames) + private unsafe Span SetUnknownHeaders(Span nativeMemory, ref HTTP_REQUEST_V1 requestStructure, IReadOnlyCollection<(string Key, string Value)> headerNames) { - var unknownHeaderStructureDestination = nativeMemory.Slice(Marshal.SizeOf()); + var unknownHeaderStructureDestination = nativeMemory[Marshal.SizeOf()..]; fixed (byte* address = &MemoryMarshal.GetReference(unknownHeaderStructureDestination)) { - requestStructure.Headers.pUnknownHeaders = (HttpApiTypes.HTTP_UNKNOWN_HEADER*)address; + requestStructure.Headers.pUnknownHeaders = (HTTP_UNKNOWN_HEADER*)address; } requestStructure.Headers.UnknownHeaderCount += (ushort)headerNames.Count; - var unknownHeadersSize = Marshal.SizeOf(); - var dataDestination = unknownHeaderStructureDestination.Slice(unknownHeadersSize * headerNames.Count); - foreach (var headerName in headerNames) + var unknownHeadersSize = Marshal.SizeOf(); + var dataDestination = unknownHeaderStructureDestination[(unknownHeadersSize * headerNames.Count)..]; + foreach (var (headerKey, headerValue) in headerNames) { - var unknownHeaderStructure = new HttpApiTypes.HTTP_UNKNOWN_HEADER(); - int nameLength = Encoding.ASCII.GetBytes(headerName.Key, dataDestination); + var unknownHeaderStructure = new HTTP_UNKNOWN_HEADER(); + var nameLength = Encoding.ASCII.GetBytes(headerKey, dataDestination); fixed (byte* address = &MemoryMarshal.GetReference(dataDestination)) { - unknownHeaderStructure.pName = address; + unknownHeaderStructure.pName = (PCSTR)address; unknownHeaderStructure.NameLength = (ushort)nameLength; } - dataDestination = dataDestination.Slice(nameLength); + dataDestination = dataDestination[nameLength..]; - if (!string.IsNullOrEmpty(headerName.Value)) + if (!string.IsNullOrEmpty(headerValue)) { - int valueLength = Encoding.ASCII.GetBytes(headerName.Value, dataDestination); + var valueLength = Encoding.ASCII.GetBytes(headerValue, dataDestination); fixed (byte* address = &MemoryMarshal.GetReference(dataDestination)) { - unknownHeaderStructure.pRawValue = address; + unknownHeaderStructure.pRawValue = (PCSTR)address; unknownHeaderStructure.RawValueLength = (ushort)valueLength; } - dataDestination = dataDestination.Slice(nameLength); + dataDestination = dataDestination[nameLength..]; } MemoryMarshal.Write(unknownHeaderStructureDestination, in unknownHeaderStructure); - unknownHeaderStructureDestination = unknownHeaderStructureDestination.Slice(unknownHeadersSize); + unknownHeaderStructureDestination = unknownHeaderStructureDestination[unknownHeadersSize..]; } return dataDestination; } - private IReadOnlyCollection<(string, string)> GenerateUnknownHeaders(int count) + private static List<(string, string)> GenerateUnknownHeaders(int count) { var result = new List<(string, string)>(); - for (int i = 0; i < count; i++) + for (var i = 0; i < count; i++) { result.Add(($"X-Custom-{i}", $"Value-{i}")); } diff --git a/src/Servers/HttpSys/samples/TestClient/Program.cs b/src/Servers/HttpSys/samples/TestClient/Program.cs index 16155fd98b4d..82f4bf1af6c1 100644 --- a/src/Servers/HttpSys/samples/TestClient/Program.cs +++ b/src/Servers/HttpSys/samples/TestClient/Program.cs @@ -1,94 +1,89 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Net; using System.Net.Http; using System.Net.WebSockets; using System.Text; -using System.Threading; -using System.Threading.Tasks; -namespace TestClient +namespace TestClient; + +public class Program { - public class Program + private const string Address = + "http://localhost:5000/public/1kb.txt"; + // "https://localhost:9090/public/1kb.txt"; + + public static void Main(string[] args) { - private const string Address = - "http://localhost:5000/public/1kb.txt"; - // "https://localhost:9090/public/1kb.txt"; + Console.WriteLine("Ready"); + Console.ReadKey(); - public static void Main(string[] args) - { - Console.WriteLine("Ready"); - Console.ReadKey(); + var handler = new HttpClientHandler(); + handler.MaxConnectionsPerServer = 500; + handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; + // handler.UseDefaultCredentials = true; + HttpClient client = new HttpClient(handler); - var handler = new HttpClientHandler(); - handler.MaxConnectionsPerServer = 500; - handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; - // handler.UseDefaultCredentials = true; - HttpClient client = new HttpClient(handler); + RunParallelRequests(client); - RunParallelRequests(client); + // RunManualRequests(client); - // RunManualRequests(client); + // RunWebSocketClient().Wait(); - // RunWebSocketClient().Wait(); + Console.WriteLine("Done"); + // Console.ReadKey(); + } - Console.WriteLine("Done"); - // Console.ReadKey(); + private static void RunManualRequests(HttpClient client) + { + while (true) + { + Console.WriteLine("Press any key to send request"); + Console.ReadKey(); + var result = client.GetAsync(Address).Result; + Console.WriteLine(result); } + } - private static void RunManualRequests(HttpClient client) + private static void RunParallelRequests(HttpClient client) + { + int completionCount = 0; + int iterations = 100000; + for (int i = 0; i < iterations; i++) { - while (true) - { - Console.WriteLine("Press any key to send request"); - Console.ReadKey(); - var result = client.GetAsync(Address).Result; - Console.WriteLine(result); - } + client.GetAsync(Address) + .ContinueWith(t => Interlocked.Increment(ref completionCount)); } - private static void RunParallelRequests(HttpClient client) + while (completionCount < iterations) { - int completionCount = 0; - int iterations = 100000; - for (int i = 0; i < iterations; i++) - { - client.GetAsync(Address) - .ContinueWith(t => Interlocked.Increment(ref completionCount)); - } - - while (completionCount < iterations) - { - Thread.Sleep(10); - } + Thread.Sleep(10); } + } + + public static async Task RunWebSocketClient() + { + ClientWebSocket websocket = new ClientWebSocket(); + + string url = "ws://localhost:5000/"; + Console.WriteLine("Connecting to: " + url); + await websocket.ConnectAsync(new Uri(url), CancellationToken.None); - public static async Task RunWebSocketClient() + string message = "Hello World"; + Console.WriteLine("Sending message: " + message); + byte[] messageBytes = Encoding.UTF8.GetBytes(message); + await websocket.SendAsync(new ArraySegment(messageBytes), WebSocketMessageType.Text, true, CancellationToken.None); + + byte[] incomingData = new byte[1024]; + WebSocketReceiveResult result = await websocket.ReceiveAsync(new ArraySegment(incomingData), CancellationToken.None); + + if (result.CloseStatus.HasValue) + { + Console.WriteLine("Closed; Status: " + result.CloseStatus + ", " + result.CloseStatusDescription); + } + else { - ClientWebSocket websocket = new ClientWebSocket(); - - string url = "ws://localhost:5000/"; - Console.WriteLine("Connecting to: " + url); - await websocket.ConnectAsync(new Uri(url), CancellationToken.None); - - string message = "Hello World"; - Console.WriteLine("Sending message: " + message); - byte[] messageBytes = Encoding.UTF8.GetBytes(message); - await websocket.SendAsync(new ArraySegment(messageBytes), WebSocketMessageType.Text, true, CancellationToken.None); - - byte[] incomingData = new byte[1024]; - WebSocketReceiveResult result = await websocket.ReceiveAsync(new ArraySegment(incomingData), CancellationToken.None); - - if (result.CloseStatus.HasValue) - { - Console.WriteLine("Closed; Status: " + result.CloseStatus + ", " + result.CloseStatusDescription); - } - else - { - Console.WriteLine("Received message: " + Encoding.UTF8.GetString(incomingData, 0, result.Count)); - } + Console.WriteLine("Received message: " + Encoding.UTF8.GetString(incomingData, 0, result.Count)); } } } diff --git a/src/Servers/HttpSys/src/AsyncAcceptContext.cs b/src/Servers/HttpSys/src/AsyncAcceptContext.cs index 72131695aed6..945428ab1da8 100644 --- a/src/Servers/HttpSys/src/AsyncAcceptContext.cs +++ b/src/Servers/HttpSys/src/AsyncAcceptContext.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Threading.Tasks.Sources; -using Microsoft.AspNetCore.HttpSys.Internal; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -39,9 +38,9 @@ internal ValueTask AcceptAsync() AllocateNativeRequest(); - uint statusCode = QueueBeginGetContext(); - if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && - statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) + var statusCode = QueueBeginGetContext(); + if (statusCode != ErrorCodes.ERROR_SUCCESS && + statusCode != ErrorCodes.ERROR_IO_PENDING) { // some other bad error, possible(?) return values are: // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED @@ -55,8 +54,8 @@ private void IOCompleted(uint errorCode, uint numBytes) { try { - if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && - errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA) + if (errorCode != ErrorCodes.ERROR_SUCCESS && + errorCode != ErrorCodes.ERROR_MORE_DATA) { _mrvts.SetException(new HttpSysException((int)errorCode)); return; @@ -64,7 +63,7 @@ private void IOCompleted(uint errorCode, uint numBytes) Debug.Assert(_requestContext != null); - if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) + if (errorCode == ErrorCodes.ERROR_SUCCESS) { var requestContext = _requestContext; // It's important that we clear the request context before we set the result @@ -79,10 +78,10 @@ private void IOCompleted(uint errorCode, uint numBytes) AllocateNativeRequest(numBytes, _requestContext.RequestId); // We need to issue a new request, either because auth failed, or because our buffer was too small the first time. - uint statusCode = QueueBeginGetContext(); + var statusCode = QueueBeginGetContext(); - if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && - statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) + if (statusCode != ErrorCodes.ERROR_SUCCESS && + statusCode != ErrorCodes.ERROR_IO_PENDING) { // someother bad error, possible(?) return values are: // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED @@ -117,14 +116,14 @@ private uint QueueBeginGetContext() _requestContext.RequestId, // Small perf impact by not using HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY // if the request sends header+body in a single TCP packet - (uint)HttpApiTypes.HTTP_FLAGS.NONE, + 0u, _requestContext.NativeRequest, _requestContext.Size, &bytesTransferred, _overlapped); - if ((statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_CONNECTION_INVALID - || statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_INVALID_PARAMETER) + if ((statusCode == ErrorCodes.ERROR_CONNECTION_INVALID + || statusCode == ErrorCodes.ERROR_INVALID_PARAMETER) && _requestContext.RequestId != 0) { // ERROR_CONNECTION_INVALID: @@ -139,7 +138,7 @@ private uint QueueBeginGetContext() _requestContext.RequestId = 0; retry = true; } - else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA) + else if (statusCode == ErrorCodes.ERROR_MORE_DATA) { // the buffer was not big enough to fit the headers, we need // to read the RequestId returned, allocate a new buffer of the required size @@ -147,7 +146,7 @@ private uint QueueBeginGetContext() AllocateNativeRequest(bytesTransferred); retry = true; } - else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS + else if (statusCode == ErrorCodes.ERROR_SUCCESS && HttpSysListener.SkipIOCPCallbackOnSuccess) { // IO operation completed synchronously - callback won't be called to signal completion. diff --git a/src/Servers/HttpSys/src/AuthenticationManager.cs b/src/Servers/HttpSys/src/AuthenticationManager.cs index dc4f88b7d93e..7bf734d6ffa8 100644 --- a/src/Servers/HttpSys/src/AuthenticationManager.cs +++ b/src/Servers/HttpSys/src/AuthenticationManager.cs @@ -4,9 +4,9 @@ using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; -using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys; public sealed class AuthenticationManager { private static readonly int AuthInfoSize = - Marshal.SizeOf(); + Marshal.SizeOf(); private UrlGroup? _urlGroup; private AuthenticationSchemes _authSchemes; @@ -78,14 +78,12 @@ private unsafe void SetUrlGroupSecurity() return; } - HttpApiTypes.HTTP_SERVER_AUTHENTICATION_INFO authInfo = - new HttpApiTypes.HTTP_SERVER_AUTHENTICATION_INFO(); + var authInfo = new HTTP_SERVER_AUTHENTICATION_INFO(); + authInfo.Flags = HttpApi.HTTP_PROPERTY_FLAGS_PRESENT; - authInfo.Flags = HttpApiTypes.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT; - var authSchemes = (HttpApiTypes.HTTP_AUTH_TYPES)_authSchemes; - if (authSchemes != HttpApiTypes.HTTP_AUTH_TYPES.NONE) + if (_authSchemes != AuthenticationSchemes.None) { - authInfo.AuthSchemes = authSchemes; + authInfo.AuthSchemes = (uint)_authSchemes; // TODO: // NTLM auth sharing (on by default?) DisableNTLMCredentialCaching @@ -97,7 +95,7 @@ private unsafe void SetUrlGroupSecurity() IntPtr infoptr = new IntPtr(&authInfo); _urlGroup.SetProperty( - HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerAuthenticationProperty, + HTTP_SERVER_PROPERTY.HttpServerAuthenticationProperty, infoptr, (uint)AuthInfoSize); } } diff --git a/src/Servers/HttpSys/src/HttpSysListener.cs b/src/Servers/HttpSys/src/HttpSysListener.cs index 2b7924491d32..81d333e027ff 100644 --- a/src/Servers/HttpSys/src/HttpSysListener.cs +++ b/src/Servers/HttpSys/src/HttpSysListener.cs @@ -6,6 +6,9 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.Extensions.Logging; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -50,8 +53,6 @@ public HttpSysListener(HttpSysOptions options, ILoggerFactory loggerFactory) throw new PlatformNotSupportedException(); } - Debug.Assert(HttpApi.ApiVersion == HttpApiTypes.HTTP_API_VERSION.Version20, "Invalid Http api version"); - Options = options; Logger = loggerFactory.CreateLogger(); @@ -304,10 +305,12 @@ internal bool ValidateRequest(NativeRequestContext requestMemory) internal unsafe void SendError(ulong requestId, int httpStatusCode, IList? authChallenges = null) { - HttpApiTypes.HTTP_RESPONSE_V2 httpResponse = new HttpApiTypes.HTTP_RESPONSE_V2(); - httpResponse.Response_V1.Version = new HttpApiTypes.HTTP_VERSION(); - httpResponse.Response_V1.Version.MajorVersion = (ushort)1; - httpResponse.Response_V1.Version.MinorVersion = (ushort)1; + var httpResponse = new HTTP_RESPONSE_V2(); + httpResponse.Base.Version = new() + { + MajorVersion = 1, + MinorVersion = 1 + }; using UnmanagedBufferAllocator allocator = new(); @@ -317,29 +320,29 @@ internal unsafe void SendError(ulong requestId, int httpStatusCode, IList 0) { - HttpApiTypes.HTTP_RESPONSE_INFO* knownHeaderInfo = allocator.AllocAsPointer(1); + var knownHeaderInfo = allocator.AllocAsPointer(1); httpResponse.pResponseInfo = knownHeaderInfo; - knownHeaderInfo[httpResponse.ResponseInfoCount].Type = HttpApiTypes.HTTP_RESPONSE_INFO_TYPE.HttpResponseInfoTypeMultipleKnownHeaders; + knownHeaderInfo[httpResponse.ResponseInfoCount].Type = HTTP_RESPONSE_INFO_TYPE.HttpResponseInfoTypeMultipleKnownHeaders; knownHeaderInfo[httpResponse.ResponseInfoCount].Length = - (uint)sizeof(HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS); + (uint)sizeof(HTTP_MULTIPLE_KNOWN_HEADERS); - HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS* header = allocator.AllocAsPointer(1); + var header = allocator.AllocAsPointer(1); - header->HeaderId = HttpApiTypes.HTTP_RESPONSE_HEADER_ID.Enum.HttpHeaderWwwAuthenticate; - header->Flags = HttpApiTypes.HTTP_RESPONSE_INFO_FLAGS.PreserveOrder; // The docs say this is for www-auth only. + header->HeaderId = HTTP_HEADER_ID.HttpHeaderWwwAuthenticate; + header->Flags = PInvoke.HTTP_RESPONSE_INFO_FLAGS_PRESERVE_ORDER; // The docs say this is for www-auth only. header->KnownHeaderCount = 0; - HttpApiTypes.HTTP_KNOWN_HEADER* nativeHeaderValues = allocator.AllocAsPointer(authChallenges.Count); + var nativeHeaderValues = allocator.AllocAsPointer(authChallenges.Count); header->KnownHeaders = nativeHeaderValues; - for (int headerValueIndex = 0; headerValueIndex < authChallenges.Count; headerValueIndex++) + for (var headerValueIndex = 0; headerValueIndex < authChallenges.Count; headerValueIndex++) { // Add Value - string headerValue = authChallenges[headerValueIndex]; + var headerValue = authChallenges[headerValueIndex]; bytes = allocator.GetHeaderEncodedBytes(headerValue, out bytesLength); nativeHeaderValues[header->KnownHeaderCount].RawValueLength = checked((ushort)bytesLength); - nativeHeaderValues[header->KnownHeaderCount].pRawValue = bytes; + nativeHeaderValues[header->KnownHeaderCount].pRawValue = (PCSTR)bytes; header->KnownHeaderCount++; } @@ -348,40 +351,38 @@ internal unsafe void SendError(ulong requestId, int httpStatusCode, IList(contentLengthLength + 1); + var pContentLength = allocator.AllocAsPointer(contentLengthLength + 1); pContentLength[0] = (byte)'0'; pContentLength[1] = 0; // null terminator - (&httpResponse.Response_V1.Headers.KnownHeaders)[(int)HttpSysResponseHeader.ContentLength].pRawValue = pContentLength; - (&httpResponse.Response_V1.Headers.KnownHeaders)[(int)HttpSysResponseHeader.ContentLength].RawValueLength = contentLengthLength; - httpResponse.Response_V1.Headers.UnknownHeaderCount = 0; - - statusCode = - HttpApi.HttpSendHttpResponse( - _requestQueue.Handle, - requestId, - 0, - &httpResponse, - null, - &dataWritten, - IntPtr.Zero, - 0, - SafeNativeOverlapped.Zero, - IntPtr.Zero); - if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) + var knownHeaders = httpResponse.Base.Headers.KnownHeaders.AsSpan(); + knownHeaders[(int)HttpSysResponseHeader.ContentLength].pRawValue = (PCSTR)pContentLength; + knownHeaders[(int)HttpSysResponseHeader.ContentLength].RawValueLength = contentLengthLength; + httpResponse.Base.Headers.UnknownHeaderCount = 0; + + statusCode = PInvoke.HttpSendHttpResponse( + _requestQueue.Handle, + requestId, + 0, + httpResponse, + null, + &dataWritten, + null, + null); + if (statusCode != ErrorCodes.ERROR_SUCCESS) { // if we fail to send a 401 something's seriously wrong, abort the request - HttpApi.HttpCancelHttpRequest(_requestQueue.Handle, requestId, IntPtr.Zero); + PInvoke.HttpCancelHttpRequest(_requestQueue.Handle, requestId, default); } } diff --git a/src/Servers/HttpSys/src/Microsoft.AspNetCore.Server.HttpSys.csproj b/src/Servers/HttpSys/src/Microsoft.AspNetCore.Server.HttpSys.csproj index 60b4e3c5a3e4..d210084f3f66 100644 --- a/src/Servers/HttpSys/src/Microsoft.AspNetCore.Server.HttpSys.csproj +++ b/src/Servers/HttpSys/src/Microsoft.AspNetCore.Server.HttpSys.csproj @@ -9,7 +9,6 @@ aspnetcore;weblistener;httpsys false true - $(NoWarn);CA1416 @@ -23,7 +22,7 @@ - + @@ -31,11 +30,19 @@ + + + + + + all + runtime; build; native; contentfiles; analyzers + diff --git a/src/Servers/HttpSys/src/NativeInterop/DisconnectListener.cs b/src/Servers/HttpSys/src/NativeInterop/DisconnectListener.cs index 26f95d13674d..f4dc688f3478 100644 --- a/src/Servers/HttpSys/src/NativeInterop/DisconnectListener.cs +++ b/src/Servers/HttpSys/src/NativeInterop/DisconnectListener.cs @@ -3,7 +3,6 @@ using System.Collections.Concurrent; using System.ComponentModel; -using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -95,8 +94,8 @@ private unsafe CancellationToken CreateDisconnectToken(ulong connectionId) Log.CreateDisconnectTokenError(_logger, exception); } - if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING && - statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) + if (statusCode != ErrorCodes.ERROR_IO_PENDING && + statusCode != ErrorCodes.ERROR_SUCCESS) { // We got an unknown result, assume the connection has been closed. boundHandle.FreeNativeOverlapped(nativeOverlapped); @@ -105,7 +104,7 @@ private unsafe CancellationToken CreateDisconnectToken(ulong connectionId) cts.Cancel(); } - if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && HttpSysListener.SkipIOCPCallbackOnSuccess) + if (statusCode == ErrorCodes.ERROR_SUCCESS && HttpSysListener.SkipIOCPCallbackOnSuccess) { // IO operation completed synchronously - callback won't be called to signal completion boundHandle.FreeNativeOverlapped(nativeOverlapped); diff --git a/src/Servers/HttpSys/src/NativeInterop/ErrorCodes.cs b/src/Servers/HttpSys/src/NativeInterop/ErrorCodes.cs new file mode 100644 index 000000000000..d66397390430 --- /dev/null +++ b/src/Servers/HttpSys/src/NativeInterop/ErrorCodes.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Server.HttpSys; + +// Only the useful subset of WIN32_ERROR +internal static class ErrorCodes +{ + internal const uint ERROR_SUCCESS = 0; + internal const uint ERROR_FILE_NOT_FOUND = 2; + internal const uint ERROR_ACCESS_DENIED = 5; + internal const uint ERROR_SHARING_VIOLATION = 32; + internal const uint ERROR_HANDLE_EOF = 38; + internal const uint ERROR_NOT_SUPPORTED = 50; + internal const uint ERROR_INVALID_PARAMETER = 87; + internal const uint ERROR_INVALID_NAME = 123; + internal const uint ERROR_ALREADY_EXISTS = 183; + internal const uint ERROR_MORE_DATA = 234; + internal const uint ERROR_OPERATION_ABORTED = 995; + internal const uint ERROR_IO_PENDING = 997; + internal const uint ERROR_NOT_FOUND = 1168; + internal const uint ERROR_CONNECTION_INVALID = 1229; +} diff --git a/src/Servers/HttpSys/src/NativeInterop/HttpApi.cs b/src/Servers/HttpSys/src/NativeInterop/HttpApi.cs index 575ebc259d25..1fec2ffea7e6 100644 --- a/src/Servers/HttpSys/src/NativeInterop/HttpApi.cs +++ b/src/Servers/HttpSys/src/NativeInterop/HttpApi.cs @@ -3,149 +3,63 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using Microsoft.AspNetCore.HttpSys.Internal; -using static Microsoft.AspNetCore.HttpSys.Internal.HttpApiTypes; +using Windows.Win32; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.HttpSys; -internal static unsafe partial class HttpApi +internal static partial class HttpApi { + private const string api_ms_win_core_io_LIB = "api-ms-win-core-io-l1-1-0.dll"; private const string HTTPAPI = "httpapi.dll"; - [LibraryImport(HTTPAPI, SetLastError = true)] - internal static partial uint HttpInitialize(HTTPAPI_VERSION version, uint flags, void* pReserved); - [LibraryImport(HTTPAPI, SetLastError = true)] internal static partial uint HttpReceiveRequestEntityBody(SafeHandle requestQueueHandle, ulong requestId, uint flags, IntPtr pEntityBuffer, uint entityBufferLength, out uint bytesReturned, SafeNativeOverlapped pOverlapped); [LibraryImport(HTTPAPI, SetLastError = true)] - internal static partial uint HttpReceiveClientCertificate(SafeHandle requestQueueHandle, ulong connectionId, uint flags, HTTP_SSL_CLIENT_CERT_INFO* pSslClientCertInfo, uint sslClientCertInfoSize, uint* pBytesReceived, SafeNativeOverlapped pOverlapped); - - [LibraryImport(HTTPAPI, SetLastError = true)] - internal static partial uint HttpReceiveClientCertificate(SafeHandle requestQueueHandle, ulong connectionId, uint flags, byte* pSslClientCertInfo, uint sslClientCertInfoSize, uint* pBytesReceived, SafeNativeOverlapped pOverlapped); - - [LibraryImport(HTTPAPI, SetLastError = true)] - internal static partial uint HttpReceiveHttpRequest(SafeHandle requestQueueHandle, ulong requestId, uint flags, HTTP_REQUEST* pRequestBuffer, uint requestBufferLength, uint* pBytesReturned, NativeOverlapped* pOverlapped); - - [LibraryImport(HTTPAPI, SetLastError = true)] - internal static partial uint HttpSendHttpResponse(SafeHandle requestQueueHandle, ulong requestId, uint flags, HTTP_RESPONSE_V2* pHttpResponse, HTTP_CACHE_POLICY* pCachePolicy, uint* pBytesSent, IntPtr pReserved1, uint Reserved2, SafeNativeOverlapped pOverlapped, IntPtr pLogData); - - [LibraryImport(HTTPAPI, SetLastError = true)] - internal static partial uint HttpSendResponseEntityBody(SafeHandle requestQueueHandle, ulong requestId, uint flags, ushort entityChunkCount, HTTP_DATA_CHUNK* pEntityChunks, uint* pBytesSent, IntPtr pReserved1, uint Reserved2, SafeNativeOverlapped pOverlapped, IntPtr pLogData); - - [LibraryImport(HTTPAPI, SetLastError = true)] - internal static partial uint HttpCancelHttpRequest(SafeHandle requestQueueHandle, ulong requestId, IntPtr pOverlapped); - - [LibraryImport(HTTPAPI, SetLastError = true)] - internal static partial uint HttpWaitForDisconnectEx(SafeHandle requestQueueHandle, ulong connectionId, uint reserved, NativeOverlapped* overlapped); - - [LibraryImport(HTTPAPI, SetLastError = true)] - internal static partial uint HttpCreateServerSession(HTTPAPI_VERSION version, ulong* serverSessionId, uint reserved); - - [LibraryImport(HTTPAPI, SetLastError = true)] - internal static partial uint HttpCreateUrlGroup(ulong serverSessionId, ulong* urlGroupId, uint reserved); - - [LibraryImport(HTTPAPI, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - internal static partial uint HttpFindUrlGroupId(string pFullyQualifiedUrl, SafeHandle requestQueueHandle, ulong* urlGroupId); - - [LibraryImport(HTTPAPI, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - internal static partial uint HttpAddUrlToUrlGroup(ulong urlGroupId, string pFullyQualifiedUrl, ulong context, uint pReserved); - - [LibraryImport(HTTPAPI, SetLastError = true)] - internal static partial uint HttpSetUrlGroupProperty(ulong urlGroupId, HTTP_SERVER_PROPERTY serverProperty, IntPtr pPropertyInfo, uint propertyInfoLength); - - [LibraryImport(HTTPAPI, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - internal static partial uint HttpRemoveUrlFromUrlGroup(ulong urlGroupId, string pFullyQualifiedUrl, uint flags); - - [LibraryImport(HTTPAPI, SetLastError = true)] - internal static partial uint HttpCloseServerSession(ulong serverSessionId); + internal static unsafe partial uint HttpReceiveClientCertificate(SafeHandle requestQueueHandle, ulong connectionId, uint flags, Windows.Win32.Networking.HttpServer.HTTP_SSL_CLIENT_CERT_INFO* pSslClientCertInfo, uint sslClientCertInfoSize, uint* pBytesReceived, SafeNativeOverlapped pOverlapped); [LibraryImport(HTTPAPI, SetLastError = true)] - internal static partial uint HttpCloseUrlGroup(ulong urlGroupId); + internal static unsafe partial uint HttpReceiveHttpRequest(SafeHandle requestQueueHandle, ulong requestId, uint flags, Windows.Win32.Networking.HttpServer.HTTP_REQUEST_V1* pRequestBuffer, uint requestBufferLength, uint* pBytesReturned, NativeOverlapped* pOverlapped); [LibraryImport(HTTPAPI, SetLastError = true)] - internal static partial uint HttpSetRequestQueueProperty(SafeHandle requestQueueHandle, HTTP_SERVER_PROPERTY serverProperty, IntPtr pPropertyInfo, uint propertyInfoLength, uint reserved, IntPtr pReserved); - - [LibraryImport(HTTPAPI, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] - internal static unsafe partial uint HttpCreateRequestQueue(HTTPAPI_VERSION version, string? pName, - IntPtr pSecurityAttributes, HTTP_CREATE_REQUEST_QUEUE_FLAG flags, out HttpRequestQueueV2Handle pReqQueueHandle); + internal static unsafe partial uint HttpSendHttpResponse(SafeHandle requestQueueHandle, ulong requestId, uint flags, Windows.Win32.Networking.HttpServer.HTTP_RESPONSE_V2* pHttpResponse, Windows.Win32.Networking.HttpServer.HTTP_CACHE_POLICY* pCachePolicy, uint* pBytesSent, IntPtr pReserved1, uint Reserved2, SafeNativeOverlapped pOverlapped, IntPtr pLogData); [LibraryImport(HTTPAPI, SetLastError = true)] - internal static unsafe partial uint HttpCloseRequestQueue(IntPtr pReqQueueHandle); + internal static unsafe partial uint HttpWaitForDisconnectEx(SafeHandle requestQueueHandle, ulong connectionId, uint reserved, NativeOverlapped* overlapped); [LibraryImport(HTTPAPI, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool HttpIsFeatureSupported(HTTP_FEATURE_ID feature); + internal static unsafe partial uint HttpSendResponseEntityBody(SafeHandle requestQueueHandle, ulong requestId, uint flags, ushort entityChunkCount, Windows.Win32.Networking.HttpServer.HTTP_DATA_CHUNK* pEntityChunks, uint* pBytesSent, IntPtr pReserved1, uint Reserved2, SafeNativeOverlapped pOverlapped, IntPtr pLogData); - [LibraryImport(HTTPAPI, SetLastError = true)] - internal static unsafe partial uint HttpDelegateRequestEx(SafeHandle pReqQueueHandle, SafeHandle pDelegateQueueHandle, ulong requestId, - ulong delegateUrlGroupId, uint propertyInfoSetSize, HTTP_DELEGATE_REQUEST_PROPERTY_INFO* pRequestPropertyBuffer); + [LibraryImport(api_ms_win_core_io_LIB, SetLastError = true)] + internal static partial uint CancelIoEx(SafeHandle handle, SafeNativeOverlapped overlapped); - internal delegate uint HttpGetRequestPropertyInvoker(SafeHandle requestQueueHandle, ulong requestId, HTTP_REQUEST_PROPERTY propertyId, + internal unsafe delegate uint HttpGetRequestPropertyInvoker(SafeHandle requestQueueHandle, ulong requestId, HTTP_REQUEST_PROPERTY propertyId, void* qualifier, uint qualifierSize, void* output, uint outputSize, uint* bytesReturned, IntPtr overlapped); - internal delegate uint HttpSetRequestPropertyInvoker(SafeHandle requestQueueHandle, ulong requestId, HTTP_REQUEST_PROPERTY propertyId, void* input, uint inputSize, IntPtr overlapped); - - private static HTTPAPI_VERSION version; - - // This property is used by HttpListener to pass the version structure to the native layer in API - // calls. + internal unsafe delegate uint HttpSetRequestPropertyInvoker(SafeHandle requestQueueHandle, ulong requestId, HTTP_REQUEST_PROPERTY propertyId, void* input, uint inputSize, IntPtr overlapped); - internal static HTTPAPI_VERSION Version - { - get - { - return version; - } - } - - // This property is used by HttpListener to get the Api version in use so that it uses appropriate - // Http APIs. - - internal static HTTP_API_VERSION ApiVersion - { - get - { - if (version.HttpApiMajorVersion == 2 && version.HttpApiMinorVersion == 0) - { - return HTTP_API_VERSION.Version20; - } - else if (version.HttpApiMajorVersion == 1 && version.HttpApiMinorVersion == 0) - { - return HTTP_API_VERSION.Version10; - } - else - { - return HTTP_API_VERSION.Invalid; - } - } - } - - internal static SafeLibraryHandle? HttpApiModule { get; private set; } - internal static HttpGetRequestPropertyInvoker? HttpGetRequestProperty { get; private set; } - internal static HttpSetRequestPropertyInvoker? HttpSetRequestProperty { get; private set; } + // HTTP_PROPERTY_FLAGS.Present (1) + internal static HTTP_PROPERTY_FLAGS HTTP_PROPERTY_FLAGS_PRESENT { get; } = new() { _bitfield = 0x00000001 }; + // This property is used by HttpListener to pass the version structure to the native layer in API calls. + internal static HTTPAPI_VERSION Version { get; } = new () { HttpApiMajorVersion = 2 }; + internal static SafeLibraryHandle? HttpApiModule { get; } + internal static HttpGetRequestPropertyInvoker? HttpGetRequestProperty { get; } + internal static HttpSetRequestPropertyInvoker? HttpSetRequestProperty { get; } [MemberNotNullWhen(true, nameof(HttpSetRequestProperty))] - internal static bool SupportsTrailers { get; private set; } + internal static bool SupportsTrailers { get; } [MemberNotNullWhen(true, nameof(HttpSetRequestProperty))] - internal static bool SupportsReset { get; private set; } - internal static bool SupportsDelegation { get; private set; } - - static HttpApi() - { - InitHttpApi(2, 0); - } + internal static bool SupportsReset { get; } + internal static bool SupportsDelegation { get; } + internal static bool Supported { get; } - private static void InitHttpApi(ushort majorVersion, ushort minorVersion) + static unsafe HttpApi() { - version.HttpApiMajorVersion = majorVersion; - version.HttpApiMinorVersion = minorVersion; - - var statusCode = HttpInitialize(version, (uint)(HTTP_FLAGS.HTTP_INITIALIZE_SERVER | HTTP_FLAGS.HTTP_INITIALIZE_CONFIG), null); + var statusCode = PInvoke.HttpInitialize(Version, HTTP_INITIALIZE.HTTP_INITIALIZE_SERVER | HTTP_INITIALIZE.HTTP_INITIALIZE_CONFIG); - supported = statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS; - - if (supported) + if (statusCode == ErrorCodes.ERROR_SUCCESS) { + Supported = true; HttpApiModule = SafeLibraryHandle.Open(HTTPAPI); HttpGetRequestProperty = HttpApiModule.GetProcAddress("HttpQueryRequestProperty", throwIfNotFound: false); HttpSetRequestProperty = HttpApiModule.GetProcAddress("HttpSetRequestProperty", throwIfNotFound: false); @@ -155,20 +69,11 @@ private static void InitHttpApi(ushort majorVersion, ushort minorVersion) } } - private static volatile bool supported; - internal static bool Supported - { - get - { - return supported; - } - } - private static bool IsFeatureSupported(HTTP_FEATURE_ID feature) { try { - return HttpIsFeatureSupported(feature); + return PInvoke.HttpIsFeatureSupported(feature); } catch (EntryPointNotFoundException) { } diff --git a/src/Servers/HttpSys/src/NativeInterop/HttpRequestQueueV2Handle.cs b/src/Servers/HttpSys/src/NativeInterop/HttpRequestQueueV2Handle.cs deleted file mode 100644 index d792748c8934..000000000000 --- a/src/Servers/HttpSys/src/NativeInterop/HttpRequestQueueV2Handle.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.AspNetCore.HttpSys.Internal; -using Microsoft.Win32.SafeHandles; - -namespace Microsoft.AspNetCore.Server.HttpSys; - -// This class is a wrapper for Http.sys V2 request queue handle. -internal sealed class HttpRequestQueueV2Handle : SafeHandleZeroOrMinusOneIsInvalid -{ - public HttpRequestQueueV2Handle() - : base(true) - { - } - - protected override bool ReleaseHandle() - { - return (HttpApi.HttpCloseRequestQueue(handle) == - UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS); - } -} diff --git a/src/Servers/HttpSys/src/NativeInterop/HttpServerSessionHandle.cs b/src/Servers/HttpSys/src/NativeInterop/HttpServerSessionHandle.cs index 3fda8c736137..1cf4a687e349 100644 --- a/src/Servers/HttpSys/src/NativeInterop/HttpServerSessionHandle.cs +++ b/src/Servers/HttpSys/src/NativeInterop/HttpServerSessionHandle.cs @@ -1,8 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.Win32.SafeHandles; +using Windows.Win32; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -34,8 +34,8 @@ protected override bool ReleaseHandle() if (Interlocked.Increment(ref disposed) == 1) { // Closing server session also closes all open url groups under that server session. - return (HttpApi.HttpCloseServerSession(serverSessionId) == - UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS); + return PInvoke.HttpCloseServerSession(serverSessionId) == + ErrorCodes.ERROR_SUCCESS; } } return true; diff --git a/src/Servers/HttpSys/src/NativeInterop/IntPtrHelper.cs b/src/Servers/HttpSys/src/NativeInterop/IntPtrHelper.cs deleted file mode 100644 index 30abb9b8c234..000000000000 --- a/src/Servers/HttpSys/src/NativeInterop/IntPtrHelper.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.AspNetCore.Server.HttpSys; - -internal static class IntPtrHelper -{ - internal static IntPtr Add(IntPtr a, int b) - { - return (IntPtr)((long)a + (long)b); - } - - internal static long Subtract(IntPtr a, IntPtr b) - { - return ((long)a - (long)b); - } -} diff --git a/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs b/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs index 4330f40f8620..810bdcce3c57 100644 --- a/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs +++ b/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs @@ -3,8 +3,9 @@ using System.Diagnostics; using System.Runtime.InteropServices; -using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.Extensions.Logging; +using Windows.Win32; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -28,58 +29,58 @@ private RequestQueue(string? requestQueueName, RequestQueueMode mode, ILogger lo _mode = mode; _logger = logger; - var flags = HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.None; + var flags = 0u; Created = true; if (_mode == RequestQueueMode.Attach) { - flags = HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.OpenExisting; + flags = PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_OPEN_EXISTING; Created = false; if (receiver) { - flags |= HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.Delegation; + flags |= PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_DELEGATION; } } - var statusCode = HttpApi.HttpCreateRequestQueue( + var statusCode = PInvoke.HttpCreateRequestQueue( HttpApi.Version, requestQueueName, - IntPtr.Zero, + default, flags, out var requestQueueHandle); - if (_mode == RequestQueueMode.CreateOrAttach && statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_ALREADY_EXISTS) + if (_mode == RequestQueueMode.CreateOrAttach && statusCode == ErrorCodes.ERROR_ALREADY_EXISTS) { // Tried to create, but it already exists so attach to it instead. Created = false; - flags = HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.OpenExisting; - statusCode = HttpApi.HttpCreateRequestQueue( + flags = PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_OPEN_EXISTING; + statusCode = PInvoke.HttpCreateRequestQueue( HttpApi.Version, requestQueueName, - IntPtr.Zero, + default, flags, out requestQueueHandle); } - if (flags.HasFlag(HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.OpenExisting) && statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_FILE_NOT_FOUND) + if ((flags & PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_OPEN_EXISTING) != 0 && statusCode == ErrorCodes.ERROR_FILE_NOT_FOUND) { throw new HttpSysException((int)statusCode, $"Failed to attach to the given request queue '{requestQueueName}', the queue could not be found."); } - else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_INVALID_NAME) + else if (statusCode == ErrorCodes.ERROR_INVALID_NAME) { throw new HttpSysException((int)statusCode, $"The given request queue name '{requestQueueName}' is invalid."); } - else if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) + else if (statusCode != ErrorCodes.ERROR_SUCCESS) { throw new HttpSysException((int)statusCode); } // Disabling callbacks when IO operation completes synchronously (returns ErrorCodes.ERROR_SUCCESS) if (HttpSysListener.SkipIOCPCallbackOnSuccess && - !UnsafeNclNativeMethods.SetFileCompletionNotificationModes( + !PInvoke.SetFileCompletionNotificationModes( requestQueueHandle, - UnsafeNclNativeMethods.FileCompletionNotificationModes.SkipCompletionPortOnSuccess | - UnsafeNclNativeMethods.FileCompletionNotificationModes.SkipSetEventOnHandle)) + (byte)(PInvoke.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS | + PInvoke.FILE_SKIP_SET_EVENT_ON_HANDLE))) { requestQueueHandle.Dispose(); throw new HttpSysException(Marshal.GetLastWin32Error()); @@ -108,9 +109,9 @@ internal unsafe void SetLengthLimit(long length) Debug.Assert(Created); CheckDisposed(); - var result = HttpApi.HttpSetRequestQueueProperty(Handle, - HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerQueueLengthProperty, - new IntPtr((void*)&length), (uint)Marshal.SizeOf(), 0, IntPtr.Zero); + var result = PInvoke.HttpSetRequestQueueProperty(Handle, + HTTP_SERVER_PROPERTY.HttpServerQueueLengthProperty, + &length, (uint)Marshal.SizeOf()); if (result != 0) { @@ -124,9 +125,9 @@ internal unsafe void SetRejectionVerbosity(Http503VerbosityLevel verbosity) Debug.Assert(Created); CheckDisposed(); - var result = HttpApi.HttpSetRequestQueueProperty(Handle, - HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServer503VerbosityProperty, - new IntPtr((void*)&verbosity), (uint)Marshal.SizeOf(), 0, IntPtr.Zero); + var result = PInvoke.HttpSetRequestQueueProperty(Handle, + HTTP_SERVER_PROPERTY.HttpServer503VerbosityProperty, + &verbosity, (uint)Marshal.SizeOf()); if (result != 0) { diff --git a/src/Shared/HttpSys/NativeInterop/SafeNativeOverlapped.cs b/src/Servers/HttpSys/src/NativeInterop/SafeNativeOverlapped.cs similarity index 78% rename from src/Shared/HttpSys/NativeInterop/SafeNativeOverlapped.cs rename to src/Servers/HttpSys/src/NativeInterop/SafeNativeOverlapped.cs index df975315c305..a7a05623d8ce 100644 --- a/src/Shared/HttpSys/NativeInterop/SafeNativeOverlapped.cs +++ b/src/Servers/HttpSys/src/NativeInterop/SafeNativeOverlapped.cs @@ -1,18 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Diagnostics; using System.Runtime.InteropServices; -using System.Threading; -namespace Microsoft.AspNetCore.HttpSys.Internal; +namespace Microsoft.AspNetCore.Server.HttpSys; internal sealed class SafeNativeOverlapped : SafeHandle { internal static readonly SafeNativeOverlapped Zero = new SafeNativeOverlapped(); private readonly ThreadPoolBoundHandle? _boundHandle; + private static bool HasShutdownStarted => Environment.HasShutdownStarted + || AppDomain.CurrentDomain.IsFinalizingForUnload(); + public SafeNativeOverlapped() : base(IntPtr.Zero, true) { @@ -35,9 +36,9 @@ protected override bool ReleaseHandle() Debug.Assert(_boundHandle != null, "ReleaseHandle can't be called on SafeNativeOverlapped.Zero."); IntPtr oldHandle = Interlocked.Exchange(ref handle, IntPtr.Zero); - // Do not call free durring AppDomain shutdown, there may be an outstanding operation. + // Do not call free during AppDomain shutdown, there may be an outstanding operation. // Overlapped will take care calling free when the native callback completes. - if (oldHandle != IntPtr.Zero && !NclUtilities.HasShutdownStarted) + if (oldHandle != IntPtr.Zero && !HasShutdownStarted) { unsafe { diff --git a/src/Servers/HttpSys/src/NativeInterop/ServerSession.cs b/src/Servers/HttpSys/src/NativeInterop/ServerSession.cs index a1cff3fc396e..9e19f5994d28 100644 --- a/src/Servers/HttpSys/src/NativeInterop/ServerSession.cs +++ b/src/Servers/HttpSys/src/NativeInterop/ServerSession.cs @@ -1,8 +1,8 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using Microsoft.AspNetCore.HttpSys.Internal; +using Windows.Win32; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -11,10 +11,10 @@ internal sealed class ServerSession : IDisposable internal unsafe ServerSession() { ulong serverSessionId = 0; - var statusCode = HttpApi.HttpCreateServerSession( + var statusCode = PInvoke.HttpCreateServerSession( HttpApi.Version, &serverSessionId, 0); - if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) + if (statusCode != ErrorCodes.ERROR_SUCCESS) { throw new HttpSysException((int)statusCode); } diff --git a/src/Servers/HttpSys/src/NativeInterop/TokenBindingUtil.cs b/src/Servers/HttpSys/src/NativeInterop/TokenBindingUtil.cs deleted file mode 100644 index 67364726dc89..000000000000 --- a/src/Servers/HttpSys/src/NativeInterop/TokenBindingUtil.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.InteropServices; -using Microsoft.AspNetCore.HttpSys.Internal; -using static Microsoft.AspNetCore.HttpSys.Internal.HttpApiTypes; -using static Microsoft.AspNetCore.HttpSys.Internal.UnsafeNclNativeMethods.TokenBinding; - -namespace Microsoft.AspNetCore.Server.HttpSys; - -/// -/// Contains helpers for dealing with TLS token binding. -/// -// TODO: https://github.com/aspnet/HttpSysServer/issues/231 -internal static unsafe class TokenBindingUtil -{ - private static byte[] ExtractIdentifierBlob(TOKENBINDING_RESULT_DATA* pTokenBindingResultData) - { - // Per http://tools.ietf.org/html/draft-ietf-tokbind-protocol-00, Sec. 4, - // the identifier is a tuple which contains (token binding type, hash algorithm - // signature algorithm, key data). We'll strip off the token binding type and - // return the remainder (starting with the hash algorithm) as an opaque blob. - byte[] retVal = new byte[checked(pTokenBindingResultData->identifierSize - 1)]; - Marshal.Copy((IntPtr)(&pTokenBindingResultData->identifierData->hashAlgorithm), retVal, 0, retVal.Length); - return retVal; - } - - /// - /// Returns the 'provided' token binding identifier, optionally also returning the - /// 'referred' token binding identifier. Returns null on failure. - /// - public static byte[]? GetProvidedTokenIdFromBindingInfo(HTTP_REQUEST_TOKEN_BINDING_INFO* pTokenBindingInfo, out byte[]? referredId) - { - byte[]? providedId = null; - referredId = null; - - HeapAllocHandle? handle = null; - int status = UnsafeNclNativeMethods.TokenBindingVerifyMessage( - pTokenBindingInfo->TokenBinding, - pTokenBindingInfo->TokenBindingSize, - pTokenBindingInfo->KeyType, - pTokenBindingInfo->TlsUnique, - pTokenBindingInfo->TlsUniqueSize, - out handle); - - // No match found or there was an error? - if (status != 0 || handle == null || handle.IsInvalid) - { - return null; - } - - using (handle) - { - // Find the first 'provided' and 'referred' types. - TOKENBINDING_RESULT_LIST* pResultList = (TOKENBINDING_RESULT_LIST*)handle.DangerousGetHandle(); - for (int i = 0; i < pResultList->resultCount; i++) - { - TOKENBINDING_RESULT_DATA* pThisResultData = &pResultList->resultData[i]; - if (pThisResultData->identifierData->bindingType == TOKENBINDING_TYPE.TOKENBINDING_TYPE_PROVIDED) - { - if (providedId != null) - { - return null; // It is invalid to have more than one 'provided' identifier. - } - providedId = ExtractIdentifierBlob(pThisResultData); - } - else if (pThisResultData->identifierData->bindingType == TOKENBINDING_TYPE.TOKENBINDING_TYPE_REFERRED) - { - if (referredId != null) - { - return null; // It is invalid to have more than one 'referred' identifier. - } - referredId = ExtractIdentifierBlob(pThisResultData); - } - } - } - - return providedId; - } -} diff --git a/src/Servers/HttpSys/src/NativeInterop/UrlGroup.cs b/src/Servers/HttpSys/src/NativeInterop/UrlGroup.cs index dbde24640ef5..9dfc3ce9a9de 100644 --- a/src/Servers/HttpSys/src/NativeInterop/UrlGroup.cs +++ b/src/Servers/HttpSys/src/NativeInterop/UrlGroup.cs @@ -3,19 +3,21 @@ using System.Diagnostics; using System.Runtime.InteropServices; -using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.Extensions.Logging; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.HttpSys; internal sealed partial class UrlGroup : IDisposable { private static readonly int BindingInfoSize = - Marshal.SizeOf(); + Marshal.SizeOf(); private static readonly int QosInfoSize = - Marshal.SizeOf(); + Marshal.SizeOf(); private static readonly int RequestPropertyInfoSize = - Marshal.SizeOf(); + Marshal.SizeOf(); private readonly ILogger _logger; @@ -30,12 +32,10 @@ internal unsafe UrlGroup(ServerSession serverSession, RequestQueue requestQueue, _requestQueue = requestQueue; _logger = logger; - ulong urlGroupId = 0; _created = true; - var statusCode = HttpApi.HttpCreateUrlGroup( - _serverSession.Id.DangerousGetServerSessionId(), &urlGroupId, 0); + var statusCode = PInvoke.HttpCreateUrlGroup(_serverSession.Id.DangerousGetServerSessionId(), out var urlGroupId); - if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) + if (statusCode != ErrorCodes.ERROR_SUCCESS) { throw new HttpSysException((int)statusCode); } @@ -48,43 +48,50 @@ internal unsafe UrlGroup(ServerSession serverSession, RequestQueue requestQueue, internal unsafe void SetMaxConnections(long maxConnections) { - var connectionLimit = new HttpApiTypes.HTTP_CONNECTION_LIMIT_INFO(); - connectionLimit.Flags = HttpApiTypes.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT; - connectionLimit.MaxConnections = (uint)maxConnections; + var connectionLimit = new HTTP_CONNECTION_LIMIT_INFO + { + Flags = HttpApi.HTTP_PROPERTY_FLAGS_PRESENT, + MaxConnections = (uint)maxConnections + }; - var qosSettings = new HttpApiTypes.HTTP_QOS_SETTING_INFO(); - qosSettings.QosType = HttpApiTypes.HTTP_QOS_SETTING_TYPE.HttpQosSettingTypeConnectionLimit; - qosSettings.QosSetting = new IntPtr(&connectionLimit); + var qosSettings = new HTTP_QOS_SETTING_INFO + { + QosType = HTTP_QOS_SETTING_TYPE.HttpQosSettingTypeConnectionLimit, + QosSetting = &connectionLimit + }; - SetProperty(HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerQosProperty, new IntPtr(&qosSettings), (uint)QosInfoSize); + SetProperty(HTTP_SERVER_PROPERTY.HttpServerQosProperty, new IntPtr(&qosSettings), (uint)QosInfoSize); } internal unsafe void SetDelegationProperty(RequestQueue destination) { - var propertyInfo = new HttpApiTypes.HTTP_BINDING_INFO(); - propertyInfo.Flags = HttpApiTypes.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT; - propertyInfo.RequestQueueHandle = destination.Handle.DangerousGetHandle(); + var propertyInfo = new HTTP_BINDING_INFO + { + Flags = HttpApi.HTTP_PROPERTY_FLAGS_PRESENT, + RequestQueueHandle = (HANDLE)destination.Handle.DangerousGetHandle() + }; - SetProperty(HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerDelegationProperty, new IntPtr(&propertyInfo), (uint)RequestPropertyInfoSize); + SetProperty(HTTP_SERVER_PROPERTY.HttpServerDelegationProperty, new IntPtr(&propertyInfo), (uint)RequestPropertyInfoSize); } internal unsafe void UnSetDelegationProperty(RequestQueue destination, bool throwOnError = true) { - var propertyInfo = new HttpApiTypes.HTTP_BINDING_INFO(); - propertyInfo.Flags = HttpApiTypes.HTTP_FLAGS.NONE; - propertyInfo.RequestQueueHandle = destination.Handle.DangerousGetHandle(); + var propertyInfo = new HTTP_BINDING_INFO + { + RequestQueueHandle = (HANDLE)destination.Handle.DangerousGetHandle() + }; - SetProperty(HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerDelegationProperty, new IntPtr(&propertyInfo), (uint)RequestPropertyInfoSize, throwOnError); + SetProperty(HTTP_SERVER_PROPERTY.HttpServerDelegationProperty, new IntPtr(&propertyInfo), (uint)RequestPropertyInfoSize, throwOnError); } - internal void SetProperty(HttpApiTypes.HTTP_SERVER_PROPERTY property, IntPtr info, uint infosize, bool throwOnError = true) + internal unsafe void SetProperty(HTTP_SERVER_PROPERTY property, IntPtr info, uint infosize, bool throwOnError = true) { Debug.Assert(info != IntPtr.Zero, "SetUrlGroupProperty called with invalid pointer"); CheckDisposed(); - var statusCode = HttpApi.HttpSetUrlGroupProperty(Id, property, info, infosize); + var statusCode = PInvoke.HttpSetUrlGroupProperty(Id, property, info.ToPointer(), infosize); - if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) + if (statusCode != ErrorCodes.ERROR_SUCCESS) { var exception = new HttpSysException((int)statusCode); Log.SetUrlPropertyError(_logger, exception); @@ -101,13 +108,15 @@ internal unsafe void AttachToQueue() // Set the association between request queue and url group. After this, requests for registered urls will // get delivered to this request queue. - var info = new HttpApiTypes.HTTP_BINDING_INFO(); - info.Flags = HttpApiTypes.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT; - info.RequestQueueHandle = _requestQueue.Handle.DangerousGetHandle(); + var info = new HTTP_BINDING_INFO + { + Flags = HttpApi.HTTP_PROPERTY_FLAGS_PRESENT, + RequestQueueHandle = (HANDLE)_requestQueue.Handle.DangerousGetHandle() + }; var infoptr = new IntPtr(&info); - SetProperty(HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerBindingProperty, + SetProperty(HTTP_SERVER_PROPERTY.HttpServerBindingProperty, infoptr, (uint)BindingInfoSize); } @@ -120,13 +129,10 @@ internal unsafe void DetachFromQueue() // is fine since http.sys allows to set HttpServerBindingProperty multiple times for valid // Url groups. - var info = new HttpApiTypes.HTTP_BINDING_INFO(); - info.Flags = HttpApiTypes.HTTP_FLAGS.NONE; - info.RequestQueueHandle = IntPtr.Zero; - + var info = new HTTP_BINDING_INFO(); var infoptr = new IntPtr(&info); - SetProperty(HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerBindingProperty, + SetProperty(HTTP_SERVER_PROPERTY.HttpServerBindingProperty, infoptr, (uint)BindingInfoSize, throwOnError: false); } @@ -134,11 +140,10 @@ internal void RegisterPrefix(string uriPrefix, int contextId) { Log.RegisteringPrefix(_logger, uriPrefix); CheckDisposed(); - var statusCode = HttpApi.HttpAddUrlToUrlGroup(Id, uriPrefix, (ulong)contextId, 0); - - if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) + var statusCode = PInvoke.HttpAddUrlToUrlGroup(Id, uriPrefix, (ulong)contextId); + if (statusCode != ErrorCodes.ERROR_SUCCESS) { - if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_ALREADY_EXISTS) + if (statusCode == ErrorCodes.ERROR_ALREADY_EXISTS) { // If we didn't create the queue and the uriPrefix already exists, confirm it exists for the // queue we attached to, if so we are all good, otherwise throw an already registered error. @@ -146,9 +151,8 @@ internal void RegisterPrefix(string uriPrefix, int contextId) { unsafe { - ulong urlGroupId; - var findUrlStatusCode = HttpApi.HttpFindUrlGroupId(uriPrefix, _requestQueue.Handle, &urlGroupId); - if (findUrlStatusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) + var findUrlStatusCode = PInvoke.HttpFindUrlGroupId(uriPrefix, _requestQueue.Handle, out var _); + if (findUrlStatusCode == ErrorCodes.ERROR_SUCCESS) { // Already registered for the desired queue, all good return; @@ -158,7 +162,7 @@ internal void RegisterPrefix(string uriPrefix, int contextId) throw new HttpSysException((int)statusCode, Resources.FormatException_PrefixAlreadyRegistered(uriPrefix)); } - if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_ACCESS_DENIED) + if (statusCode == ErrorCodes.ERROR_ACCESS_DENIED) { throw new HttpSysException((int)statusCode, Resources.FormatException_AccessDenied(uriPrefix, Environment.UserDomainName + @"\" + Environment.UserName)); } @@ -171,7 +175,7 @@ internal void UnregisterPrefix(string uriPrefix) Log.UnregisteringPrefix(_logger, uriPrefix); CheckDisposed(); - HttpApi.HttpRemoveUrlFromUrlGroup(Id, uriPrefix, 0); + PInvoke.HttpRemoveUrlFromUrlGroup(Id, uriPrefix, 0); } public void Dispose() @@ -188,9 +192,9 @@ public void Dispose() Debug.Assert(Id != 0, "HttpCloseUrlGroup called with invalid url group id"); - uint statusCode = HttpApi.HttpCloseUrlGroup(Id); + var statusCode = PInvoke.HttpCloseUrlGroup(Id); - if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) + if (statusCode != ErrorCodes.ERROR_SUCCESS) { Log.CloseUrlGroupError(_logger, statusCode); } diff --git a/src/Servers/HttpSys/src/NativeMethods.txt b/src/Servers/HttpSys/src/NativeMethods.txt new file mode 100644 index 000000000000..548306dfeade --- /dev/null +++ b/src/Servers/HttpSys/src/NativeMethods.txt @@ -0,0 +1,58 @@ +// https://github.com/microsoft/cswin32 +// Listing specific types reduces the size of the final dll. +// Uncomment the next line to import all definitions during development. +// Windows.Win32.Networking.HttpServer +CloseHandle +FILE_SKIP_COMPLETION_PORT_ON_SUCCESS +FILE_SKIP_SET_EVENT_ON_HANDLE +HTTP_AUTH_STATUS +HTTP_BINDING_INFO +HTTP_CACHE_POLICY +HTTP_CONNECTION_LIMIT_INFO +HTTP_COOKED_URL +HTTP_CREATE_REQUEST_QUEUE_FLAG_* +HTTP_DATA_CHUNK +HTTP_FEATURE_ID +HTTP_HEADER_ID +HTTP_KNOWN_HEADER +HTTP_MULTIPLE_KNOWN_HEADERS +HTTP_PROPERTY_FLAGS +HTTP_QOS_SETTING_INFO +HTTP_REQUEST_AUTH_INFO +HTTP_REQUEST_AUTH_TYPE +HTTP_REQUEST_FLAG_* +HTTP_REQUEST_PROPERTY +HTTP_REQUEST_PROPERTY_SNI +HTTP_REQUEST_PROPERTY_SNI_HOST_MAX_LENGTH +HTTP_REQUEST_PROPERTY_STREAM_ERROR +HTTP_REQUEST_V1 +HTTP_REQUEST_V2 +HTTP_RESPONSE_INFO +HTTP_RESPONSE_INFO_FLAGS_PRESERVE_ORDER +HTTP_RESPONSE_INFO_TYPE +HTTP_RESPONSE_V2 +HTTP_SEND_RESPONSE_FLAG_* +HTTP_SERVER_AUTHENTICATION_INFO +HTTP_SERVER_PROPERTY +HTTP_SSL_PROTOCOL_INFO +HTTP_TIMEOUT_LIMIT_INFO +HttpAddUrlToUrlGroup +HTTPAPI_VERSION +HttpCancelHttpRequest +HttpCloseServerSession +HttpCloseUrlGroup +HttpCreateRequestQueue +HttpCreateServerSession +HttpCreateUrlGroup +HttpDelegateRequestEx +HttpFindUrlGroupId +HttpInitialize +HttpIsFeatureSupported +HttpReceiveRequestEntityBody +HttpRemoveUrlFromUrlGroup +HttpSendHttpResponse +HttpSendResponseEntityBody +HttpSetRequestQueueProperty +HttpSetUrlGroupProperty +HttpSetUrlGroupProperty +SetFileCompletionNotificationModes diff --git a/src/Servers/HttpSys/src/RequestProcessing/ClientCertLoader.cs b/src/Servers/HttpSys/src/RequestProcessing/ClientCertLoader.cs index 63ae12d19015..56a5ae4f8fe7 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/ClientCertLoader.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/ClientCertLoader.cs @@ -1,16 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Runtime.InteropServices; using System.Security; -using System.Security.Authentication.ExtendedProtection; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; -using Microsoft.AspNetCore.HttpSys.Internal; -using Microsoft.Extensions.Logging; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -18,14 +15,12 @@ namespace Microsoft.AspNetCore.Server.HttpSys; // failures are handled internally and reported via ClientCertException or ClientCertError. internal sealed unsafe partial class ClientCertLoader : IAsyncResult, IDisposable { - private const uint CertBoblSize = 1500; + private const uint CertBlobSize = 1500; private static readonly IOCompletionCallback IOCallback = new IOCompletionCallback(WaitCallback); - private static readonly int RequestChannelBindStatusSize = - Marshal.SizeOf(); private SafeNativeOverlapped? _overlapped; private byte[]? _backingBuffer; - private HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO* _memoryBlob; + private HTTP_SSL_CLIENT_CERT_INFO* _memoryBlob; private uint _size; private readonly TaskCompletionSource _tcs; private readonly RequestContext _requestContext; @@ -41,7 +36,7 @@ internal ClientCertLoader(RequestContext requestContext, CancellationToken cance _tcs = new TaskCompletionSource(); // we will use this overlapped structure to issue async IO to ul // the event handle will be put in by the BeginHttpApi2.ERROR_SUCCESS() method - Reset(CertBoblSize); + Reset(CertBlobSize); if (cancellationToken.CanBeCanceled) { @@ -102,7 +97,7 @@ private SafeNativeOverlapped? NativeOverlapped } } - private HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO* RequestBlob + private HTTP_SSL_CLIENT_CERT_INFO* RequestBlob { get { @@ -132,7 +127,7 @@ private void Reset(uint size) var boundHandle = RequestContext.Server.RequestQueue.BoundHandle; _overlapped = new SafeNativeOverlapped(boundHandle, boundHandle.AllocateNativeOverlapped(IOCallback, this, _backingBuffer)); - _memoryBlob = (HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO*)Marshal.UnsafeAddrOfPinnedArrayElement(_backingBuffer, 0); + _memoryBlob = (HTTP_SSL_CLIENT_CERT_INFO*)Marshal.UnsafeAddrOfPinnedArrayElement(_backingBuffer, 0); } // When you use netsh to configure HTTP.SYS with clientcertnegotiation = enable @@ -155,42 +150,42 @@ private void Reset(uint size) // HTTP.SYS will not do this for you automatically internal Task LoadClientCertificateAsync() { - uint size = CertBoblSize; + var size = CertBlobSize; bool retry; do { retry = false; uint bytesReceived = 0; - uint statusCode = + var statusCode = HttpApi.HttpReceiveClientCertificate( RequestQueueHandle, RequestContext.Request.UConnectionId, - (uint)HttpApiTypes.HTTP_FLAGS.NONE, + 0u, RequestBlob, size, &bytesReceived, NativeOverlapped!); - if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA) + if (statusCode == ErrorCodes.ERROR_MORE_DATA) { - HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = RequestBlob; + var pClientCertInfo = RequestBlob; size = bytesReceived + pClientCertInfo->CertEncodedSize; Reset(size); retry = true; } - else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND) + else if (statusCode == ErrorCodes.ERROR_NOT_FOUND) { // The client did not send a cert. Complete(0, null); } - else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && + else if (statusCode == ErrorCodes.ERROR_SUCCESS && HttpSysListener.SkipIOCPCallbackOnSuccess) { IOCompleted(statusCode, bytesReceived); } - else if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && - statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) + else if (statusCode != ErrorCodes.ERROR_SUCCESS && + statusCode != ErrorCodes.ERROR_IO_PENDING) { // Some other bad error, possible(?) return values are: // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED @@ -228,16 +223,16 @@ private unsafe void IOCompleted(uint errorCode, uint numBytes) [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Redirected to callback")] private static unsafe void IOCompleted(ClientCertLoader asyncResult, uint errorCode, uint numBytes) { - RequestContext requestContext = asyncResult.RequestContext; + var requestContext = asyncResult.RequestContext; try { - if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA) + if (errorCode == ErrorCodes.ERROR_MORE_DATA) { // There is a bug that has existed in http.sys since w2k3. Bytesreceived will only // return the size of the initial cert structure. To get the full size, // we need to add the certificate encoding size as well. - HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = asyncResult.RequestBlob; + var pClientCertInfo = asyncResult.RequestBlob; asyncResult.Reset(numBytes + pClientCertInfo->CertEncodedSize); uint bytesReceived = 0; @@ -245,31 +240,31 @@ private static unsafe void IOCompleted(ClientCertLoader asyncResult, uint errorC HttpApi.HttpReceiveClientCertificate( requestContext.Server.RequestQueue.Handle, requestContext.Request.UConnectionId, - (uint)HttpApiTypes.HTTP_FLAGS.NONE, + 0u, asyncResult._memoryBlob, asyncResult._size, &bytesReceived, asyncResult._overlapped!); - if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING || - (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && !HttpSysListener.SkipIOCPCallbackOnSuccess)) + if (errorCode == ErrorCodes.ERROR_IO_PENDING || + (errorCode == ErrorCodes.ERROR_SUCCESS && !HttpSysListener.SkipIOCPCallbackOnSuccess)) { return; } } - if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND) + if (errorCode == ErrorCodes.ERROR_NOT_FOUND) { // The client did not send a cert. asyncResult.Complete(0, null); } - else if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) + else if (errorCode != ErrorCodes.ERROR_SUCCESS) { asyncResult.Fail(new HttpSysException((int)errorCode)); } else { - HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = asyncResult._memoryBlob; + var pClientCertInfo = asyncResult._memoryBlob; if (pClientCertInfo == null) { asyncResult.Complete(0, null); @@ -280,7 +275,7 @@ private static unsafe void IOCompleted(ClientCertLoader asyncResult, uint errorC { try { - byte[] certEncoded = new byte[pClientCertInfo->CertEncodedSize]; + var certEncoded = new byte[pClientCertInfo->CertEncodedSize]; Marshal.Copy((IntPtr)pClientCertInfo->pCertEncoded, certEncoded, 0, certEncoded.Length); asyncResult.Complete((int)pClientCertInfo->CertFlags, new X509Certificate2(certEncoded)); } @@ -347,83 +342,4 @@ public bool IsCompleted { get { return _tcs.Task.IsCompleted; } } - - internal static unsafe ChannelBinding? GetChannelBindingFromTls(RequestQueue requestQueue, ulong connectionId, ILogger logger) - { - // +128 since a CBT is usually <128 thus we need to call HRCC just once. If the CBT - // is >128 we will get ERROR_MORE_DATA and call again - int size = RequestChannelBindStatusSize + 128; - - Debug.Assert(size >= 0); - - byte[]? blob = null; - SafeLocalFreeChannelBinding? token = null; - - uint bytesReceived = 0; - uint statusCode; - - do - { - blob = new byte[size]; - fixed (byte* blobPtr = blob) - { - // Http.sys team: ServiceName will always be null if - // HTTP_RECEIVE_SECURE_CHANNEL_TOKEN flag is set. - statusCode = HttpApi.HttpReceiveClientCertificate( - requestQueue.Handle, - connectionId, - (uint)HttpApiTypes.HTTP_FLAGS.HTTP_RECEIVE_SECURE_CHANNEL_TOKEN, - blobPtr, - (uint)size, - &bytesReceived, - SafeNativeOverlapped.Zero); - - if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) - { - int tokenOffset = GetTokenOffsetFromBlob((IntPtr)blobPtr); - int tokenSize = GetTokenSizeFromBlob((IntPtr)blobPtr); - Debug.Assert(tokenSize < Int32.MaxValue); - - token = SafeLocalFreeChannelBinding.LocalAlloc(tokenSize); - - Marshal.Copy(blob, tokenOffset, token.DangerousGetHandle(), tokenSize); - } - else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA) - { - int tokenSize = GetTokenSizeFromBlob((IntPtr)blobPtr); - Debug.Assert(tokenSize < Int32.MaxValue); - - size = RequestChannelBindStatusSize + tokenSize; - } - else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_INVALID_PARAMETER) - { - Log.ChannelBindingUnsupported(logger); - return null; // old schannel library which doesn't support CBT - } - else - { - // It's up to the consumer to fail if the missing ChannelBinding matters to them. - Log.ChannelBindingMissing(logger, new HttpSysException((int)statusCode)); - break; - } - } - } - while (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS); - - return token; - } - - private static int GetTokenOffsetFromBlob(IntPtr blob) - { - Debug.Assert(blob != IntPtr.Zero); - IntPtr tokenPointer = Marshal.ReadIntPtr(blob, (int)Marshal.OffsetOf("ChannelToken")); - Debug.Assert(tokenPointer != IntPtr.Zero); - return (int)IntPtrHelper.Subtract(tokenPointer, blob); - } - - private static int GetTokenSizeFromBlob(IntPtr blob) - { - Debug.Assert(blob != IntPtr.Zero); - return Marshal.ReadInt32(blob, (int)Marshal.OffsetOf("ChannelTokenSize")); - } } diff --git a/src/Servers/HttpSys/src/RequestProcessing/Request.cs b/src/Servers/HttpSys/src/RequestProcessing/Request.cs index 0b9418cbcac6..290218a7e3b8 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/Request.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/Request.cs @@ -13,6 +13,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -61,12 +62,12 @@ internal Request(RequestContext requestContext) var prefix = requestContext.Server.Options.UrlPrefixes.GetPrefix((int)requestContext.UrlContext); // 'OPTIONS * HTTP/1.1' - if (KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawUrl, "*", StringComparison.Ordinal)) + if (KnownMethod == HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawUrl, "*", StringComparison.Ordinal)) { PathBase = string.Empty; Path = string.Empty; } - // Prefix may be null if the requested has been transfered to our queue + // Prefix may be null if the requested has been transferred to our queue else if (prefix is not null) { var pathBase = prefix.PathWithoutTrailingSlash; @@ -230,9 +231,9 @@ public long? ContentLength public RequestHeaders Headers { get; } - internal HttpApiTypes.HTTP_VERB KnownMethod { get; } + internal HTTP_VERB KnownMethod { get; } - internal bool IsHeadMethod => KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbHEAD; + internal bool IsHeadMethod => KnownMethod == HTTP_VERB.HttpVerbHEAD; public string Method { get; } @@ -343,16 +344,16 @@ private AspNetCore.HttpSys.Internal.SocketAddress LocalEndPoint private void GetTlsHandshakeResults() { var handshake = RequestContext.GetTlsHandshake(); - Protocol = handshake.Protocol; - CipherAlgorithm = handshake.CipherType; + Protocol = (SslProtocols)handshake.Protocol; + CipherAlgorithm = (CipherAlgorithmType)handshake.CipherType; CipherStrength = (int)handshake.CipherStrength; - HashAlgorithm = handshake.HashType; + HashAlgorithm = (HashAlgorithmType)handshake.HashType; HashStrength = (int)handshake.HashStrength; - KeyExchangeAlgorithm = handshake.KeyExchangeType; + KeyExchangeAlgorithm = (ExchangeAlgorithmType)handshake.KeyExchangeType; KeyExchangeStrength = (int)handshake.KeyExchangeStrength; var sni = RequestContext.GetClientSni(); - SniHostName = sni.Hostname; + SniHostName = sni.Hostname.ToString(); } public X509Certificate2? ClientCertificate diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs index 8da0c7a33e37..9faaab9e831d 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs @@ -1,13 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; using System.Runtime.InteropServices; -using System.Security.Authentication.ExtendedProtection; using System.Security.Principal; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.Extensions.Logging; +using Windows.Win32; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -110,22 +110,6 @@ public Task UpgradeAsync() return Task.FromResult(opaqueStream); } - // TODO: Public when needed - internal bool TryGetChannelBinding(ref ChannelBinding? value) - { - if (!Request.IsHttps) - { - Log.ChannelBindingNeedsHttps(Logger); - return false; - } - - value = ClientCertLoader.GetChannelBindingFromTls(Server.RequestQueue, Request.UConnectionId, Logger); - - Debug.Assert(value != null, "GetChannelBindingFromTls returned null even though OS supposedly supports Extended Protection"); - Log.ChannelBindingRetrieved(Logger); - return value != null; - } - /// /// Flushes and completes the response. /// @@ -208,13 +192,13 @@ internal void ForceCancelRequest() { try { - var statusCode = HttpApi.HttpCancelHttpRequest(Server.RequestQueue.Handle, - Request.RequestId, IntPtr.Zero); + var statusCode = PInvoke.HttpCancelHttpRequest(Server.RequestQueue.Handle, + Request.RequestId, default); // Either the connection has already dropped, or the last write is in progress. // The requestId becomes invalid as soon as the last Content-Length write starts. // The only way to cancel now is with CancelIoEx. - if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_CONNECTION_INVALID) + if (statusCode == ErrorCodes.ERROR_CONNECTION_INVALID) { Response.CancelLastWrite(); } @@ -225,7 +209,7 @@ internal void ForceCancelRequest() } } - internal unsafe HttpApiTypes.HTTP_REQUEST_PROPERTY_SNI GetClientSni() + internal unsafe HTTP_REQUEST_PROPERTY_SNI GetClientSni() { if (HttpApi.HttpGetRequestProperty != null) { @@ -235,7 +219,7 @@ internal unsafe HttpApiTypes.HTTP_REQUEST_PROPERTY_SNI GetClientSni() var statusCode = HttpApi.HttpGetRequestProperty( Server.RequestQueue.Handle, RequestId, - HttpApiTypes.HTTP_REQUEST_PROPERTY.HttpRequestPropertySni, + HTTP_REQUEST_PROPERTY.HttpRequestPropertySni, qualifier: null, qualifierSize: 0, (void*)pBuffer, @@ -243,9 +227,9 @@ internal unsafe HttpApiTypes.HTTP_REQUEST_PROPERTY_SNI GetClientSni() bytesReturned: null, IntPtr.Zero); - if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) + if (statusCode == ErrorCodes.ERROR_SUCCESS) { - return Marshal.PtrToStructure((IntPtr)pBuffer); + return Marshal.PtrToStructure((IntPtr)pBuffer); } } } @@ -263,9 +247,9 @@ internal unsafe void SetResetCode(int errorCode) 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); + var streamError = new HTTP_REQUEST_PROPERTY_STREAM_ERROR() { ErrorCode = (uint)errorCode }; + var statusCode = HttpApi.HttpSetRequestProperty(Server.RequestQueue.Handle, Request.RequestId, HTTP_REQUEST_PROPERTY.HttpRequestPropertyStreamError, &streamError, + (uint)sizeof(HTTP_REQUEST_PROPERTY_STREAM_ERROR), IntPtr.Zero); } catch (ObjectDisposedException) { @@ -307,25 +291,25 @@ internal unsafe void Delegate(DelegationRule destination) fixed (char* uriPointer = destination.UrlPrefix) { - var property = new HttpApiTypes.HTTP_DELEGATE_REQUEST_PROPERTY_INFO() + var property = new HTTP_DELEGATE_REQUEST_PROPERTY_INFO() { - PropertyId = HttpApiTypes.HTTP_DELEGATE_REQUEST_PROPERTY_ID.DelegateRequestDelegateUrlProperty, - PropertyInfo = (IntPtr)uriPointer, + PropertyId = HTTP_DELEGATE_REQUEST_PROPERTY_ID.DelegateRequestDelegateUrlProperty, + PropertyInfo = uriPointer, PropertyInfoLength = (uint)System.Text.Encoding.Unicode.GetByteCount(destination.UrlPrefix) }; // Passing 0 for delegateUrlGroupId allows http.sys to find the right group for the // URL passed in via the property above. If we passed in the receiver's URL group id // instead of 0, then delegation would fail if the receiver restarted. - statusCode = HttpApi.HttpDelegateRequestEx(source.Handle, + statusCode = PInvoke.HttpDelegateRequestEx(source.Handle, destination.Queue.Handle, Request.RequestId, - delegateUrlGroupId: 0, - propertyInfoSetSize: 1, - &property); + DelegateUrlGroupId: 0, + PropertyInfoSetSize: 1, + property); } - if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) + if (statusCode != ErrorCodes.ERROR_SUCCESS) { throw new HttpSysException((int)statusCode); } diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestStream.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestStream.cs index 018877e8b271..f41b04fc0b1f 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestStream.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestStream.cs @@ -4,8 +4,8 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.Extensions.Logging; +using Windows.Win32; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -133,26 +133,25 @@ public override unsafe int Read([In, Out] byte[] buffer, int offset, int size) uint flags = 0; - statusCode = - HttpApi.HttpReceiveRequestEntityBody( - RequestQueueHandle, - RequestId, - flags, - (IntPtr)(pBuffer + offset), - (uint)size, - out extraDataRead, - SafeNativeOverlapped.Zero); + statusCode = PInvoke.HttpReceiveRequestEntityBody( + RequestQueueHandle, + RequestId, + flags, + (pBuffer + offset), + (uint)size, + &extraDataRead, + default); dataRead += extraDataRead; } // Zero-byte reads - if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA && size == 0) + if (statusCode == ErrorCodes.ERROR_MORE_DATA && size == 0) { // extraDataRead returns 1 to let us know there's data available. Don't count it against the request body size yet. dataRead = 0; } - else if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) + else if (statusCode != ErrorCodes.ERROR_SUCCESS && statusCode != ErrorCodes.ERROR_HANDLE_EOF) { Exception exception = new IOException(string.Empty, new HttpSysException((int)statusCode)); Log.ErrorWhileRead(Logger, exception); @@ -172,8 +171,8 @@ public override unsafe int Read([In, Out] byte[] buffer, int offset, int size) internal void UpdateAfterRead(uint statusCode, uint dataRead) { - if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF - || statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA && dataRead == 0) + if (statusCode == ErrorCodes.ERROR_HANDLE_EOF + || statusCode != ErrorCodes.ERROR_MORE_DATA && dataRead == 0) { Dispose(); } @@ -208,7 +207,7 @@ public override unsafe Task ReadAsync(byte[] buffer, int offset, int size, dataRead = _requestContext.Request.GetChunks(ref _dataChunkIndex, ref _dataChunkOffset, buffer, offset, size); if (dataRead > 0) { - UpdateAfterRead(UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS, dataRead); + UpdateAfterRead(ErrorCodes.ERROR_SUCCESS, dataRead); if (TryCheckSizeLimit((int)dataRead, out var exception)) { return Task.FromException(exception); @@ -259,10 +258,10 @@ public override unsafe Task ReadAsync(byte[] buffer, int offset, int size, throw; } - if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) + if (statusCode != ErrorCodes.ERROR_SUCCESS && statusCode != ErrorCodes.ERROR_IO_PENDING) { asyncResult.Dispose(); - if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) + if (statusCode == ErrorCodes.ERROR_HANDLE_EOF) { uint totalRead = dataRead + bytesReturned; UpdateAfterRead(statusCode, totalRead); @@ -281,7 +280,7 @@ public override unsafe Task ReadAsync(byte[] buffer, int offset, int size, throw exception; } } - else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && + else if (statusCode == ErrorCodes.ERROR_SUCCESS && HttpSysListener.SkipIOCPCallbackOnSuccess) { // IO operation completed synchronously - callback won't be called to signal completion. diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestStreamAsyncResult.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestStreamAsyncResult.cs index e34111ba08c3..f5cc5726992d 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestStreamAsyncResult.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestStreamAsyncResult.cs @@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using Microsoft.AspNetCore.HttpSys.Internal; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -70,12 +69,12 @@ private static void IOCompleted(RequestStreamAsyncResult asyncResult, uint error try { // Zero-byte reads - if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA && asyncResult._size == 0) + if (errorCode == ErrorCodes.ERROR_MORE_DATA && asyncResult._size == 0) { // numBytes returns 1 to let us know there's data available. Don't count it against the request body size yet. asyncResult.Complete(0, errorCode); } - else if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) + else if (errorCode != ErrorCodes.ERROR_SUCCESS && errorCode != ErrorCodes.ERROR_HANDLE_EOF) { asyncResult.Fail(new IOException(string.Empty, new HttpSysException((int)errorCode))); } @@ -97,7 +96,7 @@ private static unsafe void Callback(uint errorCode, uint numBytes, NativeOverlap IOCompleted(asyncResult, errorCode, numBytes); } - internal void Complete(int read, uint errorCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) + internal void Complete(int read, uint errorCode = ErrorCodes.ERROR_SUCCESS) { if (_requestStream.TryCheckSizeLimit(read + (int)DataAlreadyRead, out var exception)) { diff --git a/src/Servers/HttpSys/src/RequestProcessing/Response.cs b/src/Servers/HttpSys/src/RequestProcessing/Response.cs index b10de647511d..7a64ec049cd6 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/Response.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/Response.cs @@ -10,8 +10,9 @@ using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; - -using static Microsoft.AspNetCore.HttpSys.Internal.UnsafeNclNativeMethods; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -28,7 +29,7 @@ internal sealed class Response private TimeSpan? _cacheTtl; private long _expectedBodyLength; private BoundaryType _boundaryType; - private HttpApiTypes.HTTP_RESPONSE_V2 _nativeResponse; + private HTTP_RESPONSE_V2 _nativeResponse; private HeaderCollection? _trailers; internal Response(RequestContext requestContext) @@ -38,14 +39,14 @@ internal Response(RequestContext requestContext) Headers = new HeaderCollection(); // We haven't started yet, or we're just buffered, we can clear any data, headers, and state so // that we can start over (e.g. to write an error message). - _nativeResponse = new HttpApiTypes.HTTP_RESPONSE_V2(); + _nativeResponse = new HTTP_RESPONSE_V2(); Headers.IsReadOnly = false; Headers.Clear(); _reasonPhrase = null; _boundaryType = BoundaryType.None; - _nativeResponse.Response_V1.StatusCode = (ushort)StatusCodes.Status200OK; - _nativeResponse.Response_V1.Version.MajorVersion = 1; - _nativeResponse.Response_V1.Version.MinorVersion = 1; + _nativeResponse.Base.StatusCode = (ushort)StatusCodes.Status200OK; + _nativeResponse.Base.Version.MajorVersion = 1; + _nativeResponse.Base.Version.MinorVersion = 1; _responseState = ResponseState.Created; _expectedBodyLength = 0; _nativeStream = null; @@ -67,7 +68,7 @@ private enum ResponseState public int StatusCode { - get { return _nativeResponse.Response_V1.StatusCode; } + get { return _nativeResponse.Base.StatusCode; } set { // Http.Sys automatically sends 100 Continue responses when you read from the request body. @@ -76,7 +77,7 @@ public int StatusCode throw new ArgumentOutOfRangeException(nameof(value), value, string.Format(CultureInfo.CurrentCulture, Resources.Exception_InvalidStatusCode, value)); } CheckResponseStarted(); - _nativeResponse.Response_V1.StatusCode = (ushort)value; + _nativeResponse.Base.StatusCode = (ushort)value; } } @@ -118,7 +119,7 @@ public AuthenticationSchemes AuthenticationChallenges private string GetReasonPhrase(int statusCode) { - string? reasonPhrase = ReasonPhrase; + var reasonPhrase = ReasonPhrase; if (string.IsNullOrWhiteSpace(reasonPhrase)) { // If the user hasn't set this then it is generated on the fly if possible. @@ -132,7 +133,7 @@ private string GetReasonPhrase(int statusCode) private static bool CanSendResponseBody(int responseCode) { - for (int i = 0; i < StatusWithNoResponseBody.Length; i++) + for (var i = 0; i < StatusWithNoResponseBody.Length; i++) { if (responseCode == StatusWithNoResponseBody[i]) { @@ -276,9 +277,9 @@ actual HTTP header will be generated by the application and sent as entity body. // It may also be faster to do this work in managed code and then pass down only one buffer. // What would we loose by bypassing HttpSendHttpResponse? internal unsafe uint SendHeaders(ref UnmanagedBufferAllocator allocator, - Span dataChunks, + Span dataChunks, ResponseStreamAsyncResult? asyncResult, - HttpApiTypes.HTTP_FLAGS flags, + uint flags, bool isOpaqueUpgrade) { Debug.Assert(!HasStarted, "HttpListenerResponse::SendHeaders()|SentHeaders is true."); @@ -291,42 +292,42 @@ internal unsafe uint SendHeaders(ref UnmanagedBufferAllocator allocator, SerializeHeaders(ref allocator, isOpaqueUpgrade); - fixed (HttpApiTypes.HTTP_DATA_CHUNK* chunks = dataChunks) + fixed (HTTP_DATA_CHUNK* chunks = dataChunks) { if (chunks != null) { - _nativeResponse.Response_V1.EntityChunkCount = checked((ushort)dataChunks.Length); - _nativeResponse.Response_V1.pEntityChunks = chunks; + _nativeResponse.Base.EntityChunkCount = checked((ushort)dataChunks.Length); + _nativeResponse.Base.pEntityChunks = chunks; } else if (asyncResult != null && asyncResult.DataChunks != null) { - _nativeResponse.Response_V1.EntityChunkCount = asyncResult.DataChunkCount; - _nativeResponse.Response_V1.pEntityChunks = asyncResult.DataChunks; + _nativeResponse.Base.EntityChunkCount = asyncResult.DataChunkCount; + _nativeResponse.Base.pEntityChunks = asyncResult.DataChunks; } else { - _nativeResponse.Response_V1.EntityChunkCount = 0; - _nativeResponse.Response_V1.pEntityChunks = null; + _nativeResponse.Base.EntityChunkCount = 0; + _nativeResponse.Base.pEntityChunks = null; } - var cachePolicy = new HttpApiTypes.HTTP_CACHE_POLICY(); + var cachePolicy = new HTTP_CACHE_POLICY(); if (_cacheTtl.HasValue && _cacheTtl.Value > TimeSpan.Zero) { - cachePolicy.Policy = HttpApiTypes.HTTP_CACHE_POLICY_TYPE.HttpCachePolicyTimeToLive; + cachePolicy.Policy = HTTP_CACHE_POLICY_TYPE.HttpCachePolicyTimeToLive; cachePolicy.SecondsToLive = (uint)Math.Min(_cacheTtl.Value.Ticks / TimeSpan.TicksPerSecond, Int32.MaxValue); } - byte* pReasonPhrase = allocator.GetHeaderEncodedBytes(reasonPhrase, out int pReasonPhraseLength); - _nativeResponse.Response_V1.ReasonLength = checked((ushort)pReasonPhraseLength); - _nativeResponse.Response_V1.pReason = pReasonPhrase; + var pReasonPhrase = allocator.GetHeaderEncodedBytes(reasonPhrase, out var pReasonPhraseLength); + _nativeResponse.Base.ReasonLength = checked((ushort)pReasonPhraseLength); + _nativeResponse.Base.pReason = (PCSTR)pReasonPhrase; - fixed (HttpApiTypes.HTTP_RESPONSE_V2* pResponse = &_nativeResponse) + fixed (HTTP_RESPONSE_V2* pResponse = &_nativeResponse) { statusCode = HttpApi.HttpSendHttpResponse( RequestContext.Server.RequestQueue.Handle, Request.RequestId, - (uint)flags, + flags, pResponse, &cachePolicy, &bytesSent, @@ -337,14 +338,14 @@ internal unsafe uint SendHeaders(ref UnmanagedBufferAllocator allocator, // GoAway is only supported on later versions. Retry. if (statusCode == ErrorCodes.ERROR_INVALID_PARAMETER - && (flags & HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_GOAWAY) != 0) + && (flags & PInvoke.HTTP_SEND_RESPONSE_FLAG_GOAWAY) != 0) { - flags &= ~HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_GOAWAY; + flags &= ~PInvoke.HTTP_SEND_RESPONSE_FLAG_GOAWAY; statusCode = HttpApi.HttpSendHttpResponse( RequestContext.Server.RequestQueue.Handle, Request.RequestId, - (uint)flags, + flags, pResponse, &cachePolicy, &bytesSent, @@ -373,15 +374,15 @@ internal unsafe uint SendHeaders(ref UnmanagedBufferAllocator allocator, return statusCode; } - internal HttpApiTypes.HTTP_FLAGS ComputeHeaders(long writeCount, bool endOfRequest = false) + internal uint ComputeHeaders(long writeCount, bool endOfRequest = false) { Headers.IsReadOnly = false; // Temporarily unlock - if (StatusCode == (ushort)StatusCodes.Status401Unauthorized) + if (StatusCode == StatusCodes.Status401Unauthorized) { AuthenticationManager.SetAuthenticationChallenge(RequestContext); } - var flags = HttpApiTypes.HTTP_FLAGS.NONE; + var flags = 0u; Debug.Assert(!HasComputedHeaders, nameof(HasComputedHeaders) + " is true."); _responseState = ResponseState.ComputedHeaders; @@ -459,10 +460,10 @@ internal HttpApiTypes.HTTP_FLAGS ComputeHeaders(long writeCount, bool endOfReque { Headers.Append(HeaderNames.Connection, Constants.Close); } - flags = HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT; + flags = PInvoke.HTTP_SEND_RESPONSE_FLAG_DISCONNECT; if (responseCloseSet && requestVersion >= Constants.V2 && SupportsGoAway) { - flags |= HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_GOAWAY; + flags |= PInvoke.HTTP_SEND_RESPONSE_FLAG_GOAWAY; } } @@ -478,117 +479,113 @@ private static bool Matches(string knownValue, StringValues input) private unsafe void SerializeHeaders(ref UnmanagedBufferAllocator allocator, bool isOpaqueUpgrade) { Headers.IsReadOnly = true; // Prohibit further modifications. - Span unknownHeaders = default; - Span knownHeaderInfo = default; + Span unknownHeaders = default; + Span knownHeaderInfo = default; if (Headers.Count == 0) { return; } - string headerName; + string headerValue; int lookup; byte* bytes; int bytesLength; - int numUnknownHeaders = 0; - int numKnownMultiHeaders = 0; - foreach (var headerPair in Headers) + var numUnknownHeaders = 0; + var numKnownMultiHeaders = 0; + foreach (var (headerName, headerValues) in Headers) { - if (headerPair.Value.Count == 0) + if (headerValues.Count == 0) { continue; } // See if this is an unknown header - lookup = HttpApiTypes.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerPair.Key); - // Http.Sys doesn't let us send the Connection: Upgrade header as a Known header. - if (lookup == -1 || - (isOpaqueUpgrade && lookup == (int)HttpApiTypes.HTTP_RESPONSE_HEADER_ID.Enum.HttpHeaderConnection)) + if (!HttpApiTypes.KnownResponseHeaders.TryGetValue(headerName, out lookup) || + (isOpaqueUpgrade && lookup == (int)HTTP_HEADER_ID.HttpHeaderConnection)) { - numUnknownHeaders += headerPair.Value.Count; + numUnknownHeaders += headerValues.Count; } - else if (headerPair.Value.Count > 1) + else if (headerValues.Count > 1) { numKnownMultiHeaders++; } // else known single-value header. } - fixed (HttpApiTypes.HTTP_KNOWN_HEADER* pKnownHeaders = &_nativeResponse.Response_V1.Headers.KnownHeaders) + fixed (__HTTP_KNOWN_HEADER_30* pKnownHeaders = &_nativeResponse.Base.Headers.KnownHeaders) { - foreach (var headerPair in Headers) + var knownHeaders = pKnownHeaders->AsSpan(); + foreach (var (headerName, headerValues) in Headers) { - if (headerPair.Value.Count == 0) + if (headerValues.Count == 0) { continue; } - headerName = headerPair.Key; - StringValues headerValues = headerPair.Value; - lookup = HttpApiTypes.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerName); // Http.Sys doesn't let us send the Connection: Upgrade header as a Known header. - if (lookup == -1 || - (isOpaqueUpgrade && lookup == (int)HttpApiTypes.HTTP_RESPONSE_HEADER_ID.Enum.HttpHeaderConnection)) + if (!HttpApiTypes.KnownResponseHeaders.TryGetValue(headerName, out lookup) || + (isOpaqueUpgrade && lookup == (int)HTTP_HEADER_ID.HttpHeaderConnection)) { if (unknownHeaders == null) { - HttpApiTypes.HTTP_UNKNOWN_HEADER* unknownAlloc = allocator.AllocAsPointer(numUnknownHeaders); - unknownHeaders = new Span(unknownAlloc, numUnknownHeaders); - _nativeResponse.Response_V1.Headers.pUnknownHeaders = unknownAlloc; + var unknownAlloc = allocator.AllocAsPointer(numUnknownHeaders); + unknownHeaders = new Span(unknownAlloc, numUnknownHeaders); + _nativeResponse.Base.Headers.pUnknownHeaders = unknownAlloc; } - for (int headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++) + for (var headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++) { // Add Name bytes = allocator.GetHeaderEncodedBytes(headerName, out bytesLength); - unknownHeaders[_nativeResponse.Response_V1.Headers.UnknownHeaderCount].NameLength = checked((ushort)bytesLength); - unknownHeaders[_nativeResponse.Response_V1.Headers.UnknownHeaderCount].pName = bytes; + unknownHeaders[_nativeResponse.Base.Headers.UnknownHeaderCount].NameLength = checked((ushort)bytesLength); + unknownHeaders[_nativeResponse.Base.Headers.UnknownHeaderCount].pName = (PCSTR)bytes; // Add Value headerValue = headerValues[headerValueIndex] ?? string.Empty; bytes = allocator.GetHeaderEncodedBytes(headerValue, out bytesLength); - unknownHeaders[_nativeResponse.Response_V1.Headers.UnknownHeaderCount].RawValueLength = checked((ushort)bytesLength); - unknownHeaders[_nativeResponse.Response_V1.Headers.UnknownHeaderCount].pRawValue = bytes; - _nativeResponse.Response_V1.Headers.UnknownHeaderCount++; + unknownHeaders[_nativeResponse.Base.Headers.UnknownHeaderCount].RawValueLength = checked((ushort)bytesLength); + unknownHeaders[_nativeResponse.Base.Headers.UnknownHeaderCount].pRawValue = (PCSTR)bytes; + _nativeResponse.Base.Headers.UnknownHeaderCount++; } } - else if (headerPair.Value.Count == 1) + else if (headerValues.Count == 1) { headerValue = headerValues[0] ?? string.Empty; bytes = allocator.GetHeaderEncodedBytes(headerValue, out bytesLength); - pKnownHeaders[lookup].RawValueLength = checked((ushort)bytesLength); - pKnownHeaders[lookup].pRawValue = bytes; + knownHeaders[lookup].RawValueLength = checked((ushort)bytesLength); + knownHeaders[lookup].pRawValue = (PCSTR)bytes; } else { if (knownHeaderInfo == null) { - HttpApiTypes.HTTP_RESPONSE_INFO* responseAlloc = allocator.AllocAsPointer(numKnownMultiHeaders); - knownHeaderInfo = new Span(responseAlloc, numKnownMultiHeaders); + var responseAlloc = allocator.AllocAsPointer(numKnownMultiHeaders); + knownHeaderInfo = new Span(responseAlloc, numKnownMultiHeaders); _nativeResponse.pResponseInfo = responseAlloc; } - knownHeaderInfo[_nativeResponse.ResponseInfoCount].Type = HttpApiTypes.HTTP_RESPONSE_INFO_TYPE.HttpResponseInfoTypeMultipleKnownHeaders; - knownHeaderInfo[_nativeResponse.ResponseInfoCount].Length = (uint)sizeof(HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS); + knownHeaderInfo[_nativeResponse.ResponseInfoCount].Type = HTTP_RESPONSE_INFO_TYPE.HttpResponseInfoTypeMultipleKnownHeaders; + knownHeaderInfo[_nativeResponse.ResponseInfoCount].Length = (uint)sizeof(HTTP_MULTIPLE_KNOWN_HEADERS); - HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS* header = allocator.AllocAsPointer(1); + var header = allocator.AllocAsPointer(1); - header->HeaderId = (HttpApiTypes.HTTP_RESPONSE_HEADER_ID.Enum)lookup; - header->Flags = HttpApiTypes.HTTP_RESPONSE_INFO_FLAGS.PreserveOrder; // TODO: The docs say this is for www-auth only. + header->HeaderId = (HTTP_HEADER_ID)lookup; + header->Flags = PInvoke.HTTP_RESPONSE_INFO_FLAGS_PRESERVE_ORDER; // The docs say this is for www-auth only. header->KnownHeaderCount = 0; - HttpApiTypes.HTTP_KNOWN_HEADER* headerAlloc = allocator.AllocAsPointer(headerValues.Count); - var nativeHeaderValues = new Span(headerAlloc, headerValues.Count); + var headerAlloc = allocator.AllocAsPointer(headerValues.Count); + var nativeHeaderValues = new Span(headerAlloc, headerValues.Count); header->KnownHeaders = headerAlloc; - for (int headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++) + for (var headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++) { // Add Value headerValue = headerValues[headerValueIndex] ?? string.Empty; bytes = allocator.GetHeaderEncodedBytes(headerValue, out bytesLength); nativeHeaderValues[header->KnownHeaderCount].RawValueLength = checked((ushort)bytesLength); - nativeHeaderValues[header->KnownHeaderCount].pRawValue = bytes; + nativeHeaderValues[header->KnownHeaderCount].pRawValue = (PCSTR)bytes; header->KnownHeaderCount++; } @@ -600,7 +597,7 @@ private unsafe void SerializeHeaders(ref UnmanagedBufferAllocator allocator, boo } } - internal unsafe void SerializeTrailers(ref UnmanagedBufferAllocator allocator, out HttpApiTypes.HTTP_DATA_CHUNK dataChunk) + internal unsafe void SerializeTrailers(ref UnmanagedBufferAllocator allocator, out HTTP_DATA_CHUNK dataChunk) { Debug.Assert(HasTrailers); MakeTrailersReadOnly(); @@ -611,14 +608,14 @@ internal unsafe void SerializeTrailers(ref UnmanagedBufferAllocator allocator, o trailerCount += trailerPair.Value.Count; } - var unknownHeaders = allocator.AllocAsPointer(trailerCount); + var unknownHeaders = allocator.AllocAsPointer(trailerCount); // Since the dataChunk instance represents an unmanaged union, we are only setting a subset of // overlapping fields. In order to make this clear here, we use Unsafe.SkipInit(). Unsafe.SkipInit(out dataChunk); - dataChunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkTrailers; - dataChunk.trailers.trailerCount = checked((ushort)trailerCount); - dataChunk.trailers.pTrailers = (nint)unknownHeaders; + dataChunk.DataChunkType = HTTP_DATA_CHUNK_TYPE.HttpDataChunkTrailers; + dataChunk.Anonymous.Trailers.TrailerCount = checked((ushort)trailerCount); + dataChunk.Anonymous.Trailers.pTrailers = unknownHeaders; var unknownHeadersOffset = 0; @@ -632,18 +629,18 @@ internal unsafe void SerializeTrailers(ref UnmanagedBufferAllocator allocator, o var headerName = headerPair.Key; var headerValues = headerPair.Value; - for (int headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++) + for (var headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++) { // Add Name - var bytes = allocator.GetHeaderEncodedBytes(headerName, out int bytesLength); + var bytes = allocator.GetHeaderEncodedBytes(headerName, out var bytesLength); unknownHeaders[unknownHeadersOffset].NameLength = checked((ushort)bytesLength); - unknownHeaders[unknownHeadersOffset].pName = bytes; + unknownHeaders[unknownHeadersOffset].pName = (PCSTR)bytes; // Add Value var headerValue = headerValues[headerValueIndex] ?? string.Empty; bytes = allocator.GetHeaderEncodedBytes(headerValue, out bytesLength); unknownHeaders[unknownHeadersOffset].RawValueLength = checked((ushort)bytesLength); - unknownHeaders[unknownHeadersOffset].pRawValue = bytes; + unknownHeaders[unknownHeadersOffset].pRawValue = (PCSTR)bytes; unknownHeadersOffset++; } } @@ -661,9 +658,9 @@ internal void SendOpaqueUpgrade() { // TODO: Send headers async? ulong errorCode = SendHeaders(ref allocator, null, null, - HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_OPAQUE | - HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA | - HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA, + PInvoke.HTTP_SEND_RESPONSE_FLAG_OPAQUE | + PInvoke.HTTP_SEND_RESPONSE_FLAG_MORE_DATA | + PInvoke.HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA, true); if (errorCode != ErrorCodes.ERROR_SUCCESS) diff --git a/src/Servers/HttpSys/src/RequestProcessing/ResponseBody.cs b/src/Servers/HttpSys/src/RequestProcessing/ResponseBody.cs index b848318e6b5d..8bb2f555e900 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/ResponseBody.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/ResponseBody.cs @@ -3,9 +3,9 @@ using System.Diagnostics; using System.Runtime.InteropServices; -using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.Extensions.Logging; -using static Microsoft.AspNetCore.HttpSys.Internal.UnsafeNclNativeMethods; +using Windows.Win32; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -144,28 +144,21 @@ public void MarkDelegated() Span pinnedBuffers = default; try { - Span dataChunks; - BuildDataChunks(ref allocator, endOfRequest, data, out dataChunks, out pinnedBuffers); + BuildDataChunks(ref allocator, endOfRequest, data, out var dataChunks, out pinnedBuffers); if (!started) { statusCode = _requestContext.Response.SendHeaders(ref allocator, dataChunks, null, flags, false); } else { - fixed (HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks = dataChunks) - { - statusCode = HttpApi.HttpSendResponseEntityBody( - RequestQueueHandle, - RequestId, - (uint)flags, - (ushort)dataChunks.Length, - pDataChunks, - null, - IntPtr.Zero, - 0, - SafeNativeOverlapped.Zero, - IntPtr.Zero); - } + statusCode = PInvoke.HttpSendResponseEntityBody( + RequestQueueHandle, + RequestId, + flags, + dataChunks, + null, + null, + null); } } finally @@ -194,7 +187,7 @@ public void MarkDelegated() } } - private unsafe void BuildDataChunks(scoped ref UnmanagedBufferAllocator allocator, bool endOfRequest, ArraySegment data, out Span dataChunks, out Span pins) + private unsafe void BuildDataChunks(scoped ref UnmanagedBufferAllocator allocator, bool endOfRequest, ArraySegment data, out Span dataChunks, out Span pins) { var hasData = data.Count > 0; var chunked = _requestContext.Response.BoundaryType == BoundaryType.Chunked; @@ -206,7 +199,7 @@ private unsafe void BuildDataChunks(scoped ref UnmanagedBufferAllocator allocato // Figure out how many data chunks if (chunked && !hasData && endOfRequest) { - dataChunks = allocator.AllocAsSpan(1); + dataChunks = allocator.AllocAsSpan(1); SetDataChunkWithPinnedData(dataChunks, ref currentChunk, Helpers.ChunkTerminator); pins = default; return; @@ -244,7 +237,7 @@ private unsafe void BuildDataChunks(scoped ref UnmanagedBufferAllocator allocato // Manually initialize the allocated GCHandles pins.Clear(); - dataChunks = allocator.AllocAsSpan(chunkCount); + dataChunks = allocator.AllocAsSpan(chunkCount); if (chunked) { @@ -280,7 +273,7 @@ private unsafe void BuildDataChunks(scoped ref UnmanagedBufferAllocator allocato } private static unsafe void SetDataChunk( - Span chunks, + Span chunks, ref int chunkIndex, ArraySegment buffer, out GCHandle handle) @@ -290,17 +283,17 @@ private static unsafe void SetDataChunk( } private static unsafe void SetDataChunkWithPinnedData( - Span chunks, + Span chunks, ref int chunkIndex, ReadOnlySpan bytes) { ref var chunk = ref chunks[chunkIndex++]; - chunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; + chunk.DataChunkType = HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; fixed (byte* ptr = bytes) { - chunk.fromMemory.pBuffer = (IntPtr)ptr; + chunk.Anonymous.FromMemory.pBuffer = ptr; } - chunk.fromMemory.BufferLength = (uint)bytes.Length; + chunk.Anonymous.FromMemory.BufferLength = (uint)bytes.Length; } private static void FreeDataBuffers(Span pinnedBuffers) @@ -364,7 +357,7 @@ private unsafe Task FlushInternalAsync(ArraySegment data, CancellationToke statusCode = HttpApi.HttpSendResponseEntityBody( RequestQueueHandle, RequestId, - (uint)flags, + flags, asyncResult.DataChunkCount, asyncResult.DataChunks, &bytesSent, @@ -416,7 +409,7 @@ private unsafe Task FlushInternalAsync(ArraySegment data, CancellationToke } // Last write, cache it for special cancellation handling. - if ((flags & HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0) + if ((flags & PInvoke.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0) { _lastWrite = asyncResult; } @@ -466,9 +459,9 @@ internal void Abort(bool dispose = true) _requestContext.Abort(); } - private HttpApiTypes.HTTP_FLAGS ComputeLeftToWrite(long writeCount, bool endOfRequest = false) + private uint ComputeLeftToWrite(long writeCount, bool endOfRequest = false) { - var flags = HttpApiTypes.HTTP_FLAGS.NONE; + var flags = 0u; if (!_requestContext.Response.HasComputedHeaders) { flags = _requestContext.Response.ComputeHeaders(writeCount, endOfRequest); @@ -491,19 +484,19 @@ private HttpApiTypes.HTTP_FLAGS ComputeLeftToWrite(long writeCount, bool endOfRe if (endOfRequest && _requestContext.Response.BoundaryType == BoundaryType.Close) { - flags |= HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT; + flags |= PInvoke.HTTP_SEND_RESPONSE_FLAG_DISCONNECT; } else if (!endOfRequest && (_leftToWrite != writeCount || _requestContext.Response.TrailersExpected)) { - flags |= HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA; + flags |= PInvoke.HTTP_SEND_RESPONSE_FLAG_MORE_DATA; } if (EnableKernelResponseBuffering) { // "When this flag is set, it should also be used consistently in calls to the HttpSendResponseEntityBody function." // so: make sure we add it in *all* scenarios where it applies - our "close" could be at the end of a bunch // of buffered chunks - flags |= HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA; + flags |= PInvoke.HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA; } // Update _leftToWrite now so we can queue up additional async writes. @@ -687,7 +680,7 @@ internal unsafe Task SendFileAsyncCore(string fileName, long offset, long? count statusCode = HttpApi.HttpSendResponseEntityBody( RequestQueueHandle, RequestId, - (uint)flags, + flags, asyncResult.DataChunkCount, asyncResult.DataChunks, &bytesSent, @@ -709,7 +702,7 @@ internal unsafe Task SendFileAsyncCore(string fileName, long offset, long? count allocator.Dispose(); } - if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) + if (statusCode != ErrorCodes.ERROR_SUCCESS && statusCode != ErrorCodes.ERROR_IO_PENDING) { if (cancellationToken.IsCancellationRequested) { @@ -739,7 +732,7 @@ internal unsafe Task SendFileAsyncCore(string fileName, long offset, long? count } // Last write, cache it for special cancellation handling. - if ((flags & HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0) + if ((flags & PInvoke.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0) { _lastWrite = asyncResult; } @@ -779,7 +772,7 @@ internal unsafe void CancelLastWrite() ResponseStreamAsyncResult? asyncState = _lastWrite; if (asyncState != null && !asyncState.IsCompleted) { - UnsafeNclNativeMethods.CancelIoEx(RequestQueueHandle, asyncState.NativeOverlapped!); + HttpApi.CancelIoEx(RequestQueueHandle, asyncState.NativeOverlapped!); } } diff --git a/src/Servers/HttpSys/src/RequestProcessing/ResponseStreamAsyncResult.cs b/src/Servers/HttpSys/src/RequestProcessing/ResponseStreamAsyncResult.cs index 34f044349a00..be44c6595059 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/ResponseStreamAsyncResult.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/ResponseStreamAsyncResult.cs @@ -3,7 +3,8 @@ using System.Diagnostics; using System.Runtime.InteropServices; -using Microsoft.AspNetCore.HttpSys.Internal; +using Windows.Win32.Foundation; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -12,7 +13,7 @@ internal sealed unsafe partial class ResponseStreamAsyncResult : IAsyncResult, I private static readonly IOCompletionCallback IOCallback = new IOCompletionCallback(Callback); private readonly SafeNativeOverlapped? _overlapped; - private readonly HttpApiTypes.HTTP_DATA_CHUNK[]? _dataChunks; + private readonly HTTP_DATA_CHUNK[]? _dataChunks; private readonly FileStream? _fileStream; private readonly ResponseBody _responseStream; private readonly TaskCompletionSource _tcs; @@ -49,7 +50,7 @@ internal ResponseStreamAsyncResult(ResponseBody responseStream, ArraySegment segment) + private static void SetDataChunk(HTTP_DATA_CHUNK[] chunks, ref int chunkIndex, object[] objectsToPin, ref int pinIndex, ArraySegment segment) { objectsToPin[pinIndex] = segment.Array!; pinIndex++; ref var chunk = ref chunks[chunkIndex++]; - chunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; + chunk.DataChunkType = HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; // The address is not set until after we pin it with Overlapped - chunk.fromMemory.BufferLength = (uint)segment.Count; + chunk.Anonymous.FromMemory.BufferLength = (uint)segment.Count; } - private static void SetDataChunkWithPinnedData(HttpApiTypes.HTTP_DATA_CHUNK[] chunks, ref int chunkIndex, ReadOnlySpan bytes) + private static void SetDataChunkWithPinnedData(HTTP_DATA_CHUNK[] chunks, ref int chunkIndex, ReadOnlySpan bytes) { ref var chunk = ref chunks[chunkIndex++]; - chunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; + chunk.DataChunkType = HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; fixed (byte* ptr = bytes) { - chunk.fromMemory.pBuffer = (IntPtr)ptr; + chunk.Anonymous.FromMemory.pBuffer = ptr; } - chunk.fromMemory.BufferLength = (uint)bytes.Length; + chunk.Anonymous.FromMemory.BufferLength = (uint)bytes.Length; } internal SafeNativeOverlapped? NativeOverlapped @@ -205,7 +206,7 @@ internal ushort DataChunkCount } } - internal HttpApiTypes.HTTP_DATA_CHUNK* DataChunks + internal HTTP_DATA_CHUNK* DataChunks { get { @@ -215,7 +216,7 @@ internal HttpApiTypes.HTTP_DATA_CHUNK* DataChunks } else { - return (HttpApiTypes.HTTP_DATA_CHUNK*)(Marshal.UnsafeAddrOfPinnedArrayElement(_dataChunks, 0)); + return (HTTP_DATA_CHUNK*)(Marshal.UnsafeAddrOfPinnedArrayElement(_dataChunks, 0)); } } } @@ -232,7 +233,7 @@ private static void IOCompleted(ResponseStreamAsyncResult asyncResult, uint erro var logger = asyncResult._responseStream.RequestContext.Logger; try { - if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) + if (errorCode != ErrorCodes.ERROR_SUCCESS && errorCode != ErrorCodes.ERROR_HANDLE_EOF) { if (asyncResult._cancellationToken.IsCancellationRequested) { diff --git a/src/Servers/HttpSys/src/TimeoutManager.cs b/src/Servers/HttpSys/src/TimeoutManager.cs index 63fe93d1786c..457577e2cf8f 100644 --- a/src/Servers/HttpSys/src/TimeoutManager.cs +++ b/src/Servers/HttpSys/src/TimeoutManager.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Runtime.InteropServices; -using Microsoft.AspNetCore.HttpSys.Internal; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys; public sealed class TimeoutManager { private static readonly int TimeoutLimitSize = - Marshal.SizeOf(); + Marshal.SizeOf(); private UrlGroup? _urlGroup; private readonly int[] _timeouts; @@ -48,11 +48,11 @@ public TimeSpan EntityBody { get { - return GetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.EntityBody); + return GetTimeSpanTimeout(TimeoutType.EntityBody); } set { - SetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.EntityBody, value); + SetTimeSpanTimeout(TimeoutType.EntityBody, value); } } @@ -71,11 +71,11 @@ public TimeSpan DrainEntityBody { get { - return GetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.DrainEntityBody); + return GetTimeSpanTimeout(TimeoutType.DrainEntityBody); } set { - SetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.DrainEntityBody, value); + SetTimeSpanTimeout(TimeoutType.DrainEntityBody, value); } } @@ -89,11 +89,11 @@ public TimeSpan RequestQueue { get { - return GetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.RequestQueue); + return GetTimeSpanTimeout(TimeoutType.RequestQueue); } set { - SetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.RequestQueue, value); + SetTimeSpanTimeout(TimeoutType.RequestQueue, value); } } @@ -108,11 +108,11 @@ public TimeSpan IdleConnection { get { - return GetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.IdleConnection); + return GetTimeSpanTimeout(TimeoutType.IdleConnection); } set { - SetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.IdleConnection, value); + SetTimeSpanTimeout(TimeoutType.IdleConnection, value); } } @@ -128,11 +128,11 @@ public TimeSpan HeaderWait { get { - return GetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.HeaderWait); + return GetTimeSpanTimeout(TimeoutType.HeaderWait); } set { - SetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.HeaderWait, value); + SetTimeSpanTimeout(TimeoutType.HeaderWait, value); } } @@ -168,13 +168,13 @@ public long MinSendBytesPerSecond #region Helpers - private TimeSpan GetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE type) + private TimeSpan GetTimeSpanTimeout(TimeoutType type) { // Since we maintain local state, GET is local. return new TimeSpan(0, 0, (int)_timeouts[(int)type]); } - private void SetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE type, TimeSpan value) + private void SetTimeSpanTimeout(TimeoutType type, TimeSpan value) { // All timeouts are defined as USHORT in native layer (except MinSendRate, which is ULONG). Make sure that // timeout value is within range. @@ -208,27 +208,33 @@ private unsafe void SetUrlGroupTimeouts(int[] timeouts, uint minSendBytesPerSeco return; } - var timeoutinfo = new HttpApiTypes.HTTP_TIMEOUT_LIMIT_INFO(); - - timeoutinfo.Flags = HttpApiTypes.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT; - timeoutinfo.DrainEntityBody = - (ushort)timeouts[(int)HttpApiTypes.HTTP_TIMEOUT_TYPE.DrainEntityBody]; - timeoutinfo.EntityBody = - (ushort)timeouts[(int)HttpApiTypes.HTTP_TIMEOUT_TYPE.EntityBody]; - timeoutinfo.RequestQueue = - (ushort)timeouts[(int)HttpApiTypes.HTTP_TIMEOUT_TYPE.RequestQueue]; - timeoutinfo.IdleConnection = - (ushort)timeouts[(int)HttpApiTypes.HTTP_TIMEOUT_TYPE.IdleConnection]; - timeoutinfo.HeaderWait = - (ushort)timeouts[(int)HttpApiTypes.HTTP_TIMEOUT_TYPE.HeaderWait]; - timeoutinfo.MinSendRate = minSendBytesPerSecond; + var timeoutinfo = new HTTP_TIMEOUT_LIMIT_INFO + { + Flags = HttpApi.HTTP_PROPERTY_FLAGS_PRESENT, + DrainEntityBody = (ushort)timeouts[(int)TimeoutType.DrainEntityBody], + EntityBody = (ushort)timeouts[(int)TimeoutType.EntityBody], + RequestQueue = (ushort)timeouts[(int)TimeoutType.RequestQueue], + IdleConnection = (ushort)timeouts[(int)TimeoutType.IdleConnection], + HeaderWait = (ushort)timeouts[(int)TimeoutType.HeaderWait], + MinSendRate = minSendBytesPerSecond + }; var infoptr = new IntPtr(&timeoutinfo); _urlGroup.SetProperty( - HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerTimeoutsProperty, + HTTP_SERVER_PROPERTY.HttpServerTimeoutsProperty, infoptr, (uint)TimeoutLimitSize); } + private enum TimeoutType : int + { + EntityBody, + DrainEntityBody, + RequestQueue, + IdleConnection, + HeaderWait, + MinSendRate, + } + #endregion Helpers } diff --git a/src/Servers/HttpSys/src/UrlPrefixCollection.cs b/src/Servers/HttpSys/src/UrlPrefixCollection.cs index 217a2fa49fd7..20cd74a55b10 100644 --- a/src/Servers/HttpSys/src/UrlPrefixCollection.cs +++ b/src/Servers/HttpSys/src/UrlPrefixCollection.cs @@ -6,7 +6,6 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.HttpSys.Internal; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -233,9 +232,9 @@ private void FindHttpPortUnsynchronized(int key, UrlPrefix urlPrefix) } catch (HttpSysException ex) { - if ((ex.ErrorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_ACCESS_DENIED - && ex.ErrorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SHARING_VIOLATION - && ex.ErrorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_ALREADY_EXISTS) || index == MaxRetries - 1) + if ((ex.ErrorCode != ErrorCodes.ERROR_ACCESS_DENIED + && ex.ErrorCode != ErrorCodes.ERROR_SHARING_VIOLATION + && ex.ErrorCode != ErrorCodes.ERROR_ALREADY_EXISTS) || index == MaxRetries - 1) { throw; } diff --git a/src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs b/src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs index 3329d3ac41dc..ec3be7a25f6e 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs @@ -1,21 +1,18 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Diagnostics; -using System.IO; using System.Net.Http; using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Text; -using System.Threading; -using System.Threading.Tasks; using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.AspNetCore.InternalTesting; +using Windows.Win32.Networking.HttpServer; using Xunit; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -207,20 +204,20 @@ public async Task Https_ITlsHandshakeFeature_MatchesIHttpSysExtensionInfoFeature Assert.NotNull(tlsFeature); Assert.NotNull(requestInfoFeature); Assert.True(requestInfoFeature.RequestInfo.Count > 0); - var tlsInfo = requestInfoFeature.RequestInfo[(int)HttpApiTypes.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeSslProtocol]; - HttpApiTypes.HTTP_SSL_PROTOCOL_INFO tlsCopy; + var tlsInfo = requestInfoFeature.RequestInfo[(int)HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeSslProtocol]; + HTTP_SSL_PROTOCOL_INFO tlsCopy; unsafe { using var handle = tlsInfo.Pin(); - tlsCopy = Marshal.PtrToStructure((IntPtr)handle.Pointer); + tlsCopy = Marshal.PtrToStructure((IntPtr)handle.Pointer); } // Assert.Equal(tlsFeature.Protocol, tlsCopy.Protocol); // These don't directly match because the native and managed enums use different values. - Assert.Equal(tlsFeature.CipherAlgorithm, tlsCopy.CipherType); + Assert.Equal((uint)tlsFeature.CipherAlgorithm, tlsCopy.CipherType); Assert.Equal(tlsFeature.CipherStrength, (int)tlsCopy.CipherStrength); - Assert.Equal(tlsFeature.HashAlgorithm, tlsCopy.HashType); + Assert.Equal((uint)tlsFeature.HashAlgorithm, tlsCopy.HashType); Assert.Equal(tlsFeature.HashStrength, (int)tlsCopy.HashStrength); - Assert.Equal(tlsFeature.KeyExchangeAlgorithm, tlsCopy.KeyExchangeType); + Assert.Equal((uint)tlsFeature.KeyExchangeAlgorithm, tlsCopy.KeyExchangeType); Assert.Equal(tlsFeature.KeyExchangeStrength, (int)tlsCopy.KeyExchangeStrength); } catch (Exception ex) diff --git a/src/Servers/HttpSys/test/FunctionalTests/Listener/ServerOnExistingQueueTests.cs b/src/Servers/HttpSys/test/FunctionalTests/Listener/ServerOnExistingQueueTests.cs index ffdbeff1ad58..d75766ede7b4 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/Listener/ServerOnExistingQueueTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/Listener/ServerOnExistingQueueTests.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.AspNetCore.InternalTesting; +using Windows.Win32; using Xunit; namespace Microsoft.AspNetCore.Server.HttpSys.Listener; @@ -239,15 +240,14 @@ public async Task Server_CreateOrAttach_NoUrlPrefix_NewUrlPrefixWorks() var queueName = Guid.NewGuid().ToString(); // Create a queue without a UrlGroup or any UrlPrefixes - HttpRequestQueueV2Handle requestQueueHandle = null; - var statusCode = HttpApi.HttpCreateRequestQueue( + var statusCode = PInvoke.HttpCreateRequestQueue( HttpApi.Version, queueName, - IntPtr.Zero, + default, 0, - out requestQueueHandle); + out var requestQueueHandle); - Assert.True(statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS); + Assert.True(statusCode == ErrorCodes.ERROR_SUCCESS); using var server = Utilities.CreateServer(options => { diff --git a/src/Servers/HttpSys/test/FunctionalTests/Listener/ServerTests.cs b/src/Servers/HttpSys/test/FunctionalTests/Listener/ServerTests.cs index b61b00ac2c6e..8a7a9d4da76e 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/Listener/ServerTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/Listener/ServerTests.cs @@ -136,7 +136,7 @@ public void Server_RegisterUnavailablePrefix_ThrowsActionableHttpSysException() var exception = Assert.Throws(() => listener.Start()); - Assert.Equal((int)UnsafeNclNativeMethods.ErrorCodes.ERROR_ALREADY_EXISTS, exception.ErrorCode); + Assert.Equal((int)ErrorCodes.ERROR_ALREADY_EXISTS, exception.ErrorCode); Assert.Contains($"The prefix '{address1}' is already registered.", exception.Message); } diff --git a/src/Servers/HttpSys/test/FunctionalTests/Listener/Utilities.cs b/src/Servers/HttpSys/test/FunctionalTests/Listener/Utilities.cs index 15664bf82119..9dc32072a530 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/Listener/Utilities.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/Listener/Utilities.cs @@ -56,9 +56,9 @@ internal static HttpSysListener CreateDynamicHttpServer(string basePath, out str catch (HttpSysException ex) { listener.Dispose(); - if (ex.ErrorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_ALREADY_EXISTS - && ex.ErrorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SHARING_VIOLATION - && ex.ErrorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_ACCESS_DENIED) + if (ex.ErrorCode != ErrorCodes.ERROR_ALREADY_EXISTS + && ex.ErrorCode != ErrorCodes.ERROR_SHARING_VIOLATION + && ex.ErrorCode != ErrorCodes.ERROR_ACCESS_DENIED) { throw; } diff --git a/src/Servers/HttpSys/test/FunctionalTests/ServerTests.cs b/src/Servers/HttpSys/test/FunctionalTests/ServerTests.cs index eaae45c3ea62..b9ebff05f721 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/ServerTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/ServerTests.cs @@ -16,6 +16,7 @@ using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.AspNetCore.InternalTesting; using Microsoft.Extensions.Options; +using Windows.Win32; using Xunit; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -25,8 +26,7 @@ public class ServerTests : LoggedTest [ConditionalFact] public async Task Server_200OK_Success() { - string address; - using (Utilities.CreateHttpServer(out address, httpContext => + using (Utilities.CreateHttpServer(out var address, httpContext => { return Task.FromResult(0); }, LoggerFactory)) @@ -41,22 +41,20 @@ public async Task Server_200OK_Success() [InlineData(RequestQueueMode.CreateOrAttach)] public async Task Server_ConnectExistingQueueName_Success(RequestQueueMode queueMode) { - string address; var queueName = Guid.NewGuid().ToString(); // First create the queue. - HttpRequestQueueV2Handle requestQueueHandle = null; - var statusCode = HttpApi.HttpCreateRequestQueue( + var statusCode = PInvoke.HttpCreateRequestQueue( HttpApi.Version, queueName, - IntPtr.Zero, + default, 0, - out requestQueueHandle); + out var requestQueueHandle); - Assert.True(statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS); + Assert.True(statusCode == ErrorCodes.ERROR_SUCCESS); // Now attach to the existing one - using (Utilities.CreateHttpServer(out address, httpContext => + using (Utilities.CreateHttpServer(out var address, httpContext => { return Task.FromResult(0); }, options => @@ -93,9 +91,8 @@ public async Task Server_ConnectExistingQueueName_Success(RequestQueueMode queue [ConditionalFact] public async Task Server_SetQueueName_Success() { - string address; var queueName = Guid.NewGuid().ToString(); - using (Utilities.CreateHttpServer(out address, httpContext => + using (Utilities.CreateHttpServer(out var address, httpContext => { return Task.FromResult(0); }, options => @@ -117,8 +114,7 @@ public async Task Server_SetQueueName_Success() [ConditionalFact] public async Task Server_SendHelloWorld_Success() { - string address; - using (Utilities.CreateHttpServer(out address, httpContext => + using (Utilities.CreateHttpServer(out var address, httpContext => { httpContext.Response.ContentLength = 11; return httpContext.Response.WriteAsync("Hello World"); @@ -132,8 +128,7 @@ public async Task Server_SendHelloWorld_Success() [ConditionalFact] public async Task Server_EchoHelloWorld_Success() { - string address; - using (Utilities.CreateHttpServer(out address, async httpContext => + using (Utilities.CreateHttpServer(out var address, async httpContext => { var input = await new StreamReader(httpContext.Request.Body).ReadToEndAsync(); Assert.Equal("Hello World", input); @@ -212,8 +207,7 @@ public async Task Server_ShutdownDuringLongRunningRequest_TimesOut() [ConditionalFact] public async Task Server_AppException_ClientReset() { - string address; - using (Utilities.CreateHttpServer(out address, httpContext => + using (Utilities.CreateHttpServer(out var address, httpContext => { throw new InvalidOperationException(); }, LoggerFactory)) @@ -232,8 +226,7 @@ public async Task Server_AppException_ClientReset() [ConditionalFact] public async Task Server_BadHttpRequestException_SetStatusCode() { - string address; - using (Utilities.CreateHttpServer(out address, httpContext => + using (Utilities.CreateHttpServer(out var address, httpContext => { throw new BadHttpRequestException("Something happened", StatusCodes.Status418ImATeapot); }, LoggerFactory)) @@ -256,8 +249,7 @@ public void Server_MultipleOutstandingAsyncRequests_Success() int requestCount = 0; TaskCompletionSource tcs = new TaskCompletionSource(); - string address; - using (Utilities.CreateHttpServer(out address, async httpContext => + using (Utilities.CreateHttpServer(out var address, async httpContext => { if (Interlocked.Increment(ref requestCount) == requestLimit) { diff --git a/src/Servers/HttpSys/test/Tests/RequestHeaderTests.cs b/src/Servers/HttpSys/test/Tests/RequestHeaderTests.cs index ca447f4c20b7..84d963923d68 100644 --- a/src/Servers/HttpSys/test/Tests/RequestHeaderTests.cs +++ b/src/Servers/HttpSys/test/Tests/RequestHeaderTests.cs @@ -6,6 +6,8 @@ using System.Text; using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.Net.Http.Headers; +using Windows.Win32.Foundation; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.HttpSys.Tests; @@ -20,7 +22,7 @@ public unsafe void RequestHeaders_PooledMemory_ReturnsKnownHeadersKeys() var nativeContext = new NativeRequestContext(MemoryPool.Shared, null, 0, false); var nativeMemory = new Span(nativeContext.NativeRequest, (int)nativeContext.Size + 8); - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); SetHostAndContentTypeHeaders(nativeMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -41,11 +43,11 @@ public unsafe void RequestHeaders_PinnedMemory_ReturnsKnownHeadersKeys() using var handle = buffer.Pin(); Span nativeMemory = buffer.Span; - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); SetHostAndContentTypeHeaders(nativeMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); - var nativeContext = new NativeRequestContext((HttpApiTypes.HTTP_REQUEST*)handle.Pointer, false); + var nativeContext = new NativeRequestContext((HTTP_REQUEST_V1*)handle.Pointer, false); var requestHeaders = new RequestHeaders(nativeContext); Assert.Equal(2, requestHeaders.Count); @@ -61,7 +63,7 @@ public unsafe void RequestHeaders_PooledMemory_ReturnsUnknownHeadersKeys() var nativeContext = new NativeRequestContext(MemoryPool.Shared, null, 0, false); var nativeMemory = new Span(nativeContext.NativeRequest, (int)nativeContext.Size + 8); - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); SetUnknownHeaders(nativeMemory, ref requestStructure, new[] { (CustomHeader1, "1"), (CustomHeader2, null) }); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -82,11 +84,11 @@ public unsafe void RequestHeaders_PinnedMemory_ReturnsUnknownHeadersKeys() using var handle = buffer.Pin(); Span nativeMemory = buffer.Span; - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); SetUnknownHeaders(nativeMemory, ref requestStructure, new[] { (CustomHeader1, "1"), (CustomHeader2, null) }); MemoryMarshal.Write(nativeMemory, in requestStructure); - var nativeContext = new NativeRequestContext((HttpApiTypes.HTTP_REQUEST*)handle.Pointer, false); + var nativeContext = new NativeRequestContext((HTTP_REQUEST_V1*)handle.Pointer, false); var requestHeaders = new RequestHeaders(nativeContext); Assert.Equal(2, requestHeaders.Count); @@ -102,7 +104,7 @@ public unsafe void RequestHeaders_PooledMemory_DoesNotReturnInvalidKnownHeadersK var nativeContext = new NativeRequestContext(MemoryPool.Shared, null, 0, false); var nativeMemory = new Span(nativeContext.NativeRequest, (int)nativeContext.Size + 8); - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); SetInvalidHostHeader(nativeMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -121,7 +123,7 @@ public unsafe void RequestHeaders_PooledMemory_DoesNotReturnInvalidUnknownHeader var nativeContext = new NativeRequestContext(MemoryPool.Shared, null, 0, false); var nativeMemory = new Span(nativeContext.NativeRequest, (int)nativeContext.Size + 8); - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); SetInvalidUnknownHeaders(nativeMemory, ref requestStructure, new[] { CustomHeader1 }); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -140,7 +142,7 @@ public unsafe void RequestHeaders_PooledMemory_ReturnsKnownAndUnKnownHeadersKeys var nativeContext = new NativeRequestContext(MemoryPool.Shared, null, 0, false); var nativeMemory = new Span(nativeContext.NativeRequest, (int)nativeContext.Size + 8); - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); var remainingMemory = SetUnknownHeaders(nativeMemory, ref requestStructure, new[] { (CustomHeader1, "1"), (CustomHeader2, null) }); SetHostAndContentTypeHeaders(remainingMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -164,12 +166,12 @@ public unsafe void RequestHeaders_PinnedMemory_ReturnsKnownAndUnKnownHeadersKeys using var handle = buffer.Pin(); Span nativeMemory = buffer.Span; - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); var remainingMemory = SetUnknownHeaders(nativeMemory, ref requestStructure, new[] { (CustomHeader1, "1"), (CustomHeader2, null) }); SetHostAndContentTypeHeaders(remainingMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); - var nativeContext = new NativeRequestContext((HttpApiTypes.HTTP_REQUEST*)handle.Pointer, false); + var nativeContext = new NativeRequestContext((HTTP_REQUEST_V1*)handle.Pointer, false); var requestHeaders = new RequestHeaders(nativeContext); Assert.Equal(4, requestHeaders.Count); @@ -187,7 +189,7 @@ public unsafe void RequestHeaders_RemoveUnknownHeader_DecreasesCount() var nativeContext = new NativeRequestContext(MemoryPool.Shared, null, 0, false); var nativeMemory = new Span(nativeContext.NativeRequest, (int)nativeContext.Size + 8); - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); SetUnknownHeaders(nativeMemory, ref requestStructure, new[] { (CustomHeader1, "1"), (CustomHeader2, null) }); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -209,7 +211,7 @@ public unsafe void RequestHeaders_AddUnknownHeader_IncreasesCount() var nativeContext = new NativeRequestContext(MemoryPool.Shared, null, 0, false); var nativeMemory = new Span(nativeContext.NativeRequest, (int)nativeContext.Size + 8); - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); SetUnknownHeaders(nativeMemory, ref requestStructure, new[] { (CustomHeader1, "1") }); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -231,7 +233,7 @@ public unsafe void RequestHeaders_RemoveUnknownHeader_RemovesKey() var nativeContext = new NativeRequestContext(MemoryPool.Shared, null, 0, false); var nativeMemory = new Span(nativeContext.NativeRequest, (int)nativeContext.Size + 8); - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); SetUnknownHeaders(nativeMemory, ref requestStructure, new[] { (CustomHeader1, "1"), (CustomHeader2, null) }); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -252,7 +254,7 @@ public unsafe void RequestHeaders_AddUnknownHeader_AddsKey() var nativeContext = new NativeRequestContext(MemoryPool.Shared, null, 0, false); var nativeMemory = new Span(nativeContext.NativeRequest, (int)nativeContext.Size + 8); - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); SetUnknownHeaders(nativeMemory, ref requestStructure, new[] { (CustomHeader1, "1") }); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -272,7 +274,7 @@ public unsafe void RequestHeaders_RemoveKnownHeader_DecreasesCount() var nativeContext = new NativeRequestContext(MemoryPool.Shared, null, 0, false); var nativeMemory = new Span(nativeContext.NativeRequest, (int)nativeContext.Size + 8); - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); SetHostAndContentTypeHeaders(nativeMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -294,7 +296,7 @@ public unsafe void RequestHeaders_AddKnownHeader_IncreasesCount() var nativeContext = new NativeRequestContext(MemoryPool.Shared, null, 0, false); var nativeMemory = new Span(nativeContext.NativeRequest, (int)nativeContext.Size + 8); - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); SetHostAndContentTypeHeaders(nativeMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -316,7 +318,7 @@ public unsafe void RequestHeaders_RemoveKnownHeader_RemovesKey() var nativeContext = new NativeRequestContext(MemoryPool.Shared, null, 0, false); var nativeMemory = new Span(nativeContext.NativeRequest, (int)nativeContext.Size + 8); - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); SetHostAndContentTypeHeaders(nativeMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -337,7 +339,7 @@ public unsafe void RequestHeaders_AddKnownHeader_AddsKey() var nativeContext = new NativeRequestContext(MemoryPool.Shared, null, 0, false); var nativeMemory = new Span(nativeContext.NativeRequest, (int)nativeContext.Size + 8); - var requestStructure = new HttpApiTypes.HTTP_REQUEST(); + var requestStructure = new HTTP_REQUEST_V1(); SetHostAndContentTypeHeaders(nativeMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -351,15 +353,15 @@ public unsafe void RequestHeaders_AddKnownHeader_AddsKey() Assert.Contains(HeaderNames.From, requestHeaders.Keys); } - private static unsafe Span SetHostAndContentTypeHeaders(Span nativeMemory, ref HttpApiTypes.HTTP_REQUEST requestStructure) + private static unsafe Span SetHostAndContentTypeHeaders(Span nativeMemory, ref HTTP_REQUEST_V1 requestStructure) { // Writing localhost to Host header - var dataDestination = nativeMemory.Slice(Marshal.SizeOf()); + var dataDestination = nativeMemory.Slice(Marshal.SizeOf()); int length = Encoding.ASCII.GetBytes("localhost:5001", dataDestination); fixed (byte* address = &MemoryMarshal.GetReference(dataDestination)) { - requestStructure.Headers.KnownHeaders_29.pRawValue = address; - requestStructure.Headers.KnownHeaders_29.RawValueLength = (ushort)length; + requestStructure.Headers.KnownHeaders._28.pRawValue = (PCSTR)address; + requestStructure.Headers.KnownHeaders._28.RawValueLength = (ushort)length; } // Writing application/json to Content-Type header @@ -367,25 +369,25 @@ private static unsafe Span SetHostAndContentTypeHeaders(Span nativeM length = Encoding.ASCII.GetBytes("application/json", dataDestination); fixed (byte* address = &MemoryMarshal.GetReference(dataDestination)) { - requestStructure.Headers.KnownHeaders_13.pRawValue = address; - requestStructure.Headers.KnownHeaders_13.RawValueLength = (ushort)length; + requestStructure.Headers.KnownHeaders._12.pRawValue = (PCSTR)address; + requestStructure.Headers.KnownHeaders._12.RawValueLength = (ushort)length; } dataDestination = dataDestination.Slice(length); return dataDestination; } - private static unsafe Span SetInvalidHostHeader(Span nativeMemory, ref HttpApiTypes.HTTP_REQUEST requestStructure) + private static unsafe Span SetInvalidHostHeader(Span nativeMemory, ref HTTP_REQUEST_V1 requestStructure) { // Writing localhost to Host header - var dataDestination = nativeMemory.Slice(Marshal.SizeOf()); + var dataDestination = nativeMemory.Slice(Marshal.SizeOf()); int length = Encoding.ASCII.GetBytes("localhost:5001", dataDestination); fixed (byte* address = &MemoryMarshal.GetReference(dataDestination)) { - requestStructure.Headers.KnownHeaders_29.pRawValue = address; + requestStructure.Headers.KnownHeaders._28.pRawValue = (PCSTR)address; // Set length to zero, to make it invalid. - requestStructure.Headers.KnownHeaders_29.RawValueLength = 0; + requestStructure.Headers.KnownHeaders._28.RawValueLength = 0; } dataDestination = dataDestination.Slice(length); @@ -395,24 +397,24 @@ private static unsafe Span SetInvalidHostHeader(Span nativeMemory, r /// /// Writes an array HTTP_UNKNOWN_HEADER and an array of header key-value pairs to nativeMemory. Pointers in the HTTP_UNKNOWN_HEADER structure points to the corresponding key-value pair. /// - private static unsafe Span SetUnknownHeaders(Span nativeMemory, ref HttpApiTypes.HTTP_REQUEST requestStructure, IReadOnlyCollection<(string Key, string Value)> headerNames) + private static unsafe Span SetUnknownHeaders(Span nativeMemory, ref HTTP_REQUEST_V1 requestStructure, IReadOnlyCollection<(string Key, string Value)> headerNames) { - var unknownHeaderStructureDestination = nativeMemory.Slice(Marshal.SizeOf()); + var unknownHeaderStructureDestination = nativeMemory.Slice(Marshal.SizeOf()); fixed (byte* address = &MemoryMarshal.GetReference(unknownHeaderStructureDestination)) { - requestStructure.Headers.pUnknownHeaders = (HttpApiTypes.HTTP_UNKNOWN_HEADER*)address; + requestStructure.Headers.pUnknownHeaders = (HTTP_UNKNOWN_HEADER*)address; } requestStructure.Headers.UnknownHeaderCount += (ushort)headerNames.Count; - var unknownHeadersSize = Marshal.SizeOf(); + var unknownHeadersSize = Marshal.SizeOf(); var dataDestination = unknownHeaderStructureDestination.Slice(unknownHeadersSize * headerNames.Count); foreach (var headerName in headerNames) { - var unknownHeaderStructure = new HttpApiTypes.HTTP_UNKNOWN_HEADER(); + var unknownHeaderStructure = new HTTP_UNKNOWN_HEADER(); int nameLength = Encoding.ASCII.GetBytes(headerName.Key, dataDestination); fixed (byte* address = &MemoryMarshal.GetReference(dataDestination)) { - unknownHeaderStructure.pName = address; + unknownHeaderStructure.pName = (PCSTR)address; unknownHeaderStructure.NameLength = (ushort)nameLength; } dataDestination = dataDestination.Slice(nameLength); @@ -422,7 +424,7 @@ private static unsafe Span SetUnknownHeaders(Span nativeMemory, ref int valueLength = Encoding.ASCII.GetBytes(headerName.Value, dataDestination); fixed (byte* address = &MemoryMarshal.GetReference(dataDestination)) { - unknownHeaderStructure.pRawValue = address; + unknownHeaderStructure.pRawValue = (PCSTR)address; unknownHeaderStructure.RawValueLength = (ushort)valueLength; } dataDestination = dataDestination.Slice(nameLength); @@ -433,26 +435,26 @@ private static unsafe Span SetUnknownHeaders(Span nativeMemory, ref return dataDestination; } - private static unsafe Span SetInvalidUnknownHeaders(Span nativeMemory, ref HttpApiTypes.HTTP_REQUEST requestStructure, IReadOnlyCollection headerNames) + private static unsafe Span SetInvalidUnknownHeaders(Span nativeMemory, ref HTTP_REQUEST_V1 requestStructure, IReadOnlyCollection headerNames) { - var unknownHeaderStructureDestination = nativeMemory.Slice(Marshal.SizeOf()); + var unknownHeaderStructureDestination = nativeMemory.Slice(Marshal.SizeOf()); fixed (byte* address = &MemoryMarshal.GetReference(unknownHeaderStructureDestination)) { - requestStructure.Headers.pUnknownHeaders = (HttpApiTypes.HTTP_UNKNOWN_HEADER*)address; + requestStructure.Headers.pUnknownHeaders = (HTTP_UNKNOWN_HEADER*)address; } // UnknownHeaderCount might be higher number to the headers that can be parsed out. requestStructure.Headers.UnknownHeaderCount += (ushort)headerNames.Count; - var unknownHeadersSize = Marshal.SizeOf(); + var unknownHeadersSize = Marshal.SizeOf(); var dataDestination = unknownHeaderStructureDestination.Slice(unknownHeadersSize * headerNames.Count); foreach (var headerName in headerNames) { - var unknownHeaderStructure = new HttpApiTypes.HTTP_UNKNOWN_HEADER(); + var unknownHeaderStructure = new HTTP_UNKNOWN_HEADER(); int nameLength = Encoding.ASCII.GetBytes(headerName, dataDestination); fixed (byte* address = &MemoryMarshal.GetReference(dataDestination)) { - unknownHeaderStructure.pName = address; + unknownHeaderStructure.pName = (PCSTR)address; // Set the length of the name to 0 to make it invalid. unknownHeaderStructure.NameLength = 0; diff --git a/src/Servers/IIS/IIS/src/Core/IISHttpContext.cs b/src/Servers/IIS/IIS/src/Core/IISHttpContext.cs index 5eff74efd87c..f0e04f3b2a95 100644 --- a/src/Servers/IIS/IIS/src/Core/IISHttpContext.cs +++ b/src/Servers/IIS/IIS/src/Core/IISHttpContext.cs @@ -21,6 +21,7 @@ using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.IIS.Core; @@ -77,7 +78,7 @@ internal unsafe IISHttpContext( IISHttpServer server, ILogger logger, bool useLatin1) - : base((HttpApiTypes.HTTP_REQUEST*)NativeMethods.HttpGetRawRequest(pInProcessHandler), useLatin1: useLatin1) + : base((HTTP_REQUEST_V1*)NativeMethods.HttpGetRawRequest(pInProcessHandler), useLatin1: useLatin1) { _memoryPool = memoryPool; _requestNativeHandle = pInProcessHandler; @@ -89,7 +90,7 @@ internal unsafe IISHttpContext( } private int PauseWriterThreshold => _options.MaxRequestBodyBufferSize; - private int ResumeWriterTheshold => PauseWriterThreshold / 2; + private int ResumeWriterThreshold => PauseWriterThreshold / 2; private bool IsHttps => SslStatus != SslStatus.Insecure; public Version HttpVersion { get; set; } = default!; @@ -133,7 +134,7 @@ internal unsafe IISHttpContext( private HeaderCollection HttpResponseTrailers => _trailers ??= new HeaderCollection(checkTrailers: true); internal bool HasTrailers => _trailers?.Count > 0; - internal HttpApiTypes.HTTP_VERB KnownMethod { get; private set; } + internal HTTP_VERB KnownMethod { get; private set; } private bool HasStartedConsumingRequestBody { get; set; } public long? MaxRequestBodySize { get; set; } @@ -142,7 +143,7 @@ protected void InitializeContext() { // create a memory barrier between initialize and disconnect to prevent a possible // NullRef with disconnect being called before these fields have been written - // disconnect aquires this lock as well + // disconnect acquires this lock as well lock (_abortLock) { _thisHandle = GCHandle.Alloc(this); @@ -163,7 +164,7 @@ protected void InitializeContext() pathBase = pathBase[..^1]; } - if (KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawTarget, "*", StringComparison.Ordinal)) + if (KnownMethod == HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawTarget, "*", StringComparison.Ordinal)) { PathBase = string.Empty; Path = string.Empty; @@ -301,7 +302,7 @@ protected void InitializeContext() // schedules app code when backpressure is relieved which may block. readerScheduler: PipeScheduler.Inline, pauseWriterThreshold: PauseWriterThreshold, - resumeWriterThreshold: ResumeWriterTheshold, + resumeWriterThreshold: ResumeWriterThreshold, minimumSegmentSize: MinAllocBufferSize)); _bodyOutput = new OutputProducer(pipe); } @@ -392,27 +393,27 @@ private bool CheckRequestCanHaveBody() private void GetTlsHandshakeResults() { - var handshake = this.GetTlsHandshake(); - Protocol = handshake.Protocol; - CipherAlgorithm = handshake.CipherType; + var handshake = GetTlsHandshake(); + Protocol = (SslProtocols)handshake.Protocol; + CipherAlgorithm = (CipherAlgorithmType)handshake.CipherType; CipherStrength = (int)handshake.CipherStrength; - HashAlgorithm = handshake.HashType; + HashAlgorithm = (HashAlgorithmType)handshake.HashType; HashStrength = (int)handshake.HashStrength; - KeyExchangeAlgorithm = handshake.KeyExchangeType; + KeyExchangeAlgorithm = (ExchangeAlgorithmType)handshake.KeyExchangeType; KeyExchangeStrength = (int)handshake.KeyExchangeStrength; var sni = GetClientSni(); - SniHostName = sni.Hostname; + SniHostName = sni.Hostname.ToString(); } - private unsafe HttpApiTypes.HTTP_REQUEST_PROPERTY_SNI GetClientSni() + private unsafe HTTP_REQUEST_PROPERTY_SNI GetClientSni() { var buffer = new byte[HttpApiTypes.SniPropertySizeInBytes]; fixed (byte* pBuffer = buffer) { var statusCode = NativeMethods.HttpQueryRequestProperty( RequestId, - HttpApiTypes.HTTP_REQUEST_PROPERTY.HttpRequestPropertySni, + HTTP_REQUEST_PROPERTY.HttpRequestPropertySni, qualifier: null, qualifierSize: 0, (void*)pBuffer, @@ -420,7 +421,7 @@ private unsafe HttpApiTypes.HTTP_REQUEST_PROPERTY_SNI GetClientSni() bytesReturned: null, IntPtr.Zero); - return statusCode == NativeMethods.HR_OK ? Marshal.PtrToStructure((IntPtr)pBuffer) : default; + return statusCode == NativeMethods.HR_OK ? Marshal.PtrToStructure((IntPtr)pBuffer) : default; } } @@ -590,7 +591,7 @@ public unsafe void SetResponseHeaders() continue; } - var knownHeaderIndex = HttpApiTypes.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerPair.Key); + var isKnownHeader = HttpApiTypes.KnownResponseHeaders.TryGetValue(headerPair.Key, out var knownHeaderIndex); for (var i = 0; i < headerValues.Count; i++) { var headerValue = headerValues[i]; @@ -605,7 +606,7 @@ public unsafe void SetResponseHeaders() fixed (byte* pHeaderValue = headerValueBytes) { - if (knownHeaderIndex == -1) + if (!isKnownHeader) { var headerNameBytes = Encoding.UTF8.GetBytes(headerPair.Key); fixed (byte* pHeaderName = headerNameBytes) diff --git a/src/Servers/IIS/IIS/src/Core/IO/AsyncIOEngine.Write.cs b/src/Servers/IIS/IIS/src/Core/IO/AsyncIOEngine.Write.cs index c786c2d28c7a..ab84c8bd85fe 100644 --- a/src/Servers/IIS/IIS/src/Core/IO/AsyncIOEngine.Write.cs +++ b/src/Servers/IIS/IIS/src/Core/IO/AsyncIOEngine.Write.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.AspNetCore.HttpSys.Internal; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.IIS.Core.IO; @@ -16,7 +16,7 @@ public AsyncWriteOperation(AsyncIOEngine engine) _engine = engine; } - protected override unsafe int WriteChunks(NativeSafeHandle requestHandler, int chunkCount, HttpApiTypes.HTTP_DATA_CHUNK* dataChunks, + protected override unsafe int WriteChunks(NativeSafeHandle requestHandler, int chunkCount, HTTP_DATA_CHUNK* dataChunks, out bool completionExpected) { return NativeMethods.HttpWriteResponseBytes(requestHandler, dataChunks, chunkCount, out completionExpected); diff --git a/src/Servers/IIS/IIS/src/Core/IO/AsyncWriteOperationBase.cs b/src/Servers/IIS/IIS/src/Core/IO/AsyncWriteOperationBase.cs index 11916321cb83..0636b9b18b13 100644 --- a/src/Servers/IIS/IIS/src/Core/IO/AsyncWriteOperationBase.cs +++ b/src/Servers/IIS/IIS/src/Core/IO/AsyncWriteOperationBase.cs @@ -3,7 +3,7 @@ using System.Buffers; using System.Diagnostics; -using Microsoft.AspNetCore.HttpSys.Internal; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.IIS.Core.IO; @@ -39,14 +39,14 @@ protected override unsafe bool InvokeOperation(out int hr, out int bytes) { // To avoid stackoverflows, we will only stackalloc if the write size is less than the StackChunkLimit // The stack size is IIS is by default 128/256 KB, so we are generous with this threshold. - var chunks = stackalloc HttpApiTypes.HTTP_DATA_CHUNK[chunkCount]; + var chunks = stackalloc HTTP_DATA_CHUNK[chunkCount]; hr = WriteSequence(_requestHandler, chunkCount, _buffer, chunks, out completionExpected); } else { // Otherwise allocate the chunks on the heap. - var chunks = new HttpApiTypes.HTTP_DATA_CHUNK[chunkCount]; - fixed (HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks = chunks) + var chunks = new HTTP_DATA_CHUNK[chunkCount]; + fixed (HTTP_DATA_CHUNK* pDataChunks = chunks) { hr = WriteSequence(_requestHandler, chunkCount, _buffer, pDataChunks, out completionExpected); } @@ -94,7 +94,7 @@ private int GetChunkCount() return count; } - private unsafe int WriteSequence(NativeSafeHandle requestHandler, int nChunks, ReadOnlySequence buffer, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, out bool fCompletionExpected) + private unsafe int WriteSequence(NativeSafeHandle requestHandler, int nChunks, ReadOnlySequence buffer, HTTP_DATA_CHUNK* pDataChunks, out bool fCompletionExpected) { var currentChunk = 0; @@ -109,9 +109,9 @@ private unsafe int WriteSequence(NativeSafeHandle requestHandler, int nChunks, R ref var chunk = ref pDataChunks[currentChunk]; handle = readOnlyMemory.Pin(); - chunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; - chunk.fromMemory.BufferLength = (uint)readOnlyMemory.Length; - chunk.fromMemory.pBuffer = (IntPtr)handle.Pointer; + chunk.DataChunkType = HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; + chunk.Anonymous.FromMemory.BufferLength = (uint)readOnlyMemory.Length; + chunk.Anonymous.FromMemory.pBuffer = handle.Pointer; currentChunk++; } @@ -119,5 +119,5 @@ private unsafe int WriteSequence(NativeSafeHandle requestHandler, int nChunks, R return WriteChunks(requestHandler, nChunks, pDataChunks, out fCompletionExpected); } - protected abstract unsafe int WriteChunks(NativeSafeHandle requestHandler, int chunkCount, HttpApiTypes.HTTP_DATA_CHUNK* dataChunks, out bool completionExpected); + protected abstract unsafe int WriteChunks(NativeSafeHandle requestHandler, int chunkCount, HTTP_DATA_CHUNK* dataChunks, out bool completionExpected); } diff --git a/src/Servers/IIS/IIS/src/Core/IO/WebSocketsAsyncIOEngine.Write.cs b/src/Servers/IIS/IIS/src/Core/IO/WebSocketsAsyncIOEngine.Write.cs index d38c541d5960..f345b7f4e614 100644 --- a/src/Servers/IIS/IIS/src/Core/IO/WebSocketsAsyncIOEngine.Write.cs +++ b/src/Servers/IIS/IIS/src/Core/IO/WebSocketsAsyncIOEngine.Write.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Runtime.InteropServices; -using Microsoft.AspNetCore.HttpSys.Internal; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.IIS.Core.IO; @@ -32,7 +32,7 @@ public WebSocketWriteOperation(WebSocketsAsyncIOEngine engine) _engine = engine; } - protected override unsafe int WriteChunks(NativeSafeHandle requestHandler, int chunkCount, HttpApiTypes.HTTP_DATA_CHUNK* dataChunks, out bool completionExpected) + protected override unsafe int WriteChunks(NativeSafeHandle requestHandler, int chunkCount, HTTP_DATA_CHUNK* dataChunks, out bool completionExpected) { return NativeMethods.HttpWebsocketsWriteBytes(requestHandler, dataChunks, chunkCount, &WriteCallback, (IntPtr)_thisHandle, out completionExpected); } diff --git a/src/Servers/IIS/IIS/src/Microsoft.AspNetCore.Server.IIS.csproj b/src/Servers/IIS/IIS/src/Microsoft.AspNetCore.Server.IIS.csproj index cba37b981972..395303a747fb 100644 --- a/src/Servers/IIS/IIS/src/Microsoft.AspNetCore.Server.IIS.csproj +++ b/src/Servers/IIS/IIS/src/Microsoft.AspNetCore.Server.IIS.csproj @@ -13,10 +13,11 @@ true - $(NoWarn);CA1416 + + $(NoWarn);CA1416;CS9195 - + @@ -26,6 +27,12 @@ + + + + + + @@ -49,6 +56,10 @@ + + all + runtime; build; native; contentfiles; analyzers + diff --git a/src/Servers/IIS/IIS/src/NativeMethods.cs b/src/Servers/IIS/IIS/src/NativeMethods.cs index 9d25ce793c03..6aa3d68ed35e 100644 --- a/src/Servers/IIS/IIS/src/NativeMethods.cs +++ b/src/Servers/IIS/IIS/src/NativeMethods.cs @@ -1,9 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.AspNetCore.Server.IIS.Core; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.IIS; @@ -58,13 +59,13 @@ private static unsafe partial int register_callbacks(NativeSafeHandle pInProcess IntPtr pvShutdownContext); [LibraryImport(AspNetCoreModuleDll)] - private static unsafe partial int http_write_response_bytes(NativeSafeHandle pInProcessHandler, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, int nChunks, [MarshalAs(UnmanagedType.Bool)] out bool fCompletionExpected); + private static unsafe partial int http_write_response_bytes(NativeSafeHandle pInProcessHandler, HTTP_DATA_CHUNK* pDataChunks, int nChunks, [MarshalAs(UnmanagedType.Bool)] out bool fCompletionExpected); [LibraryImport(AspNetCoreModuleDll)] private static partial int http_flush_response_bytes(NativeSafeHandle pInProcessHandler, [MarshalAs(UnmanagedType.Bool)] bool fMoreData, [MarshalAs(UnmanagedType.Bool)] out bool fCompletionExpected); [LibraryImport(AspNetCoreModuleDll)] - private static unsafe partial HttpApiTypes.HTTP_REQUEST_V2* http_get_raw_request(NativeSafeHandle pInProcessHandler); + private static unsafe partial HTTP_REQUEST_V2* http_get_raw_request(NativeSafeHandle pInProcessHandler); [LibraryImport(AspNetCoreModuleDll)] private static partial int http_stop_calls_into_managed(NativeSafeHandle pInProcessApplication); @@ -91,9 +92,10 @@ private static unsafe partial int register_callbacks(NativeSafeHandle pInProcess private static partial int http_get_application_properties(out IISConfigurationData iiConfigData); [LibraryImport(AspNetCoreModuleDll)] + [SuppressMessage("LibraryImportGenerator", "SYSLIB1051:Specified type is not supported by source-generated P/Invokes", Justification = "The enum is handled by the runtime.")] private static unsafe partial int http_query_request_property( ulong requestId, - HttpApiTypes.HTTP_REQUEST_PROPERTY propertyId, + HTTP_REQUEST_PROPERTY propertyId, void* qualifier, uint qualifierSize, void* output, @@ -126,7 +128,7 @@ private static unsafe partial int http_websockets_read_bytes( [LibraryImport(AspNetCoreModuleDll)] private static unsafe partial int http_websockets_write_bytes( NativeSafeHandle pInProcessHandler, - HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, + HTTP_DATA_CHUNK* pDataChunks, int nChunks, delegate* unmanaged pfnCompletionCallback, IntPtr pvCompletionContext, @@ -149,6 +151,7 @@ private static unsafe partial int http_websockets_write_bytes( [LibraryImport(AspNetCoreModuleDll)] private static unsafe partial int http_has_response4(NativeSafeHandle pInProcessHandler, [MarshalAs(UnmanagedType.Bool)] out bool isResponse4); + [LibraryImport(AspNetCoreModuleDll)] private static unsafe partial int http_response_set_trailer(NativeSafeHandle pInProcessHandler, byte* pszHeaderName, byte* pszHeaderValue, ushort usHeaderValueLength, [MarshalAs(UnmanagedType.Bool)] bool replace); @@ -186,7 +189,7 @@ public static unsafe void HttpRegisterCallbacks(NativeSafeHandle pInProcessAppli Validate(register_callbacks(pInProcessApplication, requestCallback, shutdownCallback, disconnectCallback, asyncCallback, requestsDrainedHandler, pvRequestContext, pvShutdownContext)); } - internal static unsafe int HttpWriteResponseBytes(NativeSafeHandle pInProcessHandler, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, int nChunks, out bool fCompletionExpected) + internal static unsafe int HttpWriteResponseBytes(NativeSafeHandle pInProcessHandler, HTTP_DATA_CHUNK* pDataChunks, int nChunks, out bool fCompletionExpected) { return http_write_response_bytes(pInProcessHandler, pDataChunks, nChunks, out fCompletionExpected); } @@ -196,7 +199,7 @@ public static int HttpFlushResponseBytes(NativeSafeHandle pInProcessHandler, boo return http_flush_response_bytes(pInProcessHandler, fMoreData, out fCompletionExpected); } - internal static unsafe HttpApiTypes.HTTP_REQUEST_V2* HttpGetRawRequest(NativeSafeHandle pInProcessHandler) + internal static unsafe HTTP_REQUEST_V2* HttpGetRawRequest(NativeSafeHandle pInProcessHandler) { return http_get_raw_request(pInProcessHandler); } @@ -242,7 +245,7 @@ internal static IISConfigurationData HttpGetApplicationProperties() return iisConfigurationData; } - public static unsafe int HttpQueryRequestProperty(ulong requestId, HttpApiTypes.HTTP_REQUEST_PROPERTY propertyId, void* qualifier, uint qualifierSize, void* output, uint outputSize, uint* bytesReturned, IntPtr overlapped) + public static unsafe int HttpQueryRequestProperty(ulong requestId, HTTP_REQUEST_PROPERTY propertyId, void* qualifier, uint qualifierSize, void* output, uint outputSize, uint* bytesReturned, IntPtr overlapped) { return http_query_request_property(requestId, propertyId, qualifier, qualifierSize, output, outputSize, bytesReturned, overlapped); } @@ -270,7 +273,7 @@ public static unsafe int HttpWebsocketsReadBytes( internal static unsafe int HttpWebsocketsWriteBytes( NativeSafeHandle pInProcessHandler, - HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, + HTTP_DATA_CHUNK* pDataChunks, int nChunks, delegate* unmanaged pfnCompletionCallback, IntPtr pvCompletionContext, diff --git a/src/Servers/IIS/IIS/src/NativeMethods.txt b/src/Servers/IIS/IIS/src/NativeMethods.txt new file mode 100644 index 000000000000..8d0523a197f3 --- /dev/null +++ b/src/Servers/IIS/IIS/src/NativeMethods.txt @@ -0,0 +1,16 @@ +// https://github.com/microsoft/cswin32 +// Listing specific types reduces the size of the final dll. +// Uncomment the next line to import all definitions during development. +// Windows.Win32.Networking.HttpServer +HTTP_AUTH_STATUS +HTTP_COOKED_URL +HTTP_DATA_CHUNK +HTTP_REQUEST_AUTH_INFO +HTTP_REQUEST_AUTH_TYPE +HTTP_REQUEST_FLAG_* +HTTP_REQUEST_PROPERTY +HTTP_REQUEST_PROPERTY_SNI +HTTP_REQUEST_PROPERTY_SNI_HOST_MAX_LENGTH +HTTP_REQUEST_V1 +HTTP_REQUEST_V2 +HTTP_SSL_PROTOCOL_INFO diff --git a/src/Servers/IIS/IIS/src/SourceBuildStubs.cs b/src/Servers/IIS/IIS/src/SourceBuildStubs.cs new file mode 100644 index 000000000000..fe50cc40db0d --- /dev/null +++ b/src/Servers/IIS/IIS/src/SourceBuildStubs.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Hosting; + +/// +/// Extension methods for the IIS In-Process. +/// +public static class WebHostBuilderIISExtensions +{ + /// + /// Configures the port and base path the server should listen on when running behind AspNetCoreModule. + /// The app will also be configured to capture startup errors. + /// + /// The to configure. + /// The . + public static IWebHostBuilder UseIIS(this IWebHostBuilder hostBuilder) => hostBuilder; +} diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Infrastructure/FunctionalTestsBase.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Infrastructure/FunctionalTestsBase.cs index 61daae334aed..facf7dfd420b 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Infrastructure/FunctionalTestsBase.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Infrastructure/FunctionalTestsBase.cs @@ -17,6 +17,7 @@ public FunctionalTestsBase(ITestOutputHelper output = null) : base(output) } protected IISDeployerBase _deployer; + protected IISDeploymentResult _deploymentResult; protected ApplicationDeployer CreateDeployer(IISDeploymentParameters parameters) { @@ -32,7 +33,7 @@ protected ApplicationDeployer CreateDeployer(IISDeploymentParameters parameters) protected virtual async Task DeployAsync(IISDeploymentParameters parameters) { _deployer = (IISDeployerBase)CreateDeployer(parameters); - return (IISDeploymentResult)await _deployer.DeployAsync(); + return _deploymentResult = (IISDeploymentResult)await _deployer.DeployAsync(); } protected virtual async Task StartAsync(IISDeploymentParameters parameters) @@ -55,6 +56,7 @@ public override void Dispose() public void StopServer(bool gracefulShutdown = true) { + _deploymentResult?.Dispose(); _deployer?.Dispose(gracefulShutdown); _deployer = null; } diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Infrastructure/IISTestSiteFixture.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Infrastructure/IISTestSiteFixture.cs index b947fc87fe68..73074454b0c1 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Infrastructure/IISTestSiteFixture.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Infrastructure/IISTestSiteFixture.cs @@ -65,6 +65,7 @@ public TestConnection CreateTestConnection() public void Dispose() { + _deploymentResult?.Dispose(); _deployer?.Dispose(); } diff --git a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/IISExpressShutdownTests.cs b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/IISExpressShutdownTests.cs index 8d9fb104cde4..8c21d2f9a70a 100644 --- a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/IISExpressShutdownTests.cs +++ b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/IISExpressShutdownTests.cs @@ -36,6 +36,10 @@ public async Task ServerShutsDownWhenMainExits() { // Server might close a connection before request completes } + finally + { + deploymentResult.HttpClient.Dispose(); + } deploymentResult.AssertWorkerProcessStop(); } @@ -62,6 +66,10 @@ public async Task ServerShutsDownWhenMainExitsStress() { // Server might close a connection before request completes } + finally + { + deploymentResult.HttpClient.Dispose(); + } deploymentResult.AssertWorkerProcessStop(); } diff --git a/src/Servers/IIS/IntegrationTesting.IIS/src/IISDeploymentResult.cs b/src/Servers/IIS/IntegrationTesting.IIS/src/IISDeploymentResult.cs index 1cf6072b547d..c334d7051166 100644 --- a/src/Servers/IIS/IntegrationTesting.IIS/src/IISDeploymentResult.cs +++ b/src/Servers/IIS/IntegrationTesting.IIS/src/IISDeploymentResult.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS; -public class IISDeploymentResult : DeploymentResult +public class IISDeploymentResult : DeploymentResult, IDisposable { public ILogger Logger { get; set; } @@ -43,5 +43,10 @@ public HttpClient CreateClient(HttpMessageHandler messageHandler) }; } + public void Dispose() + { + HttpClient.Dispose(); + } + public new HttpClient HttpClient { get; set; } } diff --git a/src/Shared/Buffers.MemoryPool/DiagnosticMemoryPool.cs b/src/Shared/Buffers.MemoryPool/DiagnosticMemoryPool.cs index 5c7f2cd686ae..2a35f095e630 100644 --- a/src/Shared/Buffers.MemoryPool/DiagnosticMemoryPool.cs +++ b/src/Shared/Buffers.MemoryPool/DiagnosticMemoryPool.cs @@ -1,9 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace System.Buffers; diff --git a/src/Shared/Buffers.MemoryPool/DiagnosticPoolBlock.cs b/src/Shared/Buffers.MemoryPool/DiagnosticPoolBlock.cs index 8c3ebce812b4..d2c0dfd18917 100644 --- a/src/Shared/Buffers.MemoryPool/DiagnosticPoolBlock.cs +++ b/src/Shared/Buffers.MemoryPool/DiagnosticPoolBlock.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Runtime.InteropServices; -using System.Threading; #nullable enable diff --git a/src/Shared/HttpSys/Constants.cs b/src/Shared/HttpSys/Constants.cs index 2d5695ef4155..42fc10c77978 100644 --- a/src/Shared/HttpSys/Constants.cs +++ b/src/Shared/HttpSys/Constants.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; - namespace Microsoft.AspNetCore.HttpSys.Internal; internal static class Constants diff --git a/src/Shared/HttpSys/NativeInterop/CookedUrl.cs b/src/Shared/HttpSys/NativeInterop/CookedUrl.cs index 784a235365ae..314ea7bbface 100644 --- a/src/Shared/HttpSys/NativeInterop/CookedUrl.cs +++ b/src/Shared/HttpSys/NativeInterop/CookedUrl.cs @@ -1,53 +1,53 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Runtime.InteropServices; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.HttpSys.Internal; // Note this type should only be used while the request buffer remains pinned internal readonly struct CookedUrl { - private readonly HttpApiTypes.HTTP_COOKED_URL _nativeCookedUrl; + private readonly HTTP_COOKED_URL _nativeCookedUrl; - internal CookedUrl(HttpApiTypes.HTTP_COOKED_URL nativeCookedUrl) + internal CookedUrl(HTTP_COOKED_URL nativeCookedUrl) { _nativeCookedUrl = nativeCookedUrl; } internal unsafe string? GetFullUrl() { - if (_nativeCookedUrl.pFullUrl != null && _nativeCookedUrl.FullUrlLength > 0) + if (!_nativeCookedUrl.pFullUrl.Equals(null) && _nativeCookedUrl.FullUrlLength > 0) { - return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pFullUrl, _nativeCookedUrl.FullUrlLength / 2); + return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pFullUrl.Value, _nativeCookedUrl.FullUrlLength / 2); } return null; } internal unsafe string? GetHost() { - if (_nativeCookedUrl.pHost != null && _nativeCookedUrl.HostLength > 0) + if (!_nativeCookedUrl.pHost.Equals(null) && _nativeCookedUrl.HostLength > 0) { - return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pHost, _nativeCookedUrl.HostLength / 2); + return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pHost.Value, _nativeCookedUrl.HostLength / 2); } return null; } internal unsafe string? GetAbsPath() { - if (_nativeCookedUrl.pAbsPath != null && _nativeCookedUrl.AbsPathLength > 0) + if (!_nativeCookedUrl.pAbsPath.Equals(null) && _nativeCookedUrl.AbsPathLength > 0) { - return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pAbsPath, _nativeCookedUrl.AbsPathLength / 2); + return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pAbsPath.Value, _nativeCookedUrl.AbsPathLength / 2); } return null; } internal unsafe string? GetQueryString() { - if (_nativeCookedUrl.pQueryString != null && _nativeCookedUrl.QueryStringLength > 0) + if (!_nativeCookedUrl.pQueryString.Equals(null) && _nativeCookedUrl.QueryStringLength > 0) { - return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pQueryString, _nativeCookedUrl.QueryStringLength / 2); + return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pQueryString.Value, _nativeCookedUrl.QueryStringLength / 2); } return null; } diff --git a/src/Shared/HttpSys/NativeInterop/HeapAllocHandle.cs b/src/Shared/HttpSys/NativeInterop/HeapAllocHandle.cs deleted file mode 100644 index 68c3b26ccb7c..000000000000 --- a/src/Shared/HttpSys/NativeInterop/HeapAllocHandle.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using Microsoft.Win32.SafeHandles; - -namespace Microsoft.AspNetCore.HttpSys.Internal; - -internal sealed class HeapAllocHandle : SafeHandleZeroOrMinusOneIsInvalid -{ - private static readonly IntPtr ProcessHeap = UnsafeNclNativeMethods.GetProcessHeap(); - - // Called by P/Invoke when returning SafeHandles - public HeapAllocHandle() - : base(ownsHandle: true) - { - } - - // Do not provide a finalizer - SafeHandle's critical finalizer will call ReleaseHandle for you. - protected override bool ReleaseHandle() - { - return UnsafeNclNativeMethods.HeapFree(ProcessHeap, 0, handle); - } -} diff --git a/src/Shared/HttpSys/NativeInterop/HttpApiTypes.cs b/src/Shared/HttpSys/NativeInterop/HttpApiTypes.cs index a0a35eaaa6a5..dd8b50636443 100644 --- a/src/Shared/HttpSys/NativeInterop/HttpApiTypes.cs +++ b/src/Shared/HttpSys/NativeInterop/HttpApiTypes.cs @@ -2,810 +2,59 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Security.Authentication; -using Microsoft.AspNetCore.Http; +using System.Collections.Frozen; +using Microsoft.Net.Http.Headers; +using Windows.Win32; namespace Microsoft.AspNetCore.HttpSys.Internal; -#pragma warning disable IDE0044 // Add readonly modifier. We don't want to modify these interop types internal static unsafe class HttpApiTypes { - internal enum HTTP_API_VERSION - { - Invalid, - Version10, - Version20, - } - - // see http.w for definitions - internal enum HTTP_SERVER_PROPERTY - { - HttpServerAuthenticationProperty, - HttpServerLoggingProperty, - HttpServerQosProperty, - HttpServerTimeoutsProperty, - HttpServerQueueLengthProperty, - HttpServerStateProperty, - HttpServer503VerbosityProperty, - HttpServerBindingProperty, - HttpServerExtendedAuthenticationProperty, - HttpServerListenEndpointProperty, - HttpServerChannelBindProperty, - HttpServerProtectionLevelProperty, - HttpServerDelegationProperty = 16 - } - - // Currently only one request info type is supported but the enum is for future extensibility. - - internal enum HTTP_REQUEST_INFO_TYPE - { - HttpRequestInfoTypeAuth, - HttpRequestInfoTypeChannelBind, - HttpRequestInfoTypeSslProtocol, - HttpRequestInfoTypeSslTokenBindingDraft, - HttpRequestInfoTypeSslTokenBinding, - HttpRequestInfoTypeRequestTiming, - HttpRequestInfoTypeTcpInfoV0, - HttpRequestInfoTypeRequestSizing, - HttpRequestInfoTypeQuicStats, - HttpRequestInfoTypeTcpInfoV1, - } - - internal enum HTTP_RESPONSE_INFO_TYPE - { - HttpResponseInfoTypeMultipleKnownHeaders, - HttpResponseInfoTypeAuthenticationProperty, - HttpResponseInfoTypeQosProperty, - } - - internal enum HTTP_REQUEST_PROPERTY - { - HttpRequestPropertyIsb, - HttpRequestPropertyTcpInfoV0, - HttpRequestPropertyQuicStats, - HttpRequestPropertyTcpInfoV1, - HttpRequestPropertySni, - HttpRequestPropertyStreamError, - } - - internal enum HTTP_TIMEOUT_TYPE - { - EntityBody, - DrainEntityBody, - RequestQueue, - IdleConnection, - HeaderWait, - MinSendRate, - } - - internal enum HTTP_DELEGATE_REQUEST_PROPERTY_ID : uint - { - DelegateRequestReservedProperty, - DelegateRequestDelegateUrlProperty - } - - internal enum HTTP_FEATURE_ID - { - HttpFeatureUnknown, - HttpFeatureResponseTrailers, - HttpFeatureApiTimings, - HttpFeatureDelegateEx, - } - - [StructLayout(LayoutKind.Sequential, Pack = 4)] - internal struct HTTP_DELEGATE_REQUEST_PROPERTY_INFO - { - internal HTTP_DELEGATE_REQUEST_PROPERTY_ID PropertyId; - internal uint PropertyInfoLength; - internal IntPtr PropertyInfo; - } - - internal struct HTTP_REQUEST_PROPERTY_STREAM_ERROR - { - internal uint ErrorCode; - } - - private const int HTTP_REQUEST_PROPERTY_SNI_HOST_MAX_LENGTH = 255; - internal const int SniPropertySizeInBytes = (sizeof(ushort) * (HTTP_REQUEST_PROPERTY_SNI_HOST_MAX_LENGTH + 1)) + sizeof(uint); - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Size = SniPropertySizeInBytes)] - internal struct HTTP_REQUEST_PROPERTY_SNI - { - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HTTP_REQUEST_PROPERTY_SNI_HOST_MAX_LENGTH + 1)] - internal string Hostname; - - internal HTTP_REQUEST_PROPERTY_SNI_FLAGS Flags; - } - - [Flags] - internal enum HTTP_REQUEST_PROPERTY_SNI_FLAGS : uint - { - // Indicates that SNI was used for successful endpoint lookup during handshake. - // If client sent the SNI but Http.sys still decided to use IP endpoint binding then this flag will not be set. - HTTP_REQUEST_PROPERTY_SNI_FLAG_SNI_USED = 0x00000001, - // Indicates that client did not send the SNI. - HTTP_REQUEST_PROPERTY_SNI_FLAG_NO_SNI = 0x00000002, - } - - internal const int MaxTimeout = 6; - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_VERSION - { - internal ushort MajorVersion; - internal ushort MinorVersion; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_KNOWN_HEADER - { - internal ushort RawValueLength; - internal byte* pRawValue; - } - - [StructLayout(LayoutKind.Explicit)] - internal struct HTTP_DATA_CHUNK - { - [FieldOffset(0)] - internal HTTP_DATA_CHUNK_TYPE DataChunkType; - - [FieldOffset(8)] - internal FromMemory fromMemory; - - [FieldOffset(8)] - internal FromFileHandle fromFile; - - [FieldOffset(8)] - internal Trailers trailers; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct FromMemory - { - // 4 bytes for 32bit, 8 bytes for 64bit - internal IntPtr pBuffer; - internal uint BufferLength; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct FromFileHandle - { - internal ulong offset; - internal ulong count; - internal IntPtr fileHandle; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct Trailers - { - internal ushort trailerCount; - internal IntPtr pTrailers; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTPAPI_VERSION - { - internal ushort HttpApiMajorVersion; - internal ushort HttpApiMinorVersion; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_COOKED_URL - { - internal ushort FullUrlLength; - internal ushort HostLength; - internal ushort AbsPathLength; - internal ushort QueryStringLength; - internal ushort* pFullUrl; - internal ushort* pHost; - internal ushort* pAbsPath; - internal ushort* pQueryString; - } - - // Only cache unauthorized GETs + HEADs. - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_CACHE_POLICY - { - internal HTTP_CACHE_POLICY_TYPE Policy; - internal uint SecondsToLive; - } - - internal enum HTTP_CACHE_POLICY_TYPE : int - { - HttpCachePolicyNocache = 0, - HttpCachePolicyUserInvalidates = 1, - HttpCachePolicyTimeToLive = 2, - } - - [StructLayout(LayoutKind.Sequential)] - internal struct SOCKADDR - { - internal ushort sa_family; - internal byte sa_data; - internal byte sa_data_02; - internal byte sa_data_03; - internal byte sa_data_04; - internal byte sa_data_05; - internal byte sa_data_06; - internal byte sa_data_07; - internal byte sa_data_08; - internal byte sa_data_09; - internal byte sa_data_10; - internal byte sa_data_11; - internal byte sa_data_12; - internal byte sa_data_13; - internal byte sa_data_14; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_TRANSPORT_ADDRESS - { - internal SOCKADDR* pRemoteAddress; - internal SOCKADDR* pLocalAddress; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_SSL_CLIENT_CERT_INFO - { - internal uint CertFlags; - internal uint CertEncodedSize; - internal byte* pCertEncoded; - internal void* Token; - internal byte CertDeniedByMapper; - } - - internal enum HTTP_SERVICE_BINDING_TYPE : uint - { - HttpServiceBindingTypeNone = 0, - HttpServiceBindingTypeW, - HttpServiceBindingTypeA - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_SERVICE_BINDING_BASE - { - internal HTTP_SERVICE_BINDING_TYPE Type; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_REQUEST_CHANNEL_BIND_STATUS - { - internal IntPtr ServiceName; - internal IntPtr ChannelToken; - internal uint ChannelTokenSize; - internal uint Flags; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_UNKNOWN_HEADER - { - internal ushort NameLength; - internal ushort RawValueLength; - internal byte* pName; - internal byte* pRawValue; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_SSL_INFO - { - internal ushort ServerCertKeySize; - internal ushort ConnectionKeySize; - internal uint ServerCertIssuerSize; - internal uint ServerCertSubjectSize; - internal byte* pServerCertIssuer; - internal byte* pServerCertSubject; - internal HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo; - internal uint SslClientCertNegotiated; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_RESPONSE_HEADERS - { - internal ushort UnknownHeaderCount; - internal HTTP_UNKNOWN_HEADER* pUnknownHeaders; - internal ushort TrailerCount; - internal HTTP_UNKNOWN_HEADER* pTrailers; - internal HTTP_KNOWN_HEADER KnownHeaders; - internal HTTP_KNOWN_HEADER KnownHeaders_02; - internal HTTP_KNOWN_HEADER KnownHeaders_03; - internal HTTP_KNOWN_HEADER KnownHeaders_04; - internal HTTP_KNOWN_HEADER KnownHeaders_05; - internal HTTP_KNOWN_HEADER KnownHeaders_06; - internal HTTP_KNOWN_HEADER KnownHeaders_07; - internal HTTP_KNOWN_HEADER KnownHeaders_08; - internal HTTP_KNOWN_HEADER KnownHeaders_09; - internal HTTP_KNOWN_HEADER KnownHeaders_10; - internal HTTP_KNOWN_HEADER KnownHeaders_11; - internal HTTP_KNOWN_HEADER KnownHeaders_12; - internal HTTP_KNOWN_HEADER KnownHeaders_13; - internal HTTP_KNOWN_HEADER KnownHeaders_14; - internal HTTP_KNOWN_HEADER KnownHeaders_15; - internal HTTP_KNOWN_HEADER KnownHeaders_16; - internal HTTP_KNOWN_HEADER KnownHeaders_17; - internal HTTP_KNOWN_HEADER KnownHeaders_18; - internal HTTP_KNOWN_HEADER KnownHeaders_19; - internal HTTP_KNOWN_HEADER KnownHeaders_20; - internal HTTP_KNOWN_HEADER KnownHeaders_21; - internal HTTP_KNOWN_HEADER KnownHeaders_22; - internal HTTP_KNOWN_HEADER KnownHeaders_23; - internal HTTP_KNOWN_HEADER KnownHeaders_24; - internal HTTP_KNOWN_HEADER KnownHeaders_25; - internal HTTP_KNOWN_HEADER KnownHeaders_26; - internal HTTP_KNOWN_HEADER KnownHeaders_27; - internal HTTP_KNOWN_HEADER KnownHeaders_28; - internal HTTP_KNOWN_HEADER KnownHeaders_29; - internal HTTP_KNOWN_HEADER KnownHeaders_30; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_REQUEST_HEADERS - { - internal ushort UnknownHeaderCount; - internal HTTP_UNKNOWN_HEADER* pUnknownHeaders; - internal ushort TrailerCount; - internal HTTP_UNKNOWN_HEADER* pTrailers; - internal HTTP_KNOWN_HEADER KnownHeaders; - internal HTTP_KNOWN_HEADER KnownHeaders_02; - internal HTTP_KNOWN_HEADER KnownHeaders_03; - internal HTTP_KNOWN_HEADER KnownHeaders_04; - internal HTTP_KNOWN_HEADER KnownHeaders_05; - internal HTTP_KNOWN_HEADER KnownHeaders_06; - internal HTTP_KNOWN_HEADER KnownHeaders_07; - internal HTTP_KNOWN_HEADER KnownHeaders_08; - internal HTTP_KNOWN_HEADER KnownHeaders_09; - internal HTTP_KNOWN_HEADER KnownHeaders_10; - internal HTTP_KNOWN_HEADER KnownHeaders_11; - internal HTTP_KNOWN_HEADER KnownHeaders_12; - internal HTTP_KNOWN_HEADER KnownHeaders_13; - internal HTTP_KNOWN_HEADER KnownHeaders_14; - internal HTTP_KNOWN_HEADER KnownHeaders_15; - internal HTTP_KNOWN_HEADER KnownHeaders_16; - internal HTTP_KNOWN_HEADER KnownHeaders_17; - internal HTTP_KNOWN_HEADER KnownHeaders_18; - internal HTTP_KNOWN_HEADER KnownHeaders_19; - internal HTTP_KNOWN_HEADER KnownHeaders_20; - internal HTTP_KNOWN_HEADER KnownHeaders_21; - internal HTTP_KNOWN_HEADER KnownHeaders_22; - internal HTTP_KNOWN_HEADER KnownHeaders_23; - internal HTTP_KNOWN_HEADER KnownHeaders_24; - internal HTTP_KNOWN_HEADER KnownHeaders_25; - internal HTTP_KNOWN_HEADER KnownHeaders_26; - internal HTTP_KNOWN_HEADER KnownHeaders_27; - internal HTTP_KNOWN_HEADER KnownHeaders_28; - internal HTTP_KNOWN_HEADER KnownHeaders_29; - internal HTTP_KNOWN_HEADER KnownHeaders_30; - internal HTTP_KNOWN_HEADER KnownHeaders_31; - internal HTTP_KNOWN_HEADER KnownHeaders_32; - internal HTTP_KNOWN_HEADER KnownHeaders_33; - internal HTTP_KNOWN_HEADER KnownHeaders_34; - internal HTTP_KNOWN_HEADER KnownHeaders_35; - internal HTTP_KNOWN_HEADER KnownHeaders_36; - internal HTTP_KNOWN_HEADER KnownHeaders_37; - internal HTTP_KNOWN_HEADER KnownHeaders_38; - internal HTTP_KNOWN_HEADER KnownHeaders_39; - internal HTTP_KNOWN_HEADER KnownHeaders_40; - internal HTTP_KNOWN_HEADER KnownHeaders_41; - } - - internal enum HTTP_VERB : int - { - HttpVerbUnparsed = 0, - HttpVerbUnknown = 1, - HttpVerbInvalid = 2, - HttpVerbOPTIONS = 3, - HttpVerbGET = 4, - HttpVerbHEAD = 5, - HttpVerbPOST = 6, - HttpVerbPUT = 7, - HttpVerbDELETE = 8, - HttpVerbTRACE = 9, - HttpVerbCONNECT = 10, - HttpVerbTRACK = 11, - HttpVerbMOVE = 12, - HttpVerbCOPY = 13, - HttpVerbPROPFIND = 14, - HttpVerbPROPPATCH = 15, - HttpVerbMKCOL = 16, - HttpVerbLOCK = 17, - HttpVerbUNLOCK = 18, - HttpVerbSEARCH = 19, - HttpVerbMaximum = 20, - } - - internal static readonly string?[] HttpVerbs = new string?[] - { - null, - "Unknown", - "Invalid", - HttpMethods.Options, - HttpMethods.Get, - HttpMethods.Head, - HttpMethods.Post, - HttpMethods.Put, - HttpMethods.Delete, - HttpMethods.Trace, - HttpMethods.Connect, - "TRACK", - "MOVE", - "COPY", - "PROPFIND", - "PROPPATCH", - "MKCOL", - "LOCK", - "UNLOCK", - "SEARCH", - }; - - internal enum HTTP_DATA_CHUNK_TYPE : int - { - HttpDataChunkFromMemory, - HttpDataChunkFromFileHandle, - HttpDataChunkFromFragmentCache, - HttpDataChunkFromFragmentCacheEx, - HttpDataChunkTrailers, - HttpDataChunkMaximum, - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_RESPONSE_INFO - { - internal HTTP_RESPONSE_INFO_TYPE Type; - internal uint Length; - internal HTTP_MULTIPLE_KNOWN_HEADERS* pInfo; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_RESPONSE - { - internal uint Flags; - internal HTTP_VERSION Version; - internal ushort StatusCode; - internal ushort ReasonLength; - internal byte* pReason; - internal HTTP_RESPONSE_HEADERS Headers; - internal ushort EntityChunkCount; - internal HTTP_DATA_CHUNK* pEntityChunks; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_RESPONSE_V2 - { - internal HTTP_RESPONSE Response_V1; - internal ushort ResponseInfoCount; - internal HTTP_RESPONSE_INFO* pResponseInfo; - } - - internal enum HTTP_RESPONSE_INFO_FLAGS : uint - { - None = 0, - PreserveOrder = 1, - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_MULTIPLE_KNOWN_HEADERS - { - internal HTTP_RESPONSE_HEADER_ID.Enum HeaderId; - internal HTTP_RESPONSE_INFO_FLAGS Flags; - internal ushort KnownHeaderCount; - internal HTTP_KNOWN_HEADER* KnownHeaders; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_REQUEST_AUTH_INFO - { - internal HTTP_AUTH_STATUS AuthStatus; - internal uint SecStatus; - internal uint Flags; - internal HTTP_REQUEST_AUTH_TYPE AuthType; - internal IntPtr AccessToken; - internal uint ContextAttributes; - internal uint PackedContextLength; - internal uint PackedContextType; - internal IntPtr PackedContext; - internal uint MutualAuthDataLength; - internal char* pMutualAuthData; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_SSL_PROTOCOL_INFO - { - internal SslProtocols Protocol; - internal CipherAlgorithmType CipherType; - internal uint CipherStrength; - internal HashAlgorithmType HashType; - internal uint HashStrength; - internal ExchangeAlgorithmType KeyExchangeType; - internal uint KeyExchangeStrength; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_REQUEST_INFO - { - internal HTTP_REQUEST_INFO_TYPE InfoType; - internal uint InfoLength; - internal void* pInfo; - } - - [Flags] - internal enum HTTP_REQUEST_FLAGS - { - None = 0, - MoreEntityBodyExists = 1, - IPRouted = 2, - Http2 = 4, - Http3 = 8, - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_REQUEST - { - internal HTTP_REQUEST_FLAGS Flags; - internal ulong ConnectionId; - internal ulong RequestId; - internal ulong UrlContext; - internal HTTP_VERSION Version; - internal HTTP_VERB Verb; - internal ushort UnknownVerbLength; - internal ushort RawUrlLength; - internal byte* pUnknownVerb; - internal byte* pRawUrl; - internal HTTP_COOKED_URL CookedUrl; - internal HTTP_TRANSPORT_ADDRESS Address; - internal HTTP_REQUEST_HEADERS Headers; - internal ulong BytesReceived; - internal ushort EntityChunkCount; - internal HTTP_DATA_CHUNK* pEntityChunks; - internal ulong RawConnectionId; - internal HTTP_SSL_INFO* pSslInfo; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_REQUEST_V2 - { - internal HTTP_REQUEST Request; - internal ushort RequestInfoCount; - internal HTTP_REQUEST_INFO* pRequestInfo; - } - - internal enum HTTP_AUTH_STATUS - { - HttpAuthStatusSuccess, - HttpAuthStatusNotAuthenticated, - HttpAuthStatusFailure, - } - - internal enum HTTP_REQUEST_AUTH_TYPE - { - HttpRequestAuthTypeNone = 0, - HttpRequestAuthTypeBasic, - HttpRequestAuthTypeDigest, - HttpRequestAuthTypeNTLM, - HttpRequestAuthTypeNegotiate, - HttpRequestAuthTypeKerberos - } - - internal enum HTTP_QOS_SETTING_TYPE - { - HttpQosSettingTypeBandwidth, - HttpQosSettingTypeConnectionLimit, - HttpQosSettingTypeFlowRate - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_SERVER_AUTHENTICATION_INFO - { - internal HTTP_FLAGS Flags; - internal HTTP_AUTH_TYPES AuthSchemes; - internal bool ReceiveMutualAuth; - internal bool ReceiveContextHandle; - internal bool DisableNTLMCredentialCaching; - internal ulong ExFlags; - HTTP_SERVER_AUTHENTICATION_DIGEST_PARAMS DigestParams; - HTTP_SERVER_AUTHENTICATION_BASIC_PARAMS BasicParams; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_SERVER_AUTHENTICATION_DIGEST_PARAMS - { - internal ushort DomainNameLength; - internal char* DomainName; - internal ushort RealmLength; - internal char* Realm; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_SERVER_AUTHENTICATION_BASIC_PARAMS - { - ushort RealmLength; - char* Realm; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_REQUEST_TOKEN_BINDING_INFO - { - public byte* TokenBinding; - public uint TokenBindingSize; - - public byte* TlsUnique; - public uint TlsUniqueSize; - - public char* KeyType; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_TIMEOUT_LIMIT_INFO - { - internal HTTP_FLAGS Flags; - internal ushort EntityBody; - internal ushort DrainEntityBody; - internal ushort RequestQueue; - internal ushort IdleConnection; - internal ushort HeaderWait; - internal uint MinSendRate; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_BINDING_INFO - { - internal HTTP_FLAGS Flags; - internal IntPtr RequestQueueHandle; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_CONNECTION_LIMIT_INFO - { - internal HTTP_FLAGS Flags; - internal uint MaxConnections; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct HTTP_QOS_SETTING_INFO - { - internal HTTP_QOS_SETTING_TYPE QosType; - internal IntPtr QosSetting; - } - - // see http.w for definitions - [Flags] - internal enum HTTP_FLAGS : uint - { - NONE = 0x00000000, - HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY = 0x00000001, - HTTP_RECEIVE_SECURE_CHANNEL_TOKEN = 0x00000001, - HTTP_SEND_RESPONSE_FLAG_DISCONNECT = 0x00000001, - HTTP_SEND_RESPONSE_FLAG_MORE_DATA = 0x00000002, - HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA = 0x00000004, - HTTP_SEND_RESPONSE_FLAG_RAW_HEADER = 0x00000004, - HTTP_SEND_REQUEST_FLAG_MORE_DATA = 0x00000001, - HTTP_PROPERTY_FLAG_PRESENT = 0x00000001, - HTTP_INITIALIZE_SERVER = 0x00000001, - HTTP_INITIALIZE_CONFIG = 0x00000002, - HTTP_INITIALIZE_CBT = 0x00000004, - HTTP_SEND_RESPONSE_FLAG_OPAQUE = 0x00000040, - HTTP_SEND_RESPONSE_FLAG_GOAWAY = 0x00000100, - } - - [Flags] - internal enum HTTP_AUTH_TYPES : uint - { - NONE = 0x00000000, - HTTP_AUTH_ENABLE_BASIC = 0x00000001, - HTTP_AUTH_ENABLE_DIGEST = 0x00000002, - HTTP_AUTH_ENABLE_NTLM = 0x00000004, - HTTP_AUTH_ENABLE_NEGOTIATE = 0x00000008, - HTTP_AUTH_ENABLE_KERBEROS = 0x00000010, - } - - [Flags] - internal enum HTTP_CREATE_REQUEST_QUEUE_FLAG : uint - { - None = 0, - // The HTTP_CREATE_REQUEST_QUEUE_FLAG_OPEN_EXISTING flag allows applications to open an existing request queue by name and retrieve the request queue handle. The pName parameter must contain a valid request queue name; it cannot be NULL. - OpenExisting = 1, - // The handle to the request queue created using this flag cannot be used to perform I/O operations. This flag can be set only when the request queue handle is created. - Controller = 2, - Delegation = 8 - } - - internal static class HTTP_RESPONSE_HEADER_ID - { - private static readonly string[] _strings = - { - "Cache-Control", - "Connection", - "Date", - "Keep-Alive", - "Pragma", - "Trailer", - "Transfer-Encoding", - "Upgrade", - "Via", - "Warning", - - "Allow", - "Content-Length", - "Content-Type", - "Content-Encoding", - "Content-Language", - "Content-Location", - "Content-MD5", - "Content-Range", - "Expires", - "Last-Modified", - - "Accept-Ranges", - "Age", - "ETag", - "Location", - "Proxy-Authenticate", - "Retry-After", - "Server", - "Set-Cookie", - "Vary", - "WWW-Authenticate", - }; - - private static readonly Dictionary _lookupTable = CreateLookupTable(); - - private static Dictionary CreateLookupTable() - { - Dictionary lookupTable = new Dictionary((int)Enum.HttpHeaderResponseMaximum, StringComparer.OrdinalIgnoreCase); - for (int i = 0; i < (int)Enum.HttpHeaderResponseMaximum; i++) - { - lookupTable.Add(_strings[i], i); - } - return lookupTable; - } - - internal static int IndexOfKnownHeader(string HeaderName) - { - int index; - return _lookupTable.TryGetValue(HeaderName, out index) ? index : -1; - } - - internal enum Enum - { - HttpHeaderCacheControl = 0, // general-header [section 4.5] - HttpHeaderConnection = 1, // general-header [section 4.5] - HttpHeaderDate = 2, // general-header [section 4.5] - HttpHeaderKeepAlive = 3, // general-header [not in rfc] - HttpHeaderPragma = 4, // general-header [section 4.5] - HttpHeaderTrailer = 5, // general-header [section 4.5] - HttpHeaderTransferEncoding = 6, // general-header [section 4.5] - HttpHeaderUpgrade = 7, // general-header [section 4.5] - HttpHeaderVia = 8, // general-header [section 4.5] - HttpHeaderWarning = 9, // general-header [section 4.5] - - HttpHeaderAllow = 10, // entity-header [section 7.1] - HttpHeaderContentLength = 11, // entity-header [section 7.1] - HttpHeaderContentType = 12, // entity-header [section 7.1] - HttpHeaderContentEncoding = 13, // entity-header [section 7.1] - HttpHeaderContentLanguage = 14, // entity-header [section 7.1] - HttpHeaderContentLocation = 15, // entity-header [section 7.1] - HttpHeaderContentMd5 = 16, // entity-header [section 7.1] - HttpHeaderContentRange = 17, // entity-header [section 7.1] - HttpHeaderExpires = 18, // entity-header [section 7.1] - HttpHeaderLastModified = 19, // entity-header [section 7.1] - - // Response Headers - - HttpHeaderAcceptRanges = 20, // response-header [section 6.2] - HttpHeaderAge = 21, // response-header [section 6.2] - HttpHeaderEtag = 22, // response-header [section 6.2] - HttpHeaderLocation = 23, // response-header [section 6.2] - HttpHeaderProxyAuthenticate = 24, // response-header [section 6.2] - HttpHeaderRetryAfter = 25, // response-header [section 6.2] - HttpHeaderServer = 26, // response-header [section 6.2] - HttpHeaderSetCookie = 27, // response-header [not in rfc] - HttpHeaderVary = 28, // response-header [section 6.2] - HttpHeaderWwwAuthenticate = 29, // response-header [section 6.2] - - HttpHeaderResponseMaximum = 30, - - HttpHeaderMaximum = 41 - } + // 255 + the null terminator + internal const int SniPropertySizeInBytes = (int)((sizeof(ushort) * (PInvoke.HTTP_REQUEST_PROPERTY_SNI_HOST_MAX_LENGTH + 1)) + sizeof(uint)); + + internal static FrozenDictionary KnownResponseHeaders { get; } = CreateLookupTable(); + + private static FrozenDictionary CreateLookupTable() + { + // See https://learn.microsoft.com/en-us/windows/win32/api/http/ne-http-http_header_id + string[] headerNames = + [ + HeaderNames.CacheControl, + HeaderNames.Connection, + HeaderNames.Date, + HeaderNames.KeepAlive, + HeaderNames.Pragma, + HeaderNames.Trailer, + HeaderNames.TransferEncoding, + HeaderNames.Upgrade, + HeaderNames.Via, + HeaderNames.Warning, + + HeaderNames.Allow, + HeaderNames.ContentLength, + HeaderNames.ContentType, + HeaderNames.ContentEncoding, + HeaderNames.ContentLanguage, + HeaderNames.ContentLocation, + HeaderNames.ContentMD5, + HeaderNames.ContentRange, + HeaderNames.Expires, + HeaderNames.LastModified, + + HeaderNames.AcceptRanges, + HeaderNames.Age, + HeaderNames.ETag, + HeaderNames.Location, + HeaderNames.ProxyAuthenticate, + HeaderNames.RetryAfter, + HeaderNames.Server, + HeaderNames.SetCookie, + HeaderNames.Vary, + HeaderNames.WWWAuthenticate, + ]; + + var index = 0; + return headerNames.ToFrozenDictionary(s => s, _ => index++, StringComparer.OrdinalIgnoreCase); } } diff --git a/src/Shared/HttpSys/NativeInterop/NclUtilities.cs b/src/Shared/HttpSys/NativeInterop/NclUtilities.cs deleted file mode 100644 index 5c25a7e08cb4..000000000000 --- a/src/Shared/HttpSys/NativeInterop/NclUtilities.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace Microsoft.AspNetCore.HttpSys.Internal; - -internal static class NclUtilities -{ - internal static bool HasShutdownStarted - { - get - { - return Environment.HasShutdownStarted - || AppDomain.CurrentDomain.IsFinalizingForUnload(); - } - } -} diff --git a/src/Shared/HttpSys/NativeInterop/SafeLocalFreeChannelBinding.cs b/src/Shared/HttpSys/NativeInterop/SafeLocalFreeChannelBinding.cs deleted file mode 100644 index 5a85bba6a2d3..000000000000 --- a/src/Shared/HttpSys/NativeInterop/SafeLocalFreeChannelBinding.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Security.Authentication.ExtendedProtection; - -namespace Microsoft.AspNetCore.HttpSys.Internal; - -internal sealed class SafeLocalFreeChannelBinding : ChannelBinding -{ - private const int LMEM_FIXED = 0; - private int size; - - public override int Size - { - get { return size; } - } - - public static SafeLocalFreeChannelBinding LocalAlloc(int cb) - { - SafeLocalFreeChannelBinding result; - - result = UnsafeNclNativeMethods.SafeNetHandles.LocalAllocChannelBinding(LMEM_FIXED, (UIntPtr)cb); - if (result.IsInvalid) - { - result.SetHandleAsInvalid(); -#pragma warning disable CA2201 // Do not raise reserved exception types - throw new OutOfMemoryException(); -#pragma warning restore CA2201 - } - - result.size = cb; - return result; - } - - protected override bool ReleaseHandle() - { - return UnsafeNclNativeMethods.SafeNetHandles.LocalFree(handle) == IntPtr.Zero; - } - - public override bool IsInvalid - { - get - { - return handle == IntPtr.Zero || handle.ToInt32() == -1; - } - } -} diff --git a/src/Shared/HttpSys/NativeInterop/SafeLocalMemHandle.cs b/src/Shared/HttpSys/NativeInterop/SafeLocalMemHandle.cs deleted file mode 100644 index 049f8abc00fa..000000000000 --- a/src/Shared/HttpSys/NativeInterop/SafeLocalMemHandle.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using Microsoft.Win32.SafeHandles; - -namespace Microsoft.AspNetCore.HttpSys.Internal; - -internal sealed class SafeLocalMemHandle : SafeHandleZeroOrMinusOneIsInvalid -{ - public SafeLocalMemHandle() - : base(true) - { - } - - internal SafeLocalMemHandle(IntPtr existingHandle, bool ownsHandle) - : base(ownsHandle) - { - SetHandle(existingHandle); - } - - protected override bool ReleaseHandle() - { - return UnsafeNclNativeMethods.SafeNetHandles.LocalFree(handle) == IntPtr.Zero; - } -} diff --git a/src/Shared/HttpSys/NativeInterop/SocketAddress.cs b/src/Shared/HttpSys/NativeInterop/SocketAddress.cs index 2d1254289f6f..a75ea57ba739 100644 --- a/src/Shared/HttpSys/NativeInterop/SocketAddress.cs +++ b/src/Shared/HttpSys/NativeInterop/SocketAddress.cs @@ -1,166 +1,36 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; using System.Diagnostics.Contracts; -using System.Globalization; using System.Net; using System.Net.Sockets; -using System.Text; namespace Microsoft.AspNetCore.HttpSys.Internal; -// a little perf app measured these times when comparing the internal -// buffer implemented as a managed byte[] or unmanaged memory IntPtr -// that's why we use byte[] -// byte[] total ms:19656 -// IntPtr total ms:25671 - -/// -/// -/// This class is used when subclassing EndPoint, and provides indication -/// on how to format the memory buffers that winsock uses for network addresses. -/// -/// internal sealed class SocketAddress { private const int NumberOfIPv6Labels = 8; - // Lower case hex, no leading zeros - private const string IPv6NumberFormat = "{0:x}"; - private const char IPv6StringSeparator = ':'; - private const string IPv4StringFormat = "{0:d}.{1:d}.{2:d}.{3:d}"; - - internal const int IPv6AddressSize = 28; - internal const int IPv4AddressSize = 16; - + private const int IPv6AddressSize = 28; + private const int IPv4AddressSize = 16; private const int WriteableOffset = 2; - private readonly int _size; private readonly byte[] _buffer; - private int _hash; + private readonly int _size; - /// - /// [To be supplied.] - /// - public SocketAddress(AddressFamily family, int size) + private SocketAddress(AddressFamily family, int size) { ArgumentOutOfRangeException.ThrowIfLessThan(size, WriteableOffset); + Family = family; _size = size; + // Sized to match the native structure _buffer = new byte[((size / IntPtr.Size) + 2) * IntPtr.Size]; // sizeof DWORD - -#if BIGENDIAN - m_Buffer[0] = unchecked((byte)((int)family>>8)); - m_Buffer[1] = unchecked((byte)((int)family )); -#else - _buffer[0] = unchecked((byte)((int)family)); - _buffer[1] = unchecked((byte)((int)family >> 8)); -#endif - } - - internal byte[] Buffer - { - get { return _buffer; } - } - - internal AddressFamily Family - { - get - { - int family; -#if BIGENDIAN - family = ((int)m_Buffer[0]<<8) | m_Buffer[1]; -#else - family = _buffer[0] | ((int)_buffer[1] << 8); -#endif - return (AddressFamily)family; - } - } - - internal int Size - { - get - { - return _size; - } } - // access to unmanaged serialized data. this doesn't - // allow access to the first 2 bytes of unmanaged memory - // that are supposed to contain the address family which - // is readonly. - // - // you can still use negative offsets as a back door in case - // winsock changes the way it uses SOCKADDR. maybe we want to prohibit it? - // maybe we should make the class sealed to avoid potentially dangerous calls - // into winsock with unproperly formatted data? - - /// - /// [To be supplied.] - /// - private byte this[int offset] - { - get - { - // access - if (offset < 0 || offset >= Size) - { - throw new ArgumentOutOfRangeException(nameof(offset)); - } - return _buffer[offset]; - } - } + internal AddressFamily Family { get; } internal int GetPort() { - return (int)((_buffer[2] << 8 & 0xFF00) | (_buffer[3])); - } - - public override bool Equals(object? comparand) - { - SocketAddress? castedComparand = comparand as SocketAddress; - if (castedComparand == null || this.Size != castedComparand.Size) - { - return false; - } - for (int i = 0; i < this.Size; i++) - { - if (this[i] != castedComparand[i]) - { - return false; - } - } - return true; - } - - public override int GetHashCode() - { - if (_hash == 0) - { - int i; - int size = Size & ~3; - - for (i = 0; i < size; i += 4) - { - _hash ^= (int)_buffer[i] - | ((int)_buffer[i + 1] << 8) - | ((int)_buffer[i + 2] << 16) - | ((int)_buffer[i + 3] << 24); - } - if ((Size & 3) != 0) - { - int remnant = 0; - int shift = 0; - - for (; i < Size; ++i) - { - remnant |= ((int)_buffer[i]) << shift; - shift += 8; - } - _hash ^= remnant; - } - } - return _hash; + return (_buffer[2] << 8 & 0xFF00) | (_buffer[3]); } internal IPAddress? GetIPAddress() @@ -181,184 +51,46 @@ public override int GetHashCode() private IPAddress GetIpv6Address() { - Contract.Assert(Size >= IPv6AddressSize); - byte[] bytes = new byte[NumberOfIPv6Labels * 2]; + Contract.Assert(_size >= IPv6AddressSize); + var bytes = new byte[NumberOfIPv6Labels * 2]; Array.Copy(_buffer, 8, bytes, 0, NumberOfIPv6Labels * 2); return new IPAddress(bytes); // TODO: Does scope id matter? } private IPAddress GetIPv4Address() { - Contract.Assert(Size >= IPv4AddressSize); + Contract.Assert(_size >= IPv4AddressSize); return new IPAddress(new byte[] { _buffer[4], _buffer[5], _buffer[6], _buffer[7] }); } - public override string ToString() + internal static unsafe SocketAddress? CopyOutAddress(IntPtr address) { - StringBuilder bytes = new StringBuilder(); - for (int i = WriteableOffset; i < this.Size; i++) + var addressFamily = *((ushort*)address); + if (addressFamily == (ushort)AddressFamily.InterNetwork) { - if (i > WriteableOffset) + var v4address = new SocketAddress(AddressFamily.InterNetwork, IPv4AddressSize); + fixed (byte* pBuffer = v4address._buffer) { - bytes.Append(','); - } - bytes.Append(this[i].ToString(NumberFormatInfo.InvariantInfo)); - } - return Family.ToString() + ":" + Size.ToString(NumberFormatInfo.InvariantInfo) + ":{" + bytes.ToString() + "}"; - } - - internal string? GetIPAddressString() - { - if (Family == AddressFamily.InterNetworkV6) - { - return GetIpv6AddressString(); - } - else if (Family == AddressFamily.InterNetwork) - { - return GetIPv4AddressString(); - } - else - { - return null; - } - } - - private string GetIPv4AddressString() - { - Contract.Assert(Size >= IPv4AddressSize); - - return string.Format(CultureInfo.InvariantCulture, IPv4StringFormat, - _buffer[4], _buffer[5], _buffer[6], _buffer[7]); - } - - // TODO: Does scope ID ever matter? - private unsafe string GetIpv6AddressString() - { - Contract.Assert(Size >= IPv6AddressSize); - - fixed (byte* rawBytes = _buffer) - { - // Convert from bytes to shorts. - ushort* rawShorts = stackalloc ushort[NumberOfIPv6Labels]; - int numbersOffset = 0; - // The address doesn't start at the beginning of the buffer. - for (int i = 8; i < ((NumberOfIPv6Labels * 2) + 8); i += 2) - { - rawShorts[numbersOffset++] = (ushort)(rawBytes[i] << 8 | rawBytes[i + 1]); - } - return GetIPv6AddressString(rawShorts); - } - } - - private static unsafe string GetIPv6AddressString(ushort* numbers) - { - // RFC 5952 Sections 4 & 5 - Compressed, lower case, with possible embedded IPv4 addresses. - - // Start to finish, inclusive. <-1, -1> for no compression - KeyValuePair range = FindCompressionRange(numbers); - bool ipv4Embedded = ShouldHaveIpv4Embedded(numbers); - - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < NumberOfIPv6Labels; i++) - { - if (ipv4Embedded && i == (NumberOfIPv6Labels - 2)) - { - // Write the remaining digits as an IPv4 address - builder.Append(IPv6StringSeparator); - builder.Append(string.Format(CultureInfo.InvariantCulture, IPv4StringFormat, - numbers[i] >> 8, numbers[i] & 0xFF, numbers[i + 1] >> 8, numbers[i + 1] & 0xFF)); - break; - } - - // Compression; 1::1, ::1, 1:: - if (range.Key == i) - { - // Start compression, add : - builder.Append(IPv6StringSeparator); - } - if (range.Key <= i && range.Value == (NumberOfIPv6Labels - 1)) - { - // Remainder compressed; 1:: - builder.Append(IPv6StringSeparator); - break; - } - if (range.Key <= i && i <= range.Value) - { - continue; // Compressed - } - - if (i != 0) - { - builder.Append(IPv6StringSeparator); - } - builder.Append(string.Format(CultureInfo.InvariantCulture, IPv6NumberFormat, numbers[i])); - } - - return builder.ToString(); - } - - // RFC 5952 Section 4.2.3 - // Longest consecutive sequence of zero segments, minimum 2. - // On equal, first sequence wins. - // <-1, -1> for no compression. - private static unsafe KeyValuePair FindCompressionRange(ushort* numbers) - { - int longestSequenceLength = 0; - int longestSequenceStart = -1; - - int currentSequenceLength = 0; - for (int i = 0; i < NumberOfIPv6Labels; i++) - { - if (numbers[i] == 0) - { - // In a sequence - currentSequenceLength++; - if (currentSequenceLength > longestSequenceLength) + for (var index = 2; index < IPv4AddressSize; index++) { - longestSequenceLength = currentSequenceLength; - longestSequenceStart = i - currentSequenceLength + 1; + pBuffer[index] = ((byte*)address)[index]; } } - else - { - currentSequenceLength = 0; - } + return v4address; } - - if (longestSequenceLength >= 2) - { - return new KeyValuePair(longestSequenceStart, - longestSequenceStart + longestSequenceLength - 1); - } - - return new KeyValuePair(-1, -1); // No compression - } - - // Returns true if the IPv6 address should be formated with an embedded IPv4 address: - // ::192.168.1.1 - private static unsafe bool ShouldHaveIpv4Embedded(ushort* numbers) - { - // 0:0 : 0:0 : x:x : x.x.x.x - if (numbers[0] == 0 && numbers[1] == 0 && numbers[2] == 0 && numbers[3] == 0 && numbers[6] != 0) + if (addressFamily == (ushort)AddressFamily.InterNetworkV6) { - // RFC 5952 Section 5 - 0:0 : 0:0 : 0:[0 | FFFF] : x.x.x.x - if (numbers[4] == 0 && (numbers[5] == 0 || numbers[5] == 0xFFFF)) + var v6address = new SocketAddress(AddressFamily.InterNetworkV6, IPv6AddressSize); + fixed (byte* pBuffer = v6address._buffer) { - return true; - - // SIIT - 0:0 : 0:0 : FFFF:0 : x.x.x.x - } - else if (numbers[4] == 0xFFFF && numbers[5] == 0) - { - return true; + for (var index = 2; index < IPv6AddressSize; index++) + { + pBuffer[index] = ((byte*)address)[index]; + } } - } - // ISATAP - if (numbers[4] == 0 && numbers[5] == 0x5EFE) - { - return true; + return v6address; } - return false; + return null; } -} // class SocketAddress +} diff --git a/src/Shared/HttpSys/NativeInterop/UnsafeNativeMethods.cs b/src/Shared/HttpSys/NativeInterop/UnsafeNativeMethods.cs deleted file mode 100644 index 7fed60b434b1..000000000000 --- a/src/Shared/HttpSys/NativeInterop/UnsafeNativeMethods.cs +++ /dev/null @@ -1,146 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; - -namespace Microsoft.AspNetCore.HttpSys.Internal; - -internal static unsafe partial class UnsafeNclNativeMethods -{ - private const string sspicli_LIB = "sspicli.dll"; - private const string api_ms_win_core_io_LIB = "api-ms-win-core-io-l1-1-0.dll"; - private const string api_ms_win_core_handle_LIB = "api-ms-win-core-handle-l1-1-0.dll"; - private const string api_ms_win_core_heap_LIB = "api-ms-win-core-heap-L1-2-0.dll"; - private const string api_ms_win_core_heap_obsolete_LIB = "api-ms-win-core-heap-obsolete-L1-1-0.dll"; - private const string api_ms_win_core_kernel32_legacy_LIB = "api-ms-win-core-kernel32-legacy-l1-1-0.dll"; - - private const string TOKENBINDING = "tokenbinding.dll"; - - // CONSIDER: Make this an enum, requires changing a lot of types from uint to ErrorCodes. - internal static class ErrorCodes - { - internal const uint ERROR_SUCCESS = 0; - internal const uint ERROR_FILE_NOT_FOUND = 2; - internal const uint ERROR_ACCESS_DENIED = 5; - internal const uint ERROR_SHARING_VIOLATION = 32; - internal const uint ERROR_HANDLE_EOF = 38; - internal const uint ERROR_NOT_SUPPORTED = 50; - internal const uint ERROR_INVALID_PARAMETER = 87; - internal const uint ERROR_INVALID_NAME = 123; - internal const uint ERROR_ALREADY_EXISTS = 183; - internal const uint ERROR_MORE_DATA = 234; - internal const uint ERROR_OPERATION_ABORTED = 995; - internal const uint ERROR_IO_PENDING = 997; - internal const uint ERROR_NOT_FOUND = 1168; - internal const uint ERROR_CONNECTION_INVALID = 1229; - } - - [LibraryImport(api_ms_win_core_io_LIB, SetLastError = true)] - internal static unsafe partial uint CancelIoEx(SafeHandle handle, SafeNativeOverlapped overlapped); - - [LibraryImport(api_ms_win_core_kernel32_legacy_LIB, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static unsafe partial bool SetFileCompletionNotificationModes(SafeHandle handle, FileCompletionNotificationModes modes); - - [Flags] - internal enum FileCompletionNotificationModes : byte - { - None = 0, - SkipCompletionPortOnSuccess = 1, - SkipSetEventOnHandle = 2 - } - - [LibraryImport(TOKENBINDING)] - public static partial int TokenBindingVerifyMessage( - byte* tokenBindingMessage, - uint tokenBindingMessageSize, - char* keyType, - byte* tlsUnique, - uint tlsUniqueSize, - out HeapAllocHandle resultList); - - // http://msdn.microsoft.com/en-us/library/windows/desktop/aa366569(v=vs.85).aspx - [LibraryImport(api_ms_win_core_heap_LIB, SetLastError = true)] - internal static partial IntPtr GetProcessHeap(); - - // http://msdn.microsoft.com/en-us/library/windows/desktop/aa366701(v=vs.85).aspx - [LibraryImport(api_ms_win_core_heap_LIB, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool HeapFree( - IntPtr hHeap, - uint dwFlags, - IntPtr lpMem); - - internal static partial class SafeNetHandles - { - [LibraryImport(sspicli_LIB, SetLastError = true)] - internal static partial int FreeContextBuffer( - IntPtr contextBuffer); - - [LibraryImport(api_ms_win_core_handle_LIB, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static partial bool CloseHandle(IntPtr handle); - - [LibraryImport(api_ms_win_core_heap_obsolete_LIB, EntryPoint = "LocalAlloc", SetLastError = true)] - internal static partial SafeLocalFreeChannelBinding LocalAllocChannelBinding(int uFlags, UIntPtr sizetdwBytes); - - [LibraryImport(api_ms_win_core_heap_obsolete_LIB, SetLastError = true)] - internal static partial IntPtr LocalFree(IntPtr handle); - } - - // from tokenbinding.h - internal static class TokenBinding - { - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct TOKENBINDING_RESULT_DATA - { - public uint identifierSize; - public TOKENBINDING_IDENTIFIER* identifierData; - public TOKENBINDING_EXTENSION_FORMAT extensionFormat; - public uint extensionSize; - public IntPtr extensionData; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct TOKENBINDING_IDENTIFIER - { - // Note: If the layout of these fields changes, be sure to make the - // corresponding change to TokenBindingUtil.ExtractIdentifierBlob. - - public TOKENBINDING_TYPE bindingType; - public TOKENBINDING_HASH_ALGORITHM hashAlgorithm; - public TOKENBINDING_SIGNATURE_ALGORITHM signatureAlgorithm; - } - - internal enum TOKENBINDING_TYPE : byte - { - TOKENBINDING_TYPE_PROVIDED = 0, - TOKENBINDING_TYPE_REFERRED = 1, - } - - internal enum TOKENBINDING_HASH_ALGORITHM : byte - { - TOKENBINDING_HASH_ALGORITHM_SHA256 = 4, - } - - internal enum TOKENBINDING_SIGNATURE_ALGORITHM : byte - { - TOKENBINDING_SIGNATURE_ALGORITHM_RSA = 1, - TOKENBINDING_SIGNATURE_ALGORITHM_ECDSAP256 = 3, - } - - internal enum TOKENBINDING_EXTENSION_FORMAT - { - TOKENBINDING_EXTENSION_FORMAT_UNDEFINED = 0, - } - - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct TOKENBINDING_RESULT_LIST - { - public uint resultCount; - public TOKENBINDING_RESULT_DATA* resultData; - } - } -} diff --git a/src/Shared/HttpSys/RequestProcessing/HeaderCollection.cs b/src/Shared/HttpSys/RequestProcessing/HeaderCollection.cs index 7df5134ec1d6..f3fe743aa133 100644 --- a/src/Shared/HttpSys/RequestProcessing/HeaderCollection.cs +++ b/src/Shared/HttpSys/RequestProcessing/HeaderCollection.cs @@ -1,9 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections; -using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; diff --git a/src/Shared/HttpSys/RequestProcessing/HeaderParser.cs b/src/Shared/HttpSys/RequestProcessing/HeaderParser.cs index e0cc815dc75d..f9d51e9ccd82 100644 --- a/src/Shared/HttpSys/RequestProcessing/HeaderParser.cs +++ b/src/Shared/HttpSys/RequestProcessing/HeaderParser.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; using Microsoft.Extensions.Primitives; namespace Microsoft.AspNetCore.HttpSys.Internal; diff --git a/src/Shared/HttpSys/RequestProcessing/NativeRequestContext.cs b/src/Shared/HttpSys/RequestProcessing/NativeRequestContext.cs index b58bad9e800e..882afdcf1e37 100644 --- a/src/Shared/HttpSys/RequestProcessing/NativeRequestContext.cs +++ b/src/Shared/HttpSys/RequestProcessing/NativeRequestContext.cs @@ -7,14 +7,16 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Net; -using System.Net.Sockets; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Security.Principal; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.HttpSys; using Microsoft.Extensions.Primitives; +using Windows.Win32; +using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.HttpSys.Internal; @@ -26,7 +28,7 @@ internal unsafe class NativeRequestContext : IDisposable private const int DefaultBufferSize = 4096 - AlignmentPadding; private IntPtr _originalBufferAddress; private readonly bool _useLatin1; - private HttpApiTypes.HTTP_REQUEST* _nativeRequest; + private HTTP_REQUEST_V1* _nativeRequest; private readonly IMemoryOwner? _backingBuffer; private MemoryHandle _memoryHandle; private readonly int _bufferAlignment; @@ -62,14 +64,14 @@ internal NativeRequestContext(MemoryPool memoryPool, uint? bufferSize, ulo } _backingBuffer.Memory.Span.Clear(); _memoryHandle = _backingBuffer.Memory.Pin(); - _nativeRequest = (HttpApiTypes.HTTP_REQUEST*)((long)_memoryHandle.Pointer + _bufferAlignment); + _nativeRequest = (HTTP_REQUEST_V1*)((long)_memoryHandle.Pointer + _bufferAlignment); RequestId = requestId; _useLatin1 = useLatin1; } // To be used by IIS Integration. - internal NativeRequestContext(HttpApiTypes.HTTP_REQUEST* request, bool useLatin1) + internal NativeRequestContext(HTTP_REQUEST_V1* request, bool useLatin1) { _useLatin1 = useLatin1; _nativeRequest = request; @@ -95,7 +97,7 @@ typedef struct _HTTP_REQUEST_TIMING_INFO } HTTP_REQUEST_TIMING_INFO, *PHTTP_REQUEST_TIMING_INFO; */ - if (!RequestInfo.TryGetValue((int)HttpApiTypes.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeRequestTiming, out var timingInfo)) + if (!RequestInfo.TryGetValue((int)HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeRequestTiming, out var timingInfo)) { return ReadOnlySpan.Empty; } @@ -105,12 +107,12 @@ typedef struct _HTTP_REQUEST_TIMING_INFO // Note that even though RequestTimingCount is an int, the compiler enforces alignment of data in the struct which causes 4 bytes // of padding to be added after RequestTimingCount, so we need to skip 64-bits before we get to the start of the RequestTiming array return MemoryMarshal.CreateReadOnlySpan( - ref Unsafe.As(ref MemoryMarshal.GetReference(timingInfo.Span.Slice(sizeof(long)))), + ref Unsafe.As(ref MemoryMarshal.GetReference(timingInfo.Span[sizeof(long)..])), timingCount); } } - internal HttpApiTypes.HTTP_REQUEST* NativeRequest + internal HTTP_REQUEST_V1* NativeRequest { get { @@ -119,12 +121,12 @@ internal HttpApiTypes.HTTP_REQUEST* NativeRequest } } - internal HttpApiTypes.HTTP_REQUEST_V2* NativeRequestV2 + internal HTTP_REQUEST_V2* NativeRequestV2 { get { Debug.Assert(_nativeRequest != null || _backingBuffer == null, "native request accessed after ReleasePins()."); - return (HttpApiTypes.HTTP_REQUEST_V2*)_nativeRequest; + return (HTTP_REQUEST_V2*)_nativeRequest; } } @@ -138,7 +140,7 @@ internal ulong RequestId internal ulong RawConnectionId => NativeRequest->RawConnectionId; - internal HttpApiTypes.HTTP_VERB VerbId => NativeRequest->Verb; + internal HTTP_VERB VerbId => NativeRequest->Verb; internal ulong UrlContext => NativeRequest->UrlContext; @@ -154,9 +156,9 @@ internal SslStatus SslStatus } } - internal bool IsHttp2 => NativeRequest->Flags.HasFlag(HttpApiTypes.HTTP_REQUEST_FLAGS.Http2); + internal bool IsHttp2 => (NativeRequest->Flags & PInvoke.HTTP_REQUEST_FLAG_HTTP2) != 0; - internal bool IsHttp3 => NativeRequest->Flags.HasFlag(HttpApiTypes.HTTP_REQUEST_FLAGS.Http3); + internal bool IsHttp3 => (NativeRequest->Flags & PInvoke.HTTP_REQUEST_FLAG_HTTP3) != 0; // Assumes memory isn't pinned. Will fail if called by IIS. internal uint Size @@ -195,7 +197,7 @@ public bool TryGetTimestamp(HttpSysRequestTimingType timestampType, out long tim public bool TryGetElapsedTime(HttpSysRequestTimingType startingTimestampType, HttpSysRequestTimingType endingTimestampType, out TimeSpan elapsed) { - if (TryGetTimestamp(startingTimestampType, out long startTimestamp) && TryGetTimestamp(endingTimestampType, out long endTimestamp)) + if (TryGetTimestamp(startingTimestampType, out var startTimestamp) && TryGetTimestamp(endingTimestampType, out var endTimestamp)) { elapsed = Stopwatch.GetElapsedTime(startTimestamp, endTimestamp); return true; @@ -221,11 +223,12 @@ public virtual void Dispose() internal string? GetVerb() { var verb = NativeRequest->Verb; - if (verb > HttpApiTypes.HTTP_VERB.HttpVerbUnknown && verb < HttpApiTypes.HTTP_VERB.HttpVerbMaximum) + Debug.Assert((int)HTTP_VERB.HttpVerbMaximum == HttpVerbs.Length); + if (verb > HTTP_VERB.HttpVerbUnknown && verb < HTTP_VERB.HttpVerbMaximum) { - return HttpApiTypes.HttpVerbs[(int)verb]; + return HttpVerbs[(int)verb]; } - else if (verb == HttpApiTypes.HTTP_VERB.HttpVerbUnknown && NativeRequest->pUnknownVerb != null) + else if (verb == HTTP_VERB.HttpVerbUnknown && !NativeRequest->pUnknownVerb.Equals(null)) { // Never use Latin1 for the VERB return HeaderEncoding.GetString(NativeRequest->pUnknownVerb, NativeRequest->UnknownVerbLength, useLatin1: false); @@ -234,18 +237,43 @@ public virtual void Dispose() return null; } + // Maps HTTP_VERB to strings + internal static readonly string?[] HttpVerbs = + [ + null, + "Unknown", + "Invalid", + HttpMethods.Options, + HttpMethods.Get, + HttpMethods.Head, + HttpMethods.Post, + HttpMethods.Put, + HttpMethods.Delete, + HttpMethods.Trace, + HttpMethods.Connect, + "TRACK", + "MOVE", + "COPY", + "PROPFIND", + "PROPPATCH", + "MKCOL", + "LOCK", + "UNLOCK", + "SEARCH", + ]; + internal string? GetRawUrl() { - if (NativeRequest->pRawUrl != null && NativeRequest->RawUrlLength > 0) + if (!NativeRequest->pRawUrl.Equals(null) && NativeRequest->RawUrlLength > 0) { - return Marshal.PtrToStringAnsi((IntPtr)NativeRequest->pRawUrl, NativeRequest->RawUrlLength); + return Marshal.PtrToStringAnsi((IntPtr)NativeRequest->pRawUrl.Value, NativeRequest->RawUrlLength); } return null; } internal Span GetRawUrlInBytes() { - if (NativeRequest->pRawUrl != null && NativeRequest->RawUrlLength > 0) + if (!NativeRequest->pRawUrl.Equals(null) && NativeRequest->RawUrlLength > 0) { return new Span(NativeRequest->pRawUrl, NativeRequest->RawUrlLength); } @@ -286,14 +314,14 @@ internal bool CheckAuthenticated() var requestInfo = NativeRequestV2->pRequestInfo; var infoCount = NativeRequestV2->RequestInfoCount; - for (int i = 0; i < infoCount; i++) + for (var i = 0; i < infoCount; i++) { var info = &requestInfo[i]; if (info != null - && info->InfoType == HttpApiTypes.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth) + && info->InfoType == HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth) { - var authInfo = (HttpApiTypes.HTTP_REQUEST_AUTH_INFO*)info->pInfo; - if (authInfo->AuthStatus == HttpApiTypes.HTTP_AUTH_STATUS.HttpAuthStatusSuccess) + var authInfo = (HTTP_REQUEST_AUTH_INFO*)info->pInfo; + if (authInfo->AuthStatus == HTTP_AUTH_STATUS.HttpAuthStatusSuccess) { return true; } @@ -307,20 +335,20 @@ internal WindowsPrincipal GetUser() var requestInfo = NativeRequestV2->pRequestInfo; var infoCount = NativeRequestV2->RequestInfoCount; - for (int i = 0; i < infoCount; i++) + for (var i = 0; i < infoCount; i++) { var info = &requestInfo[i]; if (info != null - && info->InfoType == HttpApiTypes.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth) + && info->InfoType == HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth) { - var authInfo = (HttpApiTypes.HTTP_REQUEST_AUTH_INFO*)info->pInfo; - if (authInfo->AuthStatus == HttpApiTypes.HTTP_AUTH_STATUS.HttpAuthStatusSuccess) + var authInfo = (HTTP_REQUEST_AUTH_INFO*)info->pInfo; + if (authInfo->AuthStatus == HTTP_AUTH_STATUS.HttpAuthStatusSuccess) { // Duplicates AccessToken var identity = new WindowsIdentity(authInfo->AccessToken, GetAuthTypeFromRequest(authInfo->AuthType)); // Close the original - UnsafeNclNativeMethods.SafeNetHandles.CloseHandle(authInfo->AccessToken); + PInvoke.CloseHandle(authInfo->AccessToken); return new WindowsPrincipal(identity); } @@ -330,18 +358,18 @@ internal WindowsPrincipal GetUser() return new WindowsPrincipal(WindowsIdentity.GetAnonymous()); // Anonymous / !IsAuthenticated } - internal HttpApiTypes.HTTP_SSL_PROTOCOL_INFO GetTlsHandshake() + internal HTTP_SSL_PROTOCOL_INFO GetTlsHandshake() { var requestInfo = NativeRequestV2->pRequestInfo; var infoCount = NativeRequestV2->RequestInfoCount; - for (int i = 0; i < infoCount; i++) + for (var i = 0; i < infoCount; i++) { var info = &requestInfo[i]; if (info != null - && info->InfoType == HttpApiTypes.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeSslProtocol) + && info->InfoType == HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeSslProtocol) { - var authInfo = *((HttpApiTypes.HTTP_SSL_PROTOCOL_INFO*)info->pInfo); + var authInfo = *(HTTP_SSL_PROTOCOL_INFO*)info->pInfo; SetSslProtocol(&authInfo); return authInfo; } @@ -350,7 +378,7 @@ internal HttpApiTypes.HTTP_SSL_PROTOCOL_INFO GetTlsHandshake() return default; } - private static void SetSslProtocol(HttpApiTypes.HTTP_SSL_PROTOCOL_INFO* protocolInfo) + private static void SetSslProtocol(HTTP_SSL_PROTOCOL_INFO* protocolInfo) { var protocol = protocolInfo->Protocol; // The OS considers client and server TLS as different enum values. SslProtocols choose to combine those for some reason. @@ -358,54 +386,48 @@ private static void SetSslProtocol(HttpApiTypes.HTTP_SSL_PROTOCOL_INFO* protocol // https://learn.microsoft.com/windows/desktop/api/schannel/ns-schannel-_secpkgcontext_connectioninfo // Compare to https://referencesource.microsoft.com/#System/net/System/Net/SecureProtocols/_SslState.cs,8905d1bf17729de3 #pragma warning disable CS0618 // Type or member is obsolete - if ((protocol & SslProtocols.Ssl2) != 0) + if ((protocol & (uint)SslProtocols.Ssl2) != 0) { - protocol |= SslProtocols.Ssl2; + protocol |= (uint)SslProtocols.Ssl2; } - if ((protocol & SslProtocols.Ssl3) != 0) + if ((protocol & (uint)SslProtocols.Ssl3) != 0) { - protocol |= SslProtocols.Ssl3; + protocol |= (uint)SslProtocols.Ssl3; } #pragma warning restore CS0618 // Type or Prmember is obsolete #pragma warning disable SYSLIB0039 // TLS 1.0 and 1.1 are obsolete - if ((protocol & SslProtocols.Tls) != 0) + if ((protocol & (uint)SslProtocols.Tls) != 0) { - protocol |= SslProtocols.Tls; + protocol |= (uint)SslProtocols.Tls; } - if ((protocol & SslProtocols.Tls11) != 0) + if ((protocol & (uint)SslProtocols.Tls11) != 0) { - protocol |= SslProtocols.Tls11; + protocol |= (uint)SslProtocols.Tls11; } #pragma warning restore SYSLIB0039 - if ((protocol & SslProtocols.Tls12) != 0) + if ((protocol & (uint)SslProtocols.Tls12) != 0) { - protocol |= SslProtocols.Tls12; + protocol |= (uint)SslProtocols.Tls12; } - if ((protocol & SslProtocols.Tls13) != 0) + if ((protocol & (uint)SslProtocols.Tls13) != 0) { - protocol |= SslProtocols.Tls13; + protocol |= (uint)SslProtocols.Tls13; } protocolInfo->Protocol = protocol; } - private static string GetAuthTypeFromRequest(HttpApiTypes.HTTP_REQUEST_AUTH_TYPE input) + private static string GetAuthTypeFromRequest(HTTP_REQUEST_AUTH_TYPE input) { - switch (input) + return input switch { - case HttpApiTypes.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeBasic: - return "Basic"; - case HttpApiTypes.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeNTLM: - return "NTLM"; - // case HttpApi.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeDigest: - // return "Digest"; - case HttpApiTypes.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeNegotiate: - return "Negotiate"; - case HttpApiTypes.HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeKerberos: - return "Kerberos"; - default: - throw new NotImplementedException(input.ToString()); - } + HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeBasic => "Basic", + HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeNTLM => "NTLM", + // case HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeDigest => "Digest"; + HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeNegotiate => "Negotiate", + HTTP_REQUEST_AUTH_TYPE.HttpRequestAuthTypeKerberos => "Kerberos", + _ => throw new NotImplementedException(input.ToString()), + }; } internal bool HasKnownHeader(HttpSysRequestHeader header) @@ -418,21 +440,21 @@ internal bool HasKnownHeader(HttpSysRequestHeader header) { fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HttpApiTypes.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment); - long fixup = pMemoryBlob - (byte*)_originalBufferAddress; + var request = (HTTP_REQUEST_V1*)(pMemoryBlob + _bufferAlignment); + var fixup = pMemoryBlob - (byte*)_originalBufferAddress; return HasKnowHeaderHelper(header, fixup, request); } } } - private bool HasKnowHeaderHelper(HttpSysRequestHeader header, long fixup, HttpApiTypes.HTTP_REQUEST* request) + private bool HasKnowHeaderHelper(HttpSysRequestHeader header, long fixup, HTTP_REQUEST_V1* request) { - int headerIndex = (int)header; + var headerIndex = (int)header; - HttpApiTypes.HTTP_KNOWN_HEADER* pKnownHeader = (&request->Headers.KnownHeaders) + headerIndex; + var pKnownHeader = request->Headers.KnownHeaders.AsSpan()[headerIndex]; // For known headers, when header value is empty, RawValueLength will be 0 and // pRawValue will point to empty string ("\0") - if (pKnownHeader->RawValueLength > 0) + if (pKnownHeader.RawValueLength > 0) { return true; } @@ -453,24 +475,24 @@ private bool HasKnowHeaderHelper(HttpSysRequestHeader header, long fixup, HttpAp { fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HttpApiTypes.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment); - long fixup = pMemoryBlob - (byte*)_originalBufferAddress; + var request = (HTTP_REQUEST_V1*)(pMemoryBlob + _bufferAlignment); + var fixup = pMemoryBlob - (byte*)_originalBufferAddress; return GetKnowHeaderHelper(header, fixup, request); } } } - private string? GetKnowHeaderHelper(HttpSysRequestHeader header, long fixup, HttpApiTypes.HTTP_REQUEST* request) + private string? GetKnowHeaderHelper(HttpSysRequestHeader header, long fixup, HTTP_REQUEST_V1* request) { - int headerIndex = (int)header; + var headerIndex = (int)header; string? value = null; - HttpApiTypes.HTTP_KNOWN_HEADER* pKnownHeader = (&request->Headers.KnownHeaders) + headerIndex; + var pKnownHeader = request->Headers.KnownHeaders.AsSpan()[headerIndex]; // For known headers, when header value is empty, RawValueLength will be 0 and // pRawValue will point to empty string ("\0") - if (pKnownHeader->RawValueLength > 0) + if (pKnownHeader.RawValueLength > 0) { - value = HeaderEncoding.GetString(pKnownHeader->pRawValue + fixup, pKnownHeader->RawValueLength, _useLatin1); + value = HeaderEncoding.GetString((byte*)pKnownHeader.pRawValue + fixup, pKnownHeader.RawValueLength, _useLatin1); } return value; @@ -486,25 +508,25 @@ internal void GetUnknownKeys(Span destination) { fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HttpApiTypes.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment); - long fixup = pMemoryBlob - (byte*)_originalBufferAddress; + var request = (HTTP_REQUEST_V1*)(pMemoryBlob + _bufferAlignment); + var fixup = pMemoryBlob - (byte*)_originalBufferAddress; PopulateUnknownKeys(request, fixup, destination); } } } - private void PopulateUnknownKeys(HttpApiTypes.HTTP_REQUEST* request, long fixup, Span destination) + private void PopulateUnknownKeys(HTTP_REQUEST_V1* request, long fixup, Span destination) { if (request->Headers.UnknownHeaderCount == 0) { return; } - var pUnknownHeader = (HttpApiTypes.HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders); - for (int index = 0; index < request->Headers.UnknownHeaderCount; index++) + var pUnknownHeader = (HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders); + for (var index = 0; index < request->Headers.UnknownHeaderCount; index++) { - if (pUnknownHeader->pName != null && pUnknownHeader->NameLength > 0) + if (!pUnknownHeader->pName.Equals(null) && pUnknownHeader->NameLength > 0) { - var headerName = HeaderEncoding.GetString(pUnknownHeader->pName + fixup, pUnknownHeader->NameLength, _useLatin1); + var headerName = HeaderEncoding.GetString((byte*)pUnknownHeader->pName + fixup, pUnknownHeader->NameLength, _useLatin1); destination[index] = headerName; } pUnknownHeader++; @@ -521,26 +543,26 @@ internal int CountUnknownHeaders() { fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HttpApiTypes.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment); - long fixup = pMemoryBlob - (byte*)_originalBufferAddress; + var request = (HTTP_REQUEST_V1*)(pMemoryBlob + _bufferAlignment); + var fixup = pMemoryBlob - (byte*)_originalBufferAddress; return CountUnknownHeaders(request, fixup); } } } - private int CountUnknownHeaders(HttpApiTypes.HTTP_REQUEST* request, long fixup) + private int CountUnknownHeaders(HTTP_REQUEST_V1* request, long fixup) { if (request->Headers.UnknownHeaderCount == 0) { return 0; } - int count = 0; - var pUnknownHeader = (HttpApiTypes.HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders); - for (int index = 0; index < request->Headers.UnknownHeaderCount; index++) + var count = 0; + var pUnknownHeader = (HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders); + for (var index = 0; index < request->Headers.UnknownHeaderCount; index++) { // For unknown headers, when header value is empty, RawValueLength will be 0 and // pRawValue will be null. - if (pUnknownHeader->pName != null && pUnknownHeader->NameLength > 0) + if (!pUnknownHeader->pName.Equals(null) && pUnknownHeader->NameLength > 0) { count++; } @@ -560,32 +582,32 @@ internal void GetUnknownHeaders(IDictionary unknownHeaders // Return value. fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HttpApiTypes.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment); - long fixup = pMemoryBlob - (byte*)_originalBufferAddress; + var request = (HTTP_REQUEST_V1*)(pMemoryBlob + _bufferAlignment); + var fixup = pMemoryBlob - (byte*)_originalBufferAddress; GetUnknownHeadersHelper(unknownHeaders, fixup, request); } } } - private void GetUnknownHeadersHelper(IDictionary unknownHeaders, long fixup, HttpApiTypes.HTTP_REQUEST* request) + private void GetUnknownHeadersHelper(IDictionary unknownHeaders, long fixup, HTTP_REQUEST_V1* request) { int index; // unknown headers if (request->Headers.UnknownHeaderCount != 0) { - var pUnknownHeader = (HttpApiTypes.HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders); + var pUnknownHeader = (HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders); for (index = 0; index < request->Headers.UnknownHeaderCount; index++) { // For unknown headers, when header value is empty, RawValueLength will be 0 and // pRawValue will be null. - if (pUnknownHeader->pName != null && pUnknownHeader->NameLength > 0) + if (!pUnknownHeader->pName.Equals(null) && pUnknownHeader->NameLength > 0) { - var headerName = HeaderEncoding.GetString(pUnknownHeader->pName + fixup, pUnknownHeader->NameLength, _useLatin1); + var headerName = HeaderEncoding.GetString((byte*)pUnknownHeader->pName + fixup, pUnknownHeader->NameLength, _useLatin1); string headerValue; - if (pUnknownHeader->pRawValue != null && pUnknownHeader->RawValueLength > 0) + if (!pUnknownHeader->pRawValue.Equals(null) && pUnknownHeader->RawValueLength > 0) { - headerValue = HeaderEncoding.GetString(pUnknownHeader->pRawValue + fixup, pUnknownHeader->RawValueLength, _useLatin1); + headerValue = HeaderEncoding.GetString((byte*)pUnknownHeader->pRawValue + fixup, pUnknownHeader->RawValueLength, _useLatin1); } else { @@ -620,13 +642,13 @@ private void GetUnknownHeadersHelper(IDictionary unknownHe { fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HttpApiTypes.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment); + var request = (HTTP_REQUEST_V1*)(pMemoryBlob + _bufferAlignment); return GetEndPointHelper(localEndpoint, request, pMemoryBlob); } } } - private SocketAddress? GetEndPointHelper(bool localEndpoint, HttpApiTypes.HTTP_REQUEST* request, byte* pMemoryBlob) + private SocketAddress? GetEndPointHelper(bool localEndpoint, HTTP_REQUEST_V1* request, byte* pMemoryBlob) { var source = localEndpoint ? (byte*)request->Address.pLocalAddress : (byte*)request->Address.pRemoteAddress; @@ -635,38 +657,7 @@ private void GetUnknownHeadersHelper(IDictionary unknownHe return null; } var address = (IntPtr)(pMemoryBlob + _bufferAlignment - (byte*)_originalBufferAddress + source); - return CopyOutAddress(address); - } - - private static SocketAddress? CopyOutAddress(IntPtr address) - { - ushort addressFamily = *((ushort*)address); - if (addressFamily == (ushort)AddressFamily.InterNetwork) - { - var v4address = new SocketAddress(AddressFamily.InterNetwork, SocketAddress.IPv4AddressSize); - fixed (byte* pBuffer = v4address.Buffer) - { - for (int index = 2; index < SocketAddress.IPv4AddressSize; index++) - { - pBuffer[index] = ((byte*)address)[index]; - } - } - return v4address; - } - if (addressFamily == (ushort)AddressFamily.InterNetworkV6) - { - var v6address = new SocketAddress(AddressFamily.InterNetworkV6, SocketAddress.IPv6AddressSize); - fixed (byte* pBuffer = v6address.Buffer) - { - for (int index = 2; index < SocketAddress.IPv6AddressSize; index++) - { - pBuffer[index] = ((byte*)address)[index]; - } - } - return v6address; - } - - return null; + return SocketAddress.CopyOutAddress(address); } internal uint GetChunks(ref int dataChunkIndex, ref uint dataChunkOffset, byte[] buffer, int offset, int size) @@ -680,28 +671,28 @@ internal uint GetChunks(ref int dataChunkIndex, ref uint dataChunkOffset, byte[] { fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HttpApiTypes.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment); - long fixup = pMemoryBlob - (byte*)_originalBufferAddress; + var request = (HTTP_REQUEST_V1*)(pMemoryBlob + _bufferAlignment); + var fixup = pMemoryBlob - (byte*)_originalBufferAddress; return GetChunksHelper(ref dataChunkIndex, ref dataChunkOffset, buffer, offset, size, fixup, request); } } } - private uint GetChunksHelper(ref int dataChunkIndex, ref uint dataChunkOffset, byte[] buffer, int offset, int size, long fixup, HttpApiTypes.HTTP_REQUEST* request) + private uint GetChunksHelper(ref int dataChunkIndex, ref uint dataChunkOffset, byte[] buffer, int offset, int size, long fixup, HTTP_REQUEST_V1* request) { uint dataRead = 0; if (request->EntityChunkCount > 0 && dataChunkIndex < request->EntityChunkCount && dataChunkIndex != -1) { - var pDataChunk = (HttpApiTypes.HTTP_DATA_CHUNK*)(fixup + (byte*)&request->pEntityChunks[dataChunkIndex]); + var pDataChunk = (HTTP_DATA_CHUNK*)(fixup + (byte*)&request->pEntityChunks[dataChunkIndex]); fixed (byte* pReadBuffer = buffer) { - byte* pTo = &pReadBuffer[offset]; + var pTo = &pReadBuffer[offset]; while (dataChunkIndex < request->EntityChunkCount && dataRead < size) { - if (dataChunkOffset >= pDataChunk->fromMemory.BufferLength) + if (dataChunkOffset >= pDataChunk->Anonymous.FromMemory.BufferLength) { dataChunkOffset = 0; dataChunkIndex++; @@ -709,9 +700,9 @@ private uint GetChunksHelper(ref int dataChunkIndex, ref uint dataChunkOffset, b } else { - byte* pFrom = (byte*)pDataChunk->fromMemory.pBuffer + dataChunkOffset + fixup; + var pFrom = (byte*)pDataChunk->Anonymous.FromMemory.pBuffer + dataChunkOffset + fixup; - uint bytesToRead = pDataChunk->fromMemory.BufferLength - (uint)dataChunkOffset; + var bytesToRead = pDataChunk->Anonymous.FromMemory.BufferLength - (uint)dataChunkOffset; if (bytesToRead > (uint)size) { bytesToRead = (uint)size; @@ -738,19 +729,19 @@ internal IReadOnlyDictionary> GetRequestInfo() { if (PermanentlyPinned) { - return GetRequestInfo((IntPtr)_nativeRequest, (HttpApiTypes.HTTP_REQUEST_V2*)_nativeRequest); + return GetRequestInfo((IntPtr)_nativeRequest, (HTTP_REQUEST_V2*)_nativeRequest); } else { fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HttpApiTypes.HTTP_REQUEST_V2*)(pMemoryBlob + _bufferAlignment); + var request = (HTTP_REQUEST_V2*)(pMemoryBlob + _bufferAlignment); return GetRequestInfo(_originalBufferAddress, request); } } } - private IReadOnlyDictionary> GetRequestInfo(IntPtr baseAddress, HttpApiTypes.HTTP_REQUEST_V2* nativeRequest) + private IReadOnlyDictionary> GetRequestInfo(IntPtr baseAddress, HTTP_REQUEST_V2* nativeRequest) { var count = nativeRequest->RequestInfoCount; if (count == 0) @@ -760,8 +751,8 @@ private IReadOnlyDictionary> GetRequestInfo(IntPtr bas var info = new Dictionary>(count); - long fixup = (byte*)nativeRequest - (byte*)baseAddress; - var pRequestInfo = (HttpApiTypes.HTTP_REQUEST_INFO*)((byte*)nativeRequest->pRequestInfo + fixup); + var fixup = (byte*)nativeRequest - (byte*)baseAddress; + var pRequestInfo = (HTTP_REQUEST_INFO*)((byte*)nativeRequest->pRequestInfo + fixup); for (var i = 0; i < count; i++) { @@ -781,42 +772,42 @@ private IReadOnlyDictionary> GetRequestInfo(IntPtr bas { if (PermanentlyPinned) { - return GetClientCertificate((IntPtr)_nativeRequest, (HttpApiTypes.HTTP_REQUEST_V2*)_nativeRequest); + return GetClientCertificate((IntPtr)_nativeRequest, (HTTP_REQUEST_V2*)_nativeRequest); } else { fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HttpApiTypes.HTTP_REQUEST_V2*)(pMemoryBlob + _bufferAlignment); + var request = (HTTP_REQUEST_V2*)(pMemoryBlob + _bufferAlignment); return GetClientCertificate(_originalBufferAddress, request); } } } // Throws CryptographicException - private X509Certificate2? GetClientCertificate(IntPtr baseAddress, HttpApiTypes.HTTP_REQUEST_V2* nativeRequest) + private X509Certificate2? GetClientCertificate(IntPtr baseAddress, HTTP_REQUEST_V2* nativeRequest) { - var request = nativeRequest->Request; - long fixup = (byte*)nativeRequest - (byte*)baseAddress; + var request = nativeRequest->Base; + var fixup = (byte*)nativeRequest - (byte*)baseAddress; if (request.pSslInfo == null) { return null; } - var sslInfo = (HttpApiTypes.HTTP_SSL_INFO*)((byte*)request.pSslInfo + fixup); + var sslInfo = (HTTP_SSL_INFO*)((byte*)request.pSslInfo + fixup); if (sslInfo->SslClientCertNegotiated == 0 || sslInfo->pClientCertInfo == null) { return null; } - var clientCertInfo = (HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO*)((byte*)sslInfo->pClientCertInfo + fixup); + var clientCertInfo = (HTTP_SSL_CLIENT_CERT_INFO*)((byte*)sslInfo->pClientCertInfo + fixup); if (clientCertInfo->pCertEncoded == null) { return null; } var clientCert = clientCertInfo->pCertEncoded + fixup; - byte[] certEncoded = new byte[clientCertInfo->CertEncodedSize]; + var certEncoded = new byte[clientCertInfo->CertEncodedSize]; Marshal.Copy((IntPtr)clientCert, certEncoded, 0, certEncoded.Length); return new X509Certificate2(certEncoded); } diff --git a/src/Shared/HttpSys/RequestProcessing/PathNormalizer.cs b/src/Shared/HttpSys/RequestProcessing/PathNormalizer.cs index b1bc8c7e22f2..d29d26624578 100644 --- a/src/Shared/HttpSys/RequestProcessing/PathNormalizer.cs +++ b/src/Shared/HttpSys/RequestProcessing/PathNormalizer.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Diagnostics; namespace Microsoft.AspNetCore.HttpSys.Internal; diff --git a/src/Shared/HttpSys/RequestProcessing/RawUrlHelper.cs b/src/Shared/HttpSys/RequestProcessing/RawUrlHelper.cs index e59bf434b435..e9b1551d4c80 100644 --- a/src/Shared/HttpSys/RequestProcessing/RawUrlHelper.cs +++ b/src/Shared/HttpSys/RequestProcessing/RawUrlHelper.cs @@ -1,9 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Text; - namespace Microsoft.AspNetCore.HttpSys.Internal; internal static class RawUrlHelper diff --git a/src/Shared/HttpSys/RequestProcessing/RequestUriBuilder.cs b/src/Shared/HttpSys/RequestProcessing/RequestUriBuilder.cs index 84d1235f4462..27ed6bcdf1b4 100644 --- a/src/Shared/HttpSys/RequestProcessing/RequestUriBuilder.cs +++ b/src/Shared/HttpSys/RequestProcessing/RequestUriBuilder.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Diagnostics; using System.Text; diff --git a/src/Shared/ServerInfrastructure/StringUtilities.cs b/src/Shared/ServerInfrastructure/StringUtilities.cs index 904cda0fc7f6..c018582212e0 100644 --- a/src/Shared/ServerInfrastructure/StringUtilities.cs +++ b/src/Shared/ServerInfrastructure/StringUtilities.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Buffers; using System.Diagnostics; using System.Numerics; diff --git a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj index 8f7746f07455..d6e5da42387b 100644 --- a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj +++ b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj @@ -4,7 +4,7 @@ $(DefaultNetCoreTargetFramework) portable true - $(NoWarn);CS0649;CS0436 + $(NoWarn);CS0649;CS0436;CS9195 annotations @@ -49,6 +49,10 @@ + + all + runtime; build; native; contentfiles; analyzers + diff --git a/src/Shared/test/Shared.Tests/NativeMethods.txt b/src/Shared/test/Shared.Tests/NativeMethods.txt new file mode 100644 index 000000000000..268b666b0111 --- /dev/null +++ b/src/Shared/test/Shared.Tests/NativeMethods.txt @@ -0,0 +1,2 @@ +// https://github.com/microsoft/cswin32 +Windows.Win32.Networking.HttpServer