diff --git a/src/Servers/HttpSys/perf/Microbenchmarks/RequestHeaderBenchmarks.cs b/src/Servers/HttpSys/perf/Microbenchmarks/RequestHeaderBenchmarks.cs index 848354b30bc3..50b62ec61f2d 100644 --- a/src/Servers/HttpSys/perf/Microbenchmarks/RequestHeaderBenchmarks.cs +++ b/src/Servers/HttpSys/perf/Microbenchmarks/RequestHeaderBenchmarks.cs @@ -6,8 +6,6 @@ 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] @@ -56,7 +54,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 HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); var remainingMemory = SetUnknownHeaders(nativeMemory, ref requestStructure, GenerateUnknownHeaders(unknowHeaderCount)); SetHostHeader(remainingMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -66,15 +64,15 @@ private unsafe RequestHeaders CreateRequestHeader(int unknowHeaderCount) return requestHeaders; } - private unsafe Span SetHostHeader(Span nativeMemory, ref HTTP_REQUEST_V1 requestStructure) + private unsafe Span SetHostHeader(Span nativeMemory, ref HttpApiTypes.HTTP_REQUEST requestStructure) { // Writing localhost to Host header - var dataDestination = nativeMemory[Marshal.SizeOf()..]; - var length = Encoding.ASCII.GetBytes("localhost:5001", dataDestination); + var dataDestination = nativeMemory.Slice(Marshal.SizeOf()); + int length = Encoding.ASCII.GetBytes("localhost:5001", dataDestination); fixed (byte* address = &MemoryMarshal.GetReference(dataDestination)) { - requestStructure.Headers.KnownHeaders._28.pRawValue = (PCSTR)address; - requestStructure.Headers.KnownHeaders._28.RawValueLength = (ushort)length; + requestStructure.Headers.KnownHeaders_29.pRawValue = address; + requestStructure.Headers.KnownHeaders_29.RawValueLength = (ushort)length; } return dataDestination; } @@ -82,48 +80,48 @@ private unsafe Span SetHostHeader(Span nativeMemory, ref HTTP_REQUES /// /// 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 HTTP_REQUEST_V1 requestStructure, IReadOnlyCollection<(string Key, string Value)> headerNames) + private unsafe Span SetUnknownHeaders(Span nativeMemory, ref HttpApiTypes.HTTP_REQUEST requestStructure, IReadOnlyCollection<(string Key, string Value)> headerNames) { - var unknownHeaderStructureDestination = nativeMemory[Marshal.SizeOf()..]; + var unknownHeaderStructureDestination = nativeMemory.Slice(Marshal.SizeOf()); fixed (byte* address = &MemoryMarshal.GetReference(unknownHeaderStructureDestination)) { - requestStructure.Headers.pUnknownHeaders = (HTTP_UNKNOWN_HEADER*)address; + requestStructure.Headers.pUnknownHeaders = (HttpApiTypes.HTTP_UNKNOWN_HEADER*)address; } requestStructure.Headers.UnknownHeaderCount += (ushort)headerNames.Count; - var unknownHeadersSize = Marshal.SizeOf(); - var dataDestination = unknownHeaderStructureDestination[(unknownHeadersSize * headerNames.Count)..]; - foreach (var (headerKey, headerValue) in headerNames) + var unknownHeadersSize = Marshal.SizeOf(); + var dataDestination = unknownHeaderStructureDestination.Slice(unknownHeadersSize * headerNames.Count); + foreach (var headerName in headerNames) { - var unknownHeaderStructure = new HTTP_UNKNOWN_HEADER(); - var nameLength = Encoding.ASCII.GetBytes(headerKey, dataDestination); + var unknownHeaderStructure = new HttpApiTypes.HTTP_UNKNOWN_HEADER(); + int nameLength = Encoding.ASCII.GetBytes(headerName.Key, dataDestination); fixed (byte* address = &MemoryMarshal.GetReference(dataDestination)) { - unknownHeaderStructure.pName = (PCSTR)address; + unknownHeaderStructure.pName = address; unknownHeaderStructure.NameLength = (ushort)nameLength; } - dataDestination = dataDestination[nameLength..]; + dataDestination = dataDestination.Slice(nameLength); - if (!string.IsNullOrEmpty(headerValue)) + if (!string.IsNullOrEmpty(headerName.Value)) { - var valueLength = Encoding.ASCII.GetBytes(headerValue, dataDestination); + int valueLength = Encoding.ASCII.GetBytes(headerName.Value, dataDestination); fixed (byte* address = &MemoryMarshal.GetReference(dataDestination)) { - unknownHeaderStructure.pRawValue = (PCSTR)address; + unknownHeaderStructure.pRawValue = address; unknownHeaderStructure.RawValueLength = (ushort)valueLength; } - dataDestination = dataDestination[nameLength..]; + dataDestination = dataDestination.Slice(nameLength); } MemoryMarshal.Write(unknownHeaderStructureDestination, in unknownHeaderStructure); - unknownHeaderStructureDestination = unknownHeaderStructureDestination[unknownHeadersSize..]; + unknownHeaderStructureDestination = unknownHeaderStructureDestination.Slice(unknownHeadersSize); } return dataDestination; } - private static List<(string, string)> GenerateUnknownHeaders(int count) + private IReadOnlyCollection<(string, string)> GenerateUnknownHeaders(int count) { var result = new List<(string, string)>(); - for (var i = 0; i < count; i++) + for (int 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 82f4bf1af6c1..16155fd98b4d 100644 --- a/src/Servers/HttpSys/samples/TestClient/Program.cs +++ b/src/Servers/HttpSys/samples/TestClient/Program.cs @@ -1,89 +1,94 @@ // 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; - -public class Program +namespace TestClient { - private const string Address = - "http://localhost:5000/public/1kb.txt"; - // "https://localhost:9090/public/1kb.txt"; - - public static void Main(string[] args) + public class Program { - Console.WriteLine("Ready"); - Console.ReadKey(); + private const string Address = + "http://localhost:5000/public/1kb.txt"; + // "https://localhost:9090/public/1kb.txt"; - var handler = new HttpClientHandler(); - handler.MaxConnectionsPerServer = 500; - handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; - // handler.UseDefaultCredentials = true; - HttpClient client = new HttpClient(handler); + public static void Main(string[] args) + { + Console.WriteLine("Ready"); + Console.ReadKey(); - RunParallelRequests(client); + var handler = new HttpClientHandler(); + handler.MaxConnectionsPerServer = 500; + handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; + // handler.UseDefaultCredentials = true; + HttpClient client = new HttpClient(handler); - // RunManualRequests(client); + RunParallelRequests(client); - // RunWebSocketClient().Wait(); + // RunManualRequests(client); - Console.WriteLine("Done"); - // Console.ReadKey(); - } + // RunWebSocketClient().Wait(); - 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); + Console.WriteLine("Done"); + // Console.ReadKey(); } - } - private static void RunParallelRequests(HttpClient client) - { - int completionCount = 0; - int iterations = 100000; - for (int i = 0; i < iterations; i++) + private static void RunManualRequests(HttpClient client) { - client.GetAsync(Address) - .ContinueWith(t => Interlocked.Increment(ref completionCount)); + while (true) + { + Console.WriteLine("Press any key to send request"); + Console.ReadKey(); + var result = client.GetAsync(Address).Result; + Console.WriteLine(result); + } } - while (completionCount < iterations) + private static void RunParallelRequests(HttpClient client) { - Thread.Sleep(10); + 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); + } } - } - - 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); - 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 + public static async Task RunWebSocketClient() { - Console.WriteLine("Received message: " + Encoding.UTF8.GetString(incomingData, 0, result.Count)); + 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)); + } } } } diff --git a/src/Servers/HttpSys/src/AsyncAcceptContext.cs b/src/Servers/HttpSys/src/AsyncAcceptContext.cs index a03ef4c85a67..c7a605b6ae9d 100644 --- a/src/Servers/HttpSys/src/AsyncAcceptContext.cs +++ b/src/Servers/HttpSys/src/AsyncAcceptContext.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Threading.Tasks.Sources; using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.HttpSys.Internal; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -45,9 +46,9 @@ internal ValueTask AcceptAsync() AllocateNativeRequest(); - var statusCode = QueueBeginGetContext(); - if (statusCode != ErrorCodes.ERROR_SUCCESS && - statusCode != ErrorCodes.ERROR_IO_PENDING) + uint statusCode = QueueBeginGetContext(); + if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && + statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) { // some other bad error, possible(?) return values are: // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED @@ -62,8 +63,8 @@ private void IOCompleted(uint errorCode, uint numBytes, bool managed) try { ObserveCompletion(managed); // expectation tracking - if (errorCode != ErrorCodes.ERROR_SUCCESS && - errorCode != ErrorCodes.ERROR_MORE_DATA) + if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && + errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA) { // (keep all the error handling in one place) throw new HttpSysException((int)errorCode); @@ -71,7 +72,7 @@ private void IOCompleted(uint errorCode, uint numBytes, bool managed) Debug.Assert(_requestContext != null); - if (errorCode == ErrorCodes.ERROR_SUCCESS) + if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { var requestContext = _requestContext; // It's important that we clear the request context before we set the result @@ -93,10 +94,10 @@ private void IOCompleted(uint errorCode, uint numBytes, bool managed) 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. - var statusCode = QueueBeginGetContext(); + uint statusCode = QueueBeginGetContext(); - if (statusCode != ErrorCodes.ERROR_SUCCESS && - statusCode != ErrorCodes.ERROR_IO_PENDING) + if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && + statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) { // some other bad error, possible(?) return values are: // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED @@ -179,7 +180,7 @@ 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 - 0u, + (uint)HttpApiTypes.HTTP_FLAGS.NONE, _requestContext.NativeRequest, _requestContext.Size, &bytesTransferred, @@ -187,7 +188,7 @@ private uint QueueBeginGetContext() switch (statusCode) { - case (ErrorCodes.ERROR_CONNECTION_INVALID or ErrorCodes.ERROR_INVALID_PARAMETER) when _requestContext.RequestId != 0: + case (UnsafeNclNativeMethods.ErrorCodes.ERROR_CONNECTION_INVALID or UnsafeNclNativeMethods.ErrorCodes.ERROR_INVALID_PARAMETER) when _requestContext.RequestId != 0: // ERROR_CONNECTION_INVALID: // The client reset the connection between the time we got the MORE_DATA error and when we called HttpReceiveHttpRequest // with the new buffer. We can clear the request id and move on to the next request. @@ -201,7 +202,7 @@ private uint QueueBeginGetContext() _requestContext.RequestId = 0; retry = true; break; - case ErrorCodes.ERROR_MORE_DATA: + case UnsafeNclNativeMethods.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 // (uint)backingBuffer.Length - AlignmentPadding @@ -209,7 +210,7 @@ private uint QueueBeginGetContext() AllocateNativeRequest(bytesTransferred); retry = true; break; - case ErrorCodes.ERROR_SUCCESS: + case UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS: if (HttpSysListener.SkipIOCPCallbackOnSuccess) { // IO operation completed synchronously - callback won't be called to signal completion. @@ -217,7 +218,7 @@ private uint QueueBeginGetContext() } // else: callback fired by IOCP (at some point), which marks completion break; - case ErrorCodes.ERROR_IO_PENDING: + case UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING: break; // no change to state - callback will occur at some point default: // fault code, not expecting an IOCP callback diff --git a/src/Servers/HttpSys/src/AuthenticationManager.cs b/src/Servers/HttpSys/src/AuthenticationManager.cs index 21027d6a2cbe..6c90f9a0959c 100644 --- a/src/Servers/HttpSys/src/AuthenticationManager.cs +++ b/src/Servers/HttpSys/src/AuthenticationManager.cs @@ -4,10 +4,10 @@ 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; -using Windows.Win32.Networking.HttpServer; +using static Microsoft.AspNetCore.HttpSys.Internal.HttpApiTypes; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -20,7 +20,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; @@ -97,23 +97,25 @@ private unsafe void SetUrlGroupSecurity() return; } - var authInfo = new HTTP_SERVER_AUTHENTICATION_INFO(); - authInfo.Flags = HttpApi.HTTP_PROPERTY_FLAGS_PRESENT; + HttpApiTypes.HTTP_SERVER_AUTHENTICATION_INFO authInfo = + new HttpApiTypes.HTTP_SERVER_AUTHENTICATION_INFO(); - if (_authSchemes != AuthenticationSchemes.None) + authInfo.Flags = HttpApiTypes.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT; + var authSchemes = (HttpApiTypes.HTTP_AUTH_TYPES)_authSchemes; + if (authSchemes != HttpApiTypes.HTTP_AUTH_TYPES.NONE) { - authInfo.AuthSchemes = (uint)_authSchemes; + authInfo.AuthSchemes = authSchemes; authInfo.ExFlags = 0; if (EnableKerberosCredentialCaching) { - authInfo.ExFlags |= (byte)PInvoke.HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING; + authInfo.ExFlags |= (byte)HTTP_AUTH_EX_FLAGS.HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING; } if (CaptureCredentials) { - authInfo.ExFlags |= (byte)PInvoke.HTTP_AUTH_EX_FLAG_CAPTURE_CREDENTIAL; + authInfo.ExFlags |= (byte)HTTP_AUTH_EX_FLAGS.HTTP_AUTH_EX_FLAG_CAPTURE_CREDENTIAL; } // TODO: @@ -122,16 +124,16 @@ private unsafe void SetUrlGroupSecurity() // Digest domain and realm - HTTP_SERVER_AUTHENTICATION_DIGEST_PARAMS // Basic realm - HTTP_SERVER_AUTHENTICATION_BASIC_PARAMS - HTTP_SERVER_PROPERTY property; + HttpApiTypes.HTTP_SERVER_PROPERTY property; if (authInfo.ExFlags != 0) { // We need to modify extended fields such as ExFlags, set the extended auth property. - property = HTTP_SERVER_PROPERTY.HttpServerExtendedAuthenticationProperty; + property = HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerExtendedAuthenticationProperty; } else { // Otherwise set the regular auth property. - property = HTTP_SERVER_PROPERTY.HttpServerAuthenticationProperty; + property = HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerAuthenticationProperty; } IntPtr infoptr = new IntPtr(&authInfo); diff --git a/src/Servers/HttpSys/src/HttpSysListener.cs b/src/Servers/HttpSys/src/HttpSysListener.cs index 81d333e027ff..2b7924491d32 100644 --- a/src/Servers/HttpSys/src/HttpSysListener.cs +++ b/src/Servers/HttpSys/src/HttpSysListener.cs @@ -6,9 +6,6 @@ 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; @@ -53,6 +50,8 @@ 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(); @@ -305,12 +304,10 @@ internal bool ValidateRequest(NativeRequestContext requestMemory) internal unsafe void SendError(ulong requestId, int httpStatusCode, IList? authChallenges = null) { - var httpResponse = new HTTP_RESPONSE_V2(); - httpResponse.Base.Version = new() - { - MajorVersion = 1, - MinorVersion = 1 - }; + 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; using UnmanagedBufferAllocator allocator = new(); @@ -320,29 +317,29 @@ internal unsafe void SendError(ulong requestId, int httpStatusCode, IList 0) { - var knownHeaderInfo = allocator.AllocAsPointer(1); + HttpApiTypes.HTTP_RESPONSE_INFO* knownHeaderInfo = allocator.AllocAsPointer(1); httpResponse.pResponseInfo = knownHeaderInfo; - knownHeaderInfo[httpResponse.ResponseInfoCount].Type = HTTP_RESPONSE_INFO_TYPE.HttpResponseInfoTypeMultipleKnownHeaders; + knownHeaderInfo[httpResponse.ResponseInfoCount].Type = HttpApiTypes.HTTP_RESPONSE_INFO_TYPE.HttpResponseInfoTypeMultipleKnownHeaders; knownHeaderInfo[httpResponse.ResponseInfoCount].Length = - (uint)sizeof(HTTP_MULTIPLE_KNOWN_HEADERS); + (uint)sizeof(HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS); - var header = allocator.AllocAsPointer(1); + HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS* header = allocator.AllocAsPointer(1); - 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->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->KnownHeaderCount = 0; - var nativeHeaderValues = allocator.AllocAsPointer(authChallenges.Count); + HttpApiTypes.HTTP_KNOWN_HEADER* nativeHeaderValues = allocator.AllocAsPointer(authChallenges.Count); header->KnownHeaders = nativeHeaderValues; - for (var headerValueIndex = 0; headerValueIndex < authChallenges.Count; headerValueIndex++) + for (int headerValueIndex = 0; headerValueIndex < authChallenges.Count; headerValueIndex++) { // Add Value - var headerValue = authChallenges[headerValueIndex]; + string headerValue = authChallenges[headerValueIndex]; bytes = allocator.GetHeaderEncodedBytes(headerValue, out bytesLength); nativeHeaderValues[header->KnownHeaderCount].RawValueLength = checked((ushort)bytesLength); - nativeHeaderValues[header->KnownHeaderCount].pRawValue = (PCSTR)bytes; + nativeHeaderValues[header->KnownHeaderCount].pRawValue = bytes; header->KnownHeaderCount++; } @@ -351,38 +348,40 @@ internal unsafe void SendError(ulong requestId, int httpStatusCode, IList(contentLengthLength + 1); + byte* pContentLength = allocator.AllocAsPointer(contentLengthLength + 1); pContentLength[0] = (byte)'0'; pContentLength[1] = 0; // null terminator - 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) + (&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) { // if we fail to send a 401 something's seriously wrong, abort the request - PInvoke.HttpCancelHttpRequest(_requestQueue.Handle, requestId, default); + HttpApi.HttpCancelHttpRequest(_requestQueue.Handle, requestId, IntPtr.Zero); } } diff --git a/src/Servers/HttpSys/src/Microsoft.AspNetCore.Server.HttpSys.csproj b/src/Servers/HttpSys/src/Microsoft.AspNetCore.Server.HttpSys.csproj index 0b1b563e2f6b..6e7ffa34cf3b 100644 --- a/src/Servers/HttpSys/src/Microsoft.AspNetCore.Server.HttpSys.csproj +++ b/src/Servers/HttpSys/src/Microsoft.AspNetCore.Server.HttpSys.csproj @@ -9,6 +9,7 @@ aspnetcore;weblistener;httpsys false true + $(NoWarn);CA1416 @@ -22,7 +23,7 @@ - + @@ -32,19 +33,11 @@ - - - - - - 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 f4dc688f3478..26f95d13674d 100644 --- a/src/Servers/HttpSys/src/NativeInterop/DisconnectListener.cs +++ b/src/Servers/HttpSys/src/NativeInterop/DisconnectListener.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.ComponentModel; +using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -94,8 +95,8 @@ private unsafe CancellationToken CreateDisconnectToken(ulong connectionId) Log.CreateDisconnectTokenError(_logger, exception); } - if (statusCode != ErrorCodes.ERROR_IO_PENDING && - statusCode != ErrorCodes.ERROR_SUCCESS) + if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING && + statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { // We got an unknown result, assume the connection has been closed. boundHandle.FreeNativeOverlapped(nativeOverlapped); @@ -104,7 +105,7 @@ private unsafe CancellationToken CreateDisconnectToken(ulong connectionId) cts.Cancel(); } - if (statusCode == ErrorCodes.ERROR_SUCCESS && HttpSysListener.SkipIOCPCallbackOnSuccess) + if (statusCode == UnsafeNclNativeMethods.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 deleted file mode 100644 index d66397390430..000000000000 --- a/src/Servers/HttpSys/src/NativeInterop/ErrorCodes.cs +++ /dev/null @@ -1,23 +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; - -// 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 1fec2ffea7e6..575ebc259d25 100644 --- a/src/Servers/HttpSys/src/NativeInterop/HttpApi.cs +++ b/src/Servers/HttpSys/src/NativeInterop/HttpApi.cs @@ -3,63 +3,149 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using Windows.Win32; -using Windows.Win32.Networking.HttpServer; +using Microsoft.AspNetCore.HttpSys.Internal; +using static Microsoft.AspNetCore.HttpSys.Internal.HttpApiTypes; namespace Microsoft.AspNetCore.Server.HttpSys; -internal static partial class HttpApi +internal static unsafe 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 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); + 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); [LibraryImport(HTTPAPI, SetLastError = true)] - 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); + internal static partial uint HttpCloseUrlGroup(ulong urlGroupId); [LibraryImport(HTTPAPI, SetLastError = true)] - 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); + 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); [LibraryImport(HTTPAPI, SetLastError = true)] - internal static unsafe partial uint HttpWaitForDisconnectEx(SafeHandle requestQueueHandle, ulong connectionId, uint reserved, NativeOverlapped* overlapped); + internal static unsafe partial uint HttpCloseRequestQueue(IntPtr pReqQueueHandle); [LibraryImport(HTTPAPI, SetLastError = true)] - 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); + [return: MarshalAs(UnmanagedType.Bool)] + internal static partial bool HttpIsFeatureSupported(HTTP_FEATURE_ID feature); - [LibraryImport(api_ms_win_core_io_LIB, SetLastError = true)] - internal static partial uint CancelIoEx(SafeHandle handle, SafeNativeOverlapped overlapped); + [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); - internal unsafe delegate uint HttpGetRequestPropertyInvoker(SafeHandle requestQueueHandle, ulong requestId, HTTP_REQUEST_PROPERTY propertyId, + internal delegate uint HttpGetRequestPropertyInvoker(SafeHandle requestQueueHandle, ulong requestId, HTTP_REQUEST_PROPERTY propertyId, void* qualifier, uint qualifierSize, void* output, uint outputSize, uint* bytesReturned, IntPtr overlapped); - internal unsafe delegate uint HttpSetRequestPropertyInvoker(SafeHandle requestQueueHandle, ulong requestId, HTTP_REQUEST_PROPERTY propertyId, void* input, uint inputSize, 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. - // 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; } + 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; } [MemberNotNullWhen(true, nameof(HttpSetRequestProperty))] - internal static bool SupportsTrailers { get; } + internal static bool SupportsTrailers { get; private set; } [MemberNotNullWhen(true, nameof(HttpSetRequestProperty))] - internal static bool SupportsReset { get; } - internal static bool SupportsDelegation { get; } - internal static bool Supported { get; } + internal static bool SupportsReset { get; private set; } + internal static bool SupportsDelegation { get; private set; } + + static HttpApi() + { + InitHttpApi(2, 0); + } - static unsafe HttpApi() + private static void InitHttpApi(ushort majorVersion, ushort minorVersion) { - var statusCode = PInvoke.HttpInitialize(Version, HTTP_INITIALIZE.HTTP_INITIALIZE_SERVER | HTTP_INITIALIZE.HTTP_INITIALIZE_CONFIG); + version.HttpApiMajorVersion = majorVersion; + version.HttpApiMinorVersion = minorVersion; + + var statusCode = HttpInitialize(version, (uint)(HTTP_FLAGS.HTTP_INITIALIZE_SERVER | HTTP_FLAGS.HTTP_INITIALIZE_CONFIG), null); - if (statusCode == ErrorCodes.ERROR_SUCCESS) + supported = statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS; + + if (supported) { - Supported = true; HttpApiModule = SafeLibraryHandle.Open(HTTPAPI); HttpGetRequestProperty = HttpApiModule.GetProcAddress("HttpQueryRequestProperty", throwIfNotFound: false); HttpSetRequestProperty = HttpApiModule.GetProcAddress("HttpSetRequestProperty", throwIfNotFound: false); @@ -69,11 +155,20 @@ static unsafe HttpApi() } } + private static volatile bool supported; + internal static bool Supported + { + get + { + return supported; + } + } + private static bool IsFeatureSupported(HTTP_FEATURE_ID feature) { try { - return PInvoke.HttpIsFeatureSupported(feature); + return HttpIsFeatureSupported(feature); } catch (EntryPointNotFoundException) { } diff --git a/src/Servers/HttpSys/src/NativeInterop/HttpRequestQueueV2Handle.cs b/src/Servers/HttpSys/src/NativeInterop/HttpRequestQueueV2Handle.cs new file mode 100644 index 000000000000..d792748c8934 --- /dev/null +++ b/src/Servers/HttpSys/src/NativeInterop/HttpRequestQueueV2Handle.cs @@ -0,0 +1,22 @@ +// 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 1cf4a687e349..3fda8c736137 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 PInvoke.HttpCloseServerSession(serverSessionId) == - ErrorCodes.ERROR_SUCCESS; + return (HttpApi.HttpCloseServerSession(serverSessionId) == + UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS); } } return true; diff --git a/src/Servers/HttpSys/src/NativeInterop/IntPtrHelper.cs b/src/Servers/HttpSys/src/NativeInterop/IntPtrHelper.cs new file mode 100644 index 000000000000..30abb9b8c234 --- /dev/null +++ b/src/Servers/HttpSys/src/NativeInterop/IntPtrHelper.cs @@ -0,0 +1,17 @@ +// 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 810bdcce3c57..4330f40f8620 100644 --- a/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs +++ b/src/Servers/HttpSys/src/NativeInterop/RequestQueue.cs @@ -3,9 +3,8 @@ 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; @@ -29,58 +28,58 @@ private RequestQueue(string? requestQueueName, RequestQueueMode mode, ILogger lo _mode = mode; _logger = logger; - var flags = 0u; + var flags = HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.None; Created = true; if (_mode == RequestQueueMode.Attach) { - flags = PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_OPEN_EXISTING; + flags = HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.OpenExisting; Created = false; if (receiver) { - flags |= PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_DELEGATION; + flags |= HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.Delegation; } } - var statusCode = PInvoke.HttpCreateRequestQueue( + var statusCode = HttpApi.HttpCreateRequestQueue( HttpApi.Version, requestQueueName, - default, + IntPtr.Zero, flags, out var requestQueueHandle); - if (_mode == RequestQueueMode.CreateOrAttach && statusCode == ErrorCodes.ERROR_ALREADY_EXISTS) + if (_mode == RequestQueueMode.CreateOrAttach && statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_ALREADY_EXISTS) { // Tried to create, but it already exists so attach to it instead. Created = false; - flags = PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_OPEN_EXISTING; - statusCode = PInvoke.HttpCreateRequestQueue( + flags = HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.OpenExisting; + statusCode = HttpApi.HttpCreateRequestQueue( HttpApi.Version, requestQueueName, - default, + IntPtr.Zero, flags, out requestQueueHandle); } - if ((flags & PInvoke.HTTP_CREATE_REQUEST_QUEUE_FLAG_OPEN_EXISTING) != 0 && statusCode == ErrorCodes.ERROR_FILE_NOT_FOUND) + if (flags.HasFlag(HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.OpenExisting) && statusCode == UnsafeNclNativeMethods.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 == ErrorCodes.ERROR_INVALID_NAME) + else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_INVALID_NAME) { throw new HttpSysException((int)statusCode, $"The given request queue name '{requestQueueName}' is invalid."); } - else if (statusCode != ErrorCodes.ERROR_SUCCESS) + else if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { throw new HttpSysException((int)statusCode); } // Disabling callbacks when IO operation completes synchronously (returns ErrorCodes.ERROR_SUCCESS) if (HttpSysListener.SkipIOCPCallbackOnSuccess && - !PInvoke.SetFileCompletionNotificationModes( + !UnsafeNclNativeMethods.SetFileCompletionNotificationModes( requestQueueHandle, - (byte)(PInvoke.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS | - PInvoke.FILE_SKIP_SET_EVENT_ON_HANDLE))) + UnsafeNclNativeMethods.FileCompletionNotificationModes.SkipCompletionPortOnSuccess | + UnsafeNclNativeMethods.FileCompletionNotificationModes.SkipSetEventOnHandle)) { requestQueueHandle.Dispose(); throw new HttpSysException(Marshal.GetLastWin32Error()); @@ -109,9 +108,9 @@ internal unsafe void SetLengthLimit(long length) Debug.Assert(Created); CheckDisposed(); - var result = PInvoke.HttpSetRequestQueueProperty(Handle, - HTTP_SERVER_PROPERTY.HttpServerQueueLengthProperty, - &length, (uint)Marshal.SizeOf()); + var result = HttpApi.HttpSetRequestQueueProperty(Handle, + HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerQueueLengthProperty, + new IntPtr((void*)&length), (uint)Marshal.SizeOf(), 0, IntPtr.Zero); if (result != 0) { @@ -125,9 +124,9 @@ internal unsafe void SetRejectionVerbosity(Http503VerbosityLevel verbosity) Debug.Assert(Created); CheckDisposed(); - var result = PInvoke.HttpSetRequestQueueProperty(Handle, - HTTP_SERVER_PROPERTY.HttpServer503VerbosityProperty, - &verbosity, (uint)Marshal.SizeOf()); + var result = HttpApi.HttpSetRequestQueueProperty(Handle, + HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServer503VerbosityProperty, + new IntPtr((void*)&verbosity), (uint)Marshal.SizeOf(), 0, IntPtr.Zero); if (result != 0) { diff --git a/src/Servers/HttpSys/src/NativeInterop/ServerSession.cs b/src/Servers/HttpSys/src/NativeInterop/ServerSession.cs index 9e19f5994d28..a1cff3fc396e 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 Windows.Win32; +using Microsoft.AspNetCore.HttpSys.Internal; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -11,10 +11,10 @@ internal sealed class ServerSession : IDisposable internal unsafe ServerSession() { ulong serverSessionId = 0; - var statusCode = PInvoke.HttpCreateServerSession( + var statusCode = HttpApi.HttpCreateServerSession( HttpApi.Version, &serverSessionId, 0); - if (statusCode != ErrorCodes.ERROR_SUCCESS) + if (statusCode != UnsafeNclNativeMethods.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 new file mode 100644 index 000000000000..67364726dc89 --- /dev/null +++ b/src/Servers/HttpSys/src/NativeInterop/TokenBindingUtil.cs @@ -0,0 +1,80 @@ +// 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 9dfc3ce9a9de..dbde24640ef5 100644 --- a/src/Servers/HttpSys/src/NativeInterop/UrlGroup.cs +++ b/src/Servers/HttpSys/src/NativeInterop/UrlGroup.cs @@ -3,21 +3,19 @@ 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; @@ -32,10 +30,12 @@ internal unsafe UrlGroup(ServerSession serverSession, RequestQueue requestQueue, _requestQueue = requestQueue; _logger = logger; + ulong urlGroupId = 0; _created = true; - var statusCode = PInvoke.HttpCreateUrlGroup(_serverSession.Id.DangerousGetServerSessionId(), out var urlGroupId); + var statusCode = HttpApi.HttpCreateUrlGroup( + _serverSession.Id.DangerousGetServerSessionId(), &urlGroupId, 0); - if (statusCode != ErrorCodes.ERROR_SUCCESS) + if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { throw new HttpSysException((int)statusCode); } @@ -48,50 +48,43 @@ internal unsafe UrlGroup(ServerSession serverSession, RequestQueue requestQueue, internal unsafe void SetMaxConnections(long maxConnections) { - var connectionLimit = new HTTP_CONNECTION_LIMIT_INFO - { - Flags = HttpApi.HTTP_PROPERTY_FLAGS_PRESENT, - MaxConnections = (uint)maxConnections - }; + var connectionLimit = new HttpApiTypes.HTTP_CONNECTION_LIMIT_INFO(); + connectionLimit.Flags = HttpApiTypes.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT; + connectionLimit.MaxConnections = (uint)maxConnections; - var qosSettings = new HTTP_QOS_SETTING_INFO - { - QosType = HTTP_QOS_SETTING_TYPE.HttpQosSettingTypeConnectionLimit, - QosSetting = &connectionLimit - }; + var qosSettings = new HttpApiTypes.HTTP_QOS_SETTING_INFO(); + qosSettings.QosType = HttpApiTypes.HTTP_QOS_SETTING_TYPE.HttpQosSettingTypeConnectionLimit; + qosSettings.QosSetting = new IntPtr(&connectionLimit); - SetProperty(HTTP_SERVER_PROPERTY.HttpServerQosProperty, new IntPtr(&qosSettings), (uint)QosInfoSize); + SetProperty(HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerQosProperty, new IntPtr(&qosSettings), (uint)QosInfoSize); } internal unsafe void SetDelegationProperty(RequestQueue destination) { - var propertyInfo = new HTTP_BINDING_INFO - { - Flags = HttpApi.HTTP_PROPERTY_FLAGS_PRESENT, - RequestQueueHandle = (HANDLE)destination.Handle.DangerousGetHandle() - }; + var propertyInfo = new HttpApiTypes.HTTP_BINDING_INFO(); + propertyInfo.Flags = HttpApiTypes.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT; + propertyInfo.RequestQueueHandle = destination.Handle.DangerousGetHandle(); - SetProperty(HTTP_SERVER_PROPERTY.HttpServerDelegationProperty, new IntPtr(&propertyInfo), (uint)RequestPropertyInfoSize); + SetProperty(HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerDelegationProperty, new IntPtr(&propertyInfo), (uint)RequestPropertyInfoSize); } internal unsafe void UnSetDelegationProperty(RequestQueue destination, bool throwOnError = true) { - var propertyInfo = new HTTP_BINDING_INFO - { - RequestQueueHandle = (HANDLE)destination.Handle.DangerousGetHandle() - }; + var propertyInfo = new HttpApiTypes.HTTP_BINDING_INFO(); + propertyInfo.Flags = HttpApiTypes.HTTP_FLAGS.NONE; + propertyInfo.RequestQueueHandle = destination.Handle.DangerousGetHandle(); - SetProperty(HTTP_SERVER_PROPERTY.HttpServerDelegationProperty, new IntPtr(&propertyInfo), (uint)RequestPropertyInfoSize, throwOnError); + SetProperty(HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerDelegationProperty, new IntPtr(&propertyInfo), (uint)RequestPropertyInfoSize, throwOnError); } - internal unsafe void SetProperty(HTTP_SERVER_PROPERTY property, IntPtr info, uint infosize, bool throwOnError = true) + internal void SetProperty(HttpApiTypes.HTTP_SERVER_PROPERTY property, IntPtr info, uint infosize, bool throwOnError = true) { Debug.Assert(info != IntPtr.Zero, "SetUrlGroupProperty called with invalid pointer"); CheckDisposed(); - var statusCode = PInvoke.HttpSetUrlGroupProperty(Id, property, info.ToPointer(), infosize); + var statusCode = HttpApi.HttpSetUrlGroupProperty(Id, property, info, infosize); - if (statusCode != ErrorCodes.ERROR_SUCCESS) + if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { var exception = new HttpSysException((int)statusCode); Log.SetUrlPropertyError(_logger, exception); @@ -108,15 +101,13 @@ 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 HTTP_BINDING_INFO - { - Flags = HttpApi.HTTP_PROPERTY_FLAGS_PRESENT, - RequestQueueHandle = (HANDLE)_requestQueue.Handle.DangerousGetHandle() - }; + var info = new HttpApiTypes.HTTP_BINDING_INFO(); + info.Flags = HttpApiTypes.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT; + info.RequestQueueHandle = _requestQueue.Handle.DangerousGetHandle(); var infoptr = new IntPtr(&info); - SetProperty(HTTP_SERVER_PROPERTY.HttpServerBindingProperty, + SetProperty(HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerBindingProperty, infoptr, (uint)BindingInfoSize); } @@ -129,10 +120,13 @@ internal unsafe void DetachFromQueue() // is fine since http.sys allows to set HttpServerBindingProperty multiple times for valid // Url groups. - var info = new HTTP_BINDING_INFO(); + var info = new HttpApiTypes.HTTP_BINDING_INFO(); + info.Flags = HttpApiTypes.HTTP_FLAGS.NONE; + info.RequestQueueHandle = IntPtr.Zero; + var infoptr = new IntPtr(&info); - SetProperty(HTTP_SERVER_PROPERTY.HttpServerBindingProperty, + SetProperty(HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerBindingProperty, infoptr, (uint)BindingInfoSize, throwOnError: false); } @@ -140,10 +134,11 @@ internal void RegisterPrefix(string uriPrefix, int contextId) { Log.RegisteringPrefix(_logger, uriPrefix); CheckDisposed(); - var statusCode = PInvoke.HttpAddUrlToUrlGroup(Id, uriPrefix, (ulong)contextId); - if (statusCode != ErrorCodes.ERROR_SUCCESS) + var statusCode = HttpApi.HttpAddUrlToUrlGroup(Id, uriPrefix, (ulong)contextId, 0); + + if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { - if (statusCode == ErrorCodes.ERROR_ALREADY_EXISTS) + if (statusCode == UnsafeNclNativeMethods.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. @@ -151,8 +146,9 @@ internal void RegisterPrefix(string uriPrefix, int contextId) { unsafe { - var findUrlStatusCode = PInvoke.HttpFindUrlGroupId(uriPrefix, _requestQueue.Handle, out var _); - if (findUrlStatusCode == ErrorCodes.ERROR_SUCCESS) + ulong urlGroupId; + var findUrlStatusCode = HttpApi.HttpFindUrlGroupId(uriPrefix, _requestQueue.Handle, &urlGroupId); + if (findUrlStatusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { // Already registered for the desired queue, all good return; @@ -162,7 +158,7 @@ internal void RegisterPrefix(string uriPrefix, int contextId) throw new HttpSysException((int)statusCode, Resources.FormatException_PrefixAlreadyRegistered(uriPrefix)); } - if (statusCode == ErrorCodes.ERROR_ACCESS_DENIED) + if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_ACCESS_DENIED) { throw new HttpSysException((int)statusCode, Resources.FormatException_AccessDenied(uriPrefix, Environment.UserDomainName + @"\" + Environment.UserName)); } @@ -175,7 +171,7 @@ internal void UnregisterPrefix(string uriPrefix) Log.UnregisteringPrefix(_logger, uriPrefix); CheckDisposed(); - PInvoke.HttpRemoveUrlFromUrlGroup(Id, uriPrefix, 0); + HttpApi.HttpRemoveUrlFromUrlGroup(Id, uriPrefix, 0); } public void Dispose() @@ -192,9 +188,9 @@ public void Dispose() Debug.Assert(Id != 0, "HttpCloseUrlGroup called with invalid url group id"); - var statusCode = PInvoke.HttpCloseUrlGroup(Id); + uint statusCode = HttpApi.HttpCloseUrlGroup(Id); - if (statusCode != ErrorCodes.ERROR_SUCCESS) + if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { Log.CloseUrlGroupError(_logger, statusCode); } diff --git a/src/Servers/HttpSys/src/NativeMethods.txt b/src/Servers/HttpSys/src/NativeMethods.txt deleted file mode 100644 index 3d6f700d6101..000000000000 --- a/src/Servers/HttpSys/src/NativeMethods.txt +++ /dev/null @@ -1,59 +0,0 @@ -// 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_EX_FLAG_* -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 56a5ae4f8fe7..63ae12d19015 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/ClientCertLoader.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/ClientCertLoader.cs @@ -1,13 +1,16 @@ // 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 Windows.Win32.Networking.HttpServer; +using Microsoft.AspNetCore.HttpSys.Internal; +using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -15,12 +18,14 @@ 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 CertBlobSize = 1500; + private const uint CertBoblSize = 1500; private static readonly IOCompletionCallback IOCallback = new IOCompletionCallback(WaitCallback); + private static readonly int RequestChannelBindStatusSize = + Marshal.SizeOf(); private SafeNativeOverlapped? _overlapped; private byte[]? _backingBuffer; - private HTTP_SSL_CLIENT_CERT_INFO* _memoryBlob; + private HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO* _memoryBlob; private uint _size; private readonly TaskCompletionSource _tcs; private readonly RequestContext _requestContext; @@ -36,7 +41,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(CertBlobSize); + Reset(CertBoblSize); if (cancellationToken.CanBeCanceled) { @@ -97,7 +102,7 @@ private SafeNativeOverlapped? NativeOverlapped } } - private HTTP_SSL_CLIENT_CERT_INFO* RequestBlob + private HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO* RequestBlob { get { @@ -127,7 +132,7 @@ private void Reset(uint size) var boundHandle = RequestContext.Server.RequestQueue.BoundHandle; _overlapped = new SafeNativeOverlapped(boundHandle, boundHandle.AllocateNativeOverlapped(IOCallback, this, _backingBuffer)); - _memoryBlob = (HTTP_SSL_CLIENT_CERT_INFO*)Marshal.UnsafeAddrOfPinnedArrayElement(_backingBuffer, 0); + _memoryBlob = (HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO*)Marshal.UnsafeAddrOfPinnedArrayElement(_backingBuffer, 0); } // When you use netsh to configure HTTP.SYS with clientcertnegotiation = enable @@ -150,42 +155,42 @@ private void Reset(uint size) // HTTP.SYS will not do this for you automatically internal Task LoadClientCertificateAsync() { - var size = CertBlobSize; + uint size = CertBoblSize; bool retry; do { retry = false; uint bytesReceived = 0; - var statusCode = + uint statusCode = HttpApi.HttpReceiveClientCertificate( RequestQueueHandle, RequestContext.Request.UConnectionId, - 0u, + (uint)HttpApiTypes.HTTP_FLAGS.NONE, RequestBlob, size, &bytesReceived, NativeOverlapped!); - if (statusCode == ErrorCodes.ERROR_MORE_DATA) + if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA) { - var pClientCertInfo = RequestBlob; + HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = RequestBlob; size = bytesReceived + pClientCertInfo->CertEncodedSize; Reset(size); retry = true; } - else if (statusCode == ErrorCodes.ERROR_NOT_FOUND) + else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND) { // The client did not send a cert. Complete(0, null); } - else if (statusCode == ErrorCodes.ERROR_SUCCESS && + else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && HttpSysListener.SkipIOCPCallbackOnSuccess) { IOCompleted(statusCode, bytesReceived); } - else if (statusCode != ErrorCodes.ERROR_SUCCESS && - statusCode != ErrorCodes.ERROR_IO_PENDING) + else if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && + statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) { // Some other bad error, possible(?) return values are: // ERROR_INVALID_HANDLE, ERROR_INSUFFICIENT_BUFFER, ERROR_OPERATION_ABORTED @@ -223,16 +228,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) { - var requestContext = asyncResult.RequestContext; + RequestContext requestContext = asyncResult.RequestContext; try { - if (errorCode == ErrorCodes.ERROR_MORE_DATA) + if (errorCode == UnsafeNclNativeMethods.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. - var pClientCertInfo = asyncResult.RequestBlob; + HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = asyncResult.RequestBlob; asyncResult.Reset(numBytes + pClientCertInfo->CertEncodedSize); uint bytesReceived = 0; @@ -240,31 +245,31 @@ private static unsafe void IOCompleted(ClientCertLoader asyncResult, uint errorC HttpApi.HttpReceiveClientCertificate( requestContext.Server.RequestQueue.Handle, requestContext.Request.UConnectionId, - 0u, + (uint)HttpApiTypes.HTTP_FLAGS.NONE, asyncResult._memoryBlob, asyncResult._size, &bytesReceived, asyncResult._overlapped!); - if (errorCode == ErrorCodes.ERROR_IO_PENDING || - (errorCode == ErrorCodes.ERROR_SUCCESS && !HttpSysListener.SkipIOCPCallbackOnSuccess)) + if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING || + (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && !HttpSysListener.SkipIOCPCallbackOnSuccess)) { return; } } - if (errorCode == ErrorCodes.ERROR_NOT_FOUND) + if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND) { // The client did not send a cert. asyncResult.Complete(0, null); } - else if (errorCode != ErrorCodes.ERROR_SUCCESS) + else if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { asyncResult.Fail(new HttpSysException((int)errorCode)); } else { - var pClientCertInfo = asyncResult._memoryBlob; + HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo = asyncResult._memoryBlob; if (pClientCertInfo == null) { asyncResult.Complete(0, null); @@ -275,7 +280,7 @@ private static unsafe void IOCompleted(ClientCertLoader asyncResult, uint errorC { try { - var certEncoded = new byte[pClientCertInfo->CertEncodedSize]; + byte[] certEncoded = new byte[pClientCertInfo->CertEncodedSize]; Marshal.Copy((IntPtr)pClientCertInfo->pCertEncoded, certEncoded, 0, certEncoded.Length); asyncResult.Complete((int)pClientCertInfo->CertFlags, new X509Certificate2(certEncoded)); } @@ -342,4 +347,83 @@ 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 290218a7e3b8..0b9418cbcac6 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/Request.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/Request.cs @@ -13,7 +13,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; -using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -62,12 +61,12 @@ internal Request(RequestContext requestContext) var prefix = requestContext.Server.Options.UrlPrefixes.GetPrefix((int)requestContext.UrlContext); // 'OPTIONS * HTTP/1.1' - if (KnownMethod == HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawUrl, "*", StringComparison.Ordinal)) + if (KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawUrl, "*", StringComparison.Ordinal)) { PathBase = string.Empty; Path = string.Empty; } - // Prefix may be null if the requested has been transferred to our queue + // Prefix may be null if the requested has been transfered to our queue else if (prefix is not null) { var pathBase = prefix.PathWithoutTrailingSlash; @@ -231,9 +230,9 @@ public long? ContentLength public RequestHeaders Headers { get; } - internal HTTP_VERB KnownMethod { get; } + internal HttpApiTypes.HTTP_VERB KnownMethod { get; } - internal bool IsHeadMethod => KnownMethod == HTTP_VERB.HttpVerbHEAD; + internal bool IsHeadMethod => KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbHEAD; public string Method { get; } @@ -344,16 +343,16 @@ private AspNetCore.HttpSys.Internal.SocketAddress LocalEndPoint private void GetTlsHandshakeResults() { var handshake = RequestContext.GetTlsHandshake(); - Protocol = (SslProtocols)handshake.Protocol; - CipherAlgorithm = (CipherAlgorithmType)handshake.CipherType; + Protocol = handshake.Protocol; + CipherAlgorithm = handshake.CipherType; CipherStrength = (int)handshake.CipherStrength; - HashAlgorithm = (HashAlgorithmType)handshake.HashType; + HashAlgorithm = handshake.HashType; HashStrength = (int)handshake.HashStrength; - KeyExchangeAlgorithm = (ExchangeAlgorithmType)handshake.KeyExchangeType; + KeyExchangeAlgorithm = handshake.KeyExchangeType; KeyExchangeStrength = (int)handshake.KeyExchangeStrength; var sni = RequestContext.GetClientSni(); - SniHostName = sni.Hostname.ToString(); + SniHostName = sni.Hostname; } public X509Certificate2? ClientCertificate diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs index 9faaab9e831d..8da0c7a33e37 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,6 +110,22 @@ 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. /// @@ -192,13 +208,13 @@ internal void ForceCancelRequest() { try { - var statusCode = PInvoke.HttpCancelHttpRequest(Server.RequestQueue.Handle, - Request.RequestId, default); + var statusCode = HttpApi.HttpCancelHttpRequest(Server.RequestQueue.Handle, + Request.RequestId, IntPtr.Zero); // 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 == ErrorCodes.ERROR_CONNECTION_INVALID) + if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_CONNECTION_INVALID) { Response.CancelLastWrite(); } @@ -209,7 +225,7 @@ internal void ForceCancelRequest() } } - internal unsafe HTTP_REQUEST_PROPERTY_SNI GetClientSni() + internal unsafe HttpApiTypes.HTTP_REQUEST_PROPERTY_SNI GetClientSni() { if (HttpApi.HttpGetRequestProperty != null) { @@ -219,7 +235,7 @@ internal unsafe HTTP_REQUEST_PROPERTY_SNI GetClientSni() var statusCode = HttpApi.HttpGetRequestProperty( Server.RequestQueue.Handle, RequestId, - HTTP_REQUEST_PROPERTY.HttpRequestPropertySni, + HttpApiTypes.HTTP_REQUEST_PROPERTY.HttpRequestPropertySni, qualifier: null, qualifierSize: 0, (void*)pBuffer, @@ -227,9 +243,9 @@ internal unsafe HTTP_REQUEST_PROPERTY_SNI GetClientSni() bytesReturned: null, IntPtr.Zero); - if (statusCode == ErrorCodes.ERROR_SUCCESS) + if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS) { - return Marshal.PtrToStructure((IntPtr)pBuffer); + return Marshal.PtrToStructure((IntPtr)pBuffer); } } } @@ -247,9 +263,9 @@ internal unsafe void SetResetCode(int errorCode) try { - 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); + var streamError = new HttpApiTypes.HTTP_REQUEST_PROPERTY_STREAM_ERROR() { ErrorCode = (uint)errorCode }; + var statusCode = HttpApi.HttpSetRequestProperty(Server.RequestQueue.Handle, Request.RequestId, HttpApiTypes.HTTP_REQUEST_PROPERTY.HttpRequestPropertyStreamError, (void*)&streamError, + (uint)sizeof(HttpApiTypes.HTTP_REQUEST_PROPERTY_STREAM_ERROR), IntPtr.Zero); } catch (ObjectDisposedException) { @@ -291,25 +307,25 @@ internal unsafe void Delegate(DelegationRule destination) fixed (char* uriPointer = destination.UrlPrefix) { - var property = new HTTP_DELEGATE_REQUEST_PROPERTY_INFO() + var property = new HttpApiTypes.HTTP_DELEGATE_REQUEST_PROPERTY_INFO() { - PropertyId = HTTP_DELEGATE_REQUEST_PROPERTY_ID.DelegateRequestDelegateUrlProperty, - PropertyInfo = uriPointer, + PropertyId = HttpApiTypes.HTTP_DELEGATE_REQUEST_PROPERTY_ID.DelegateRequestDelegateUrlProperty, + PropertyInfo = (IntPtr)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 = PInvoke.HttpDelegateRequestEx(source.Handle, + statusCode = HttpApi.HttpDelegateRequestEx(source.Handle, destination.Queue.Handle, Request.RequestId, - DelegateUrlGroupId: 0, - PropertyInfoSetSize: 1, - property); + delegateUrlGroupId: 0, + propertyInfoSetSize: 1, + &property); } - if (statusCode != ErrorCodes.ERROR_SUCCESS) + if (statusCode != UnsafeNclNativeMethods.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 f41b04fc0b1f..018877e8b271 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,25 +133,26 @@ public override unsafe int Read([In, Out] byte[] buffer, int offset, int size) uint flags = 0; - statusCode = PInvoke.HttpReceiveRequestEntityBody( - RequestQueueHandle, - RequestId, - flags, - (pBuffer + offset), - (uint)size, - &extraDataRead, - default); + statusCode = + HttpApi.HttpReceiveRequestEntityBody( + RequestQueueHandle, + RequestId, + flags, + (IntPtr)(pBuffer + offset), + (uint)size, + out extraDataRead, + SafeNativeOverlapped.Zero); dataRead += extraDataRead; } // Zero-byte reads - if (statusCode == ErrorCodes.ERROR_MORE_DATA && size == 0) + if (statusCode == UnsafeNclNativeMethods.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 != ErrorCodes.ERROR_SUCCESS && statusCode != ErrorCodes.ERROR_HANDLE_EOF) + else if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) { Exception exception = new IOException(string.Empty, new HttpSysException((int)statusCode)); Log.ErrorWhileRead(Logger, exception); @@ -171,8 +172,8 @@ public override unsafe int Read([In, Out] byte[] buffer, int offset, int size) internal void UpdateAfterRead(uint statusCode, uint dataRead) { - if (statusCode == ErrorCodes.ERROR_HANDLE_EOF - || statusCode != ErrorCodes.ERROR_MORE_DATA && dataRead == 0) + if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF + || statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA && dataRead == 0) { Dispose(); } @@ -207,7 +208,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(ErrorCodes.ERROR_SUCCESS, dataRead); + UpdateAfterRead(UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS, dataRead); if (TryCheckSizeLimit((int)dataRead, out var exception)) { return Task.FromException(exception); @@ -258,10 +259,10 @@ public override unsafe Task ReadAsync(byte[] buffer, int offset, int size, throw; } - if (statusCode != ErrorCodes.ERROR_SUCCESS && statusCode != ErrorCodes.ERROR_IO_PENDING) + if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) { asyncResult.Dispose(); - if (statusCode == ErrorCodes.ERROR_HANDLE_EOF) + if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) { uint totalRead = dataRead + bytesReturned; UpdateAfterRead(statusCode, totalRead); @@ -280,7 +281,7 @@ public override unsafe Task ReadAsync(byte[] buffer, int offset, int size, throw exception; } } - else if (statusCode == ErrorCodes.ERROR_SUCCESS && + else if (statusCode == UnsafeNclNativeMethods.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 f5cc5726992d..e34111ba08c3 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestStreamAsyncResult.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestStreamAsyncResult.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; +using Microsoft.AspNetCore.HttpSys.Internal; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -69,12 +70,12 @@ private static void IOCompleted(RequestStreamAsyncResult asyncResult, uint error try { // Zero-byte reads - if (errorCode == ErrorCodes.ERROR_MORE_DATA && asyncResult._size == 0) + if (errorCode == UnsafeNclNativeMethods.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 != ErrorCodes.ERROR_SUCCESS && errorCode != ErrorCodes.ERROR_HANDLE_EOF) + else if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) { asyncResult.Fail(new IOException(string.Empty, new HttpSysException((int)errorCode))); } @@ -96,7 +97,7 @@ private static unsafe void Callback(uint errorCode, uint numBytes, NativeOverlap IOCompleted(asyncResult, errorCode, numBytes); } - internal void Complete(int read, uint errorCode = ErrorCodes.ERROR_SUCCESS) + internal void Complete(int read, uint errorCode = UnsafeNclNativeMethods.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 9cf919798e89..0257f0ac086b 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/Response.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/Response.cs @@ -10,9 +10,8 @@ using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; -using Windows.Win32; -using Windows.Win32.Foundation; -using Windows.Win32.Networking.HttpServer; + +using static Microsoft.AspNetCore.HttpSys.Internal.UnsafeNclNativeMethods; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -29,7 +28,7 @@ internal sealed class Response private TimeSpan? _cacheTtl; private long _expectedBodyLength; private BoundaryType _boundaryType; - private HTTP_RESPONSE_V2 _nativeResponse; + private HttpApiTypes.HTTP_RESPONSE_V2 _nativeResponse; private HeaderCollection? _trailers; internal Response(RequestContext requestContext) @@ -39,14 +38,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 HTTP_RESPONSE_V2(); + _nativeResponse = new HttpApiTypes.HTTP_RESPONSE_V2(); Headers.IsReadOnly = false; Headers.Clear(); _reasonPhrase = null; _boundaryType = BoundaryType.None; - _nativeResponse.Base.StatusCode = (ushort)StatusCodes.Status200OK; - _nativeResponse.Base.Version.MajorVersion = 1; - _nativeResponse.Base.Version.MinorVersion = 1; + _nativeResponse.Response_V1.StatusCode = (ushort)StatusCodes.Status200OK; + _nativeResponse.Response_V1.Version.MajorVersion = 1; + _nativeResponse.Response_V1.Version.MinorVersion = 1; _responseState = ResponseState.Created; _expectedBodyLength = 0; _nativeStream = null; @@ -68,7 +67,7 @@ private enum ResponseState public int StatusCode { - get { return _nativeResponse.Base.StatusCode; } + get { return _nativeResponse.Response_V1.StatusCode; } set { // Http.Sys automatically sends 100 Continue responses when you read from the request body. @@ -77,7 +76,7 @@ public int StatusCode throw new ArgumentOutOfRangeException(nameof(value), value, string.Format(CultureInfo.CurrentCulture, Resources.Exception_InvalidStatusCode, value)); } CheckResponseStarted(); - _nativeResponse.Base.StatusCode = (ushort)value; + _nativeResponse.Response_V1.StatusCode = (ushort)value; } } @@ -119,7 +118,7 @@ public AuthenticationSchemes AuthenticationChallenges private string GetReasonPhrase(int statusCode) { - var reasonPhrase = ReasonPhrase; + string? reasonPhrase = ReasonPhrase; if (string.IsNullOrWhiteSpace(reasonPhrase)) { // If the user hasn't set this then it is generated on the fly if possible. @@ -133,7 +132,7 @@ private string GetReasonPhrase(int statusCode) private static bool CanSendResponseBody(int responseCode) { - for (var i = 0; i < StatusWithNoResponseBody.Length; i++) + for (int i = 0; i < StatusWithNoResponseBody.Length; i++) { if (responseCode == StatusWithNoResponseBody[i]) { @@ -277,9 +276,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, - uint flags, + HttpApiTypes.HTTP_FLAGS flags, bool isOpaqueUpgrade) { Debug.Assert(!HasStarted, "HttpListenerResponse::SendHeaders()|SentHeaders is true."); @@ -292,42 +291,42 @@ internal unsafe uint SendHeaders(ref UnmanagedBufferAllocator allocator, SerializeHeaders(ref allocator, isOpaqueUpgrade); - fixed (HTTP_DATA_CHUNK* chunks = dataChunks) + fixed (HttpApiTypes.HTTP_DATA_CHUNK* chunks = dataChunks) { if (chunks != null) { - _nativeResponse.Base.EntityChunkCount = checked((ushort)dataChunks.Length); - _nativeResponse.Base.pEntityChunks = chunks; + _nativeResponse.Response_V1.EntityChunkCount = checked((ushort)dataChunks.Length); + _nativeResponse.Response_V1.pEntityChunks = chunks; } else if (asyncResult != null && asyncResult.DataChunks != null) { - _nativeResponse.Base.EntityChunkCount = asyncResult.DataChunkCount; - _nativeResponse.Base.pEntityChunks = asyncResult.DataChunks; + _nativeResponse.Response_V1.EntityChunkCount = asyncResult.DataChunkCount; + _nativeResponse.Response_V1.pEntityChunks = asyncResult.DataChunks; } else { - _nativeResponse.Base.EntityChunkCount = 0; - _nativeResponse.Base.pEntityChunks = null; + _nativeResponse.Response_V1.EntityChunkCount = 0; + _nativeResponse.Response_V1.pEntityChunks = null; } - var cachePolicy = new HTTP_CACHE_POLICY(); + var cachePolicy = new HttpApiTypes.HTTP_CACHE_POLICY(); if (_cacheTtl.HasValue && _cacheTtl.Value > TimeSpan.Zero) { - cachePolicy.Policy = HTTP_CACHE_POLICY_TYPE.HttpCachePolicyTimeToLive; + cachePolicy.Policy = HttpApiTypes.HTTP_CACHE_POLICY_TYPE.HttpCachePolicyTimeToLive; cachePolicy.SecondsToLive = (uint)Math.Min(_cacheTtl.Value.Ticks / TimeSpan.TicksPerSecond, Int32.MaxValue); } - var pReasonPhrase = allocator.GetHeaderEncodedBytes(reasonPhrase, out var pReasonPhraseLength); - _nativeResponse.Base.ReasonLength = checked((ushort)pReasonPhraseLength); - _nativeResponse.Base.pReason = (PCSTR)pReasonPhrase; + byte* pReasonPhrase = allocator.GetHeaderEncodedBytes(reasonPhrase, out int pReasonPhraseLength); + _nativeResponse.Response_V1.ReasonLength = checked((ushort)pReasonPhraseLength); + _nativeResponse.Response_V1.pReason = pReasonPhrase; - fixed (HTTP_RESPONSE_V2* pResponse = &_nativeResponse) + fixed (HttpApiTypes.HTTP_RESPONSE_V2* pResponse = &_nativeResponse) { statusCode = HttpApi.HttpSendHttpResponse( RequestContext.Server.RequestQueue.Handle, Request.RequestId, - flags, + (uint)flags, pResponse, &cachePolicy, &bytesSent, @@ -338,14 +337,14 @@ internal unsafe uint SendHeaders(ref UnmanagedBufferAllocator allocator, // GoAway is only supported on later versions. Retry. if (statusCode == ErrorCodes.ERROR_INVALID_PARAMETER - && (flags & PInvoke.HTTP_SEND_RESPONSE_FLAG_GOAWAY) != 0) + && (flags & HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_GOAWAY) != 0) { - flags &= ~PInvoke.HTTP_SEND_RESPONSE_FLAG_GOAWAY; + flags &= ~HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_GOAWAY; statusCode = HttpApi.HttpSendHttpResponse( RequestContext.Server.RequestQueue.Handle, Request.RequestId, - flags, + (uint)flags, pResponse, &cachePolicy, &bytesSent, @@ -374,15 +373,15 @@ internal unsafe uint SendHeaders(ref UnmanagedBufferAllocator allocator, return statusCode; } - internal uint ComputeHeaders(long writeCount, bool endOfRequest = false) + internal HttpApiTypes.HTTP_FLAGS ComputeHeaders(long writeCount, bool endOfRequest = false) { Headers.IsReadOnly = false; // Temporarily unlock - if (StatusCode == StatusCodes.Status401Unauthorized) + if (StatusCode == (ushort)StatusCodes.Status401Unauthorized) { AuthenticationManager.SetAuthenticationChallenge(RequestContext); } - var flags = 0u; + var flags = HttpApiTypes.HTTP_FLAGS.NONE; Debug.Assert(!HasComputedHeaders, nameof(HasComputedHeaders) + " is true."); _responseState = ResponseState.ComputedHeaders; @@ -460,10 +459,10 @@ internal uint ComputeHeaders(long writeCount, bool endOfRequest = false) { Headers.Append(HeaderNames.Connection, Constants.Close); } - flags = PInvoke.HTTP_SEND_RESPONSE_FLAG_DISCONNECT; + flags = HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT; if (responseCloseSet && requestVersion >= Constants.V2 && SupportsGoAway) { - flags |= PInvoke.HTTP_SEND_RESPONSE_FLAG_GOAWAY; + flags |= HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_GOAWAY; } } @@ -479,113 +478,117 @@ 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; - var numUnknownHeaders = 0; - var numKnownMultiHeaders = 0; - foreach (var (headerName, headerValues) in Headers) + int numUnknownHeaders = 0; + int numKnownMultiHeaders = 0; + foreach (var headerPair in Headers) { - if (headerValues.Count == 0) + if (headerPair.Value.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 (!HttpApiTypes.KnownResponseHeaders.TryGetValue(headerName, out lookup) || - (isOpaqueUpgrade && lookup == (int)HTTP_HEADER_ID.HttpHeaderConnection)) + if (lookup == -1 || + (isOpaqueUpgrade && lookup == (int)HttpApiTypes.HTTP_RESPONSE_HEADER_ID.Enum.HttpHeaderConnection)) { - numUnknownHeaders += headerValues.Count; + numUnknownHeaders += headerPair.Value.Count; } - else if (headerValues.Count > 1) + else if (headerPair.Value.Count > 1) { numKnownMultiHeaders++; } // else known single-value header. } - fixed (__HTTP_KNOWN_HEADER_30* pKnownHeaders = &_nativeResponse.Base.Headers.KnownHeaders) + fixed (HttpApiTypes.HTTP_KNOWN_HEADER* pKnownHeaders = &_nativeResponse.Response_V1.Headers.KnownHeaders) { - var knownHeaders = pKnownHeaders->AsSpan(); - foreach (var (headerName, headerValues) in Headers) + foreach (var headerPair in Headers) { - if (headerValues.Count == 0) + if (headerPair.Value.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 (!HttpApiTypes.KnownResponseHeaders.TryGetValue(headerName, out lookup) || - (isOpaqueUpgrade && lookup == (int)HTTP_HEADER_ID.HttpHeaderConnection)) + if (lookup == -1 || + (isOpaqueUpgrade && lookup == (int)HttpApiTypes.HTTP_RESPONSE_HEADER_ID.Enum.HttpHeaderConnection)) { if (unknownHeaders.Length == 0) { - var unknownAlloc = allocator.AllocAsPointer(numUnknownHeaders); - unknownHeaders = new Span(unknownAlloc, numUnknownHeaders); - _nativeResponse.Base.Headers.pUnknownHeaders = unknownAlloc; + HttpApiTypes.HTTP_UNKNOWN_HEADER* unknownAlloc = allocator.AllocAsPointer(numUnknownHeaders); + unknownHeaders = new Span(unknownAlloc, numUnknownHeaders); + _nativeResponse.Response_V1.Headers.pUnknownHeaders = unknownAlloc; } - for (var headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++) + for (int headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++) { // Add Name bytes = allocator.GetHeaderEncodedBytes(headerName, out bytesLength); - unknownHeaders[_nativeResponse.Base.Headers.UnknownHeaderCount].NameLength = checked((ushort)bytesLength); - unknownHeaders[_nativeResponse.Base.Headers.UnknownHeaderCount].pName = (PCSTR)bytes; + unknownHeaders[_nativeResponse.Response_V1.Headers.UnknownHeaderCount].NameLength = checked((ushort)bytesLength); + unknownHeaders[_nativeResponse.Response_V1.Headers.UnknownHeaderCount].pName = bytes; // Add Value headerValue = headerValues[headerValueIndex] ?? string.Empty; bytes = allocator.GetHeaderEncodedBytes(headerValue, out bytesLength); - unknownHeaders[_nativeResponse.Base.Headers.UnknownHeaderCount].RawValueLength = checked((ushort)bytesLength); - unknownHeaders[_nativeResponse.Base.Headers.UnknownHeaderCount].pRawValue = (PCSTR)bytes; - _nativeResponse.Base.Headers.UnknownHeaderCount++; + unknownHeaders[_nativeResponse.Response_V1.Headers.UnknownHeaderCount].RawValueLength = checked((ushort)bytesLength); + unknownHeaders[_nativeResponse.Response_V1.Headers.UnknownHeaderCount].pRawValue = bytes; + _nativeResponse.Response_V1.Headers.UnknownHeaderCount++; } } - else if (headerValues.Count == 1) + else if (headerPair.Value.Count == 1) { headerValue = headerValues[0] ?? string.Empty; bytes = allocator.GetHeaderEncodedBytes(headerValue, out bytesLength); - knownHeaders[lookup].RawValueLength = checked((ushort)bytesLength); - knownHeaders[lookup].pRawValue = (PCSTR)bytes; + pKnownHeaders[lookup].RawValueLength = checked((ushort)bytesLength); + pKnownHeaders[lookup].pRawValue = bytes; } else { if (knownHeaderInfo.Length == 0) { - var responseAlloc = allocator.AllocAsPointer(numKnownMultiHeaders); - knownHeaderInfo = new Span(responseAlloc, numKnownMultiHeaders); + HttpApiTypes.HTTP_RESPONSE_INFO* responseAlloc = allocator.AllocAsPointer(numKnownMultiHeaders); + knownHeaderInfo = new Span(responseAlloc, numKnownMultiHeaders); _nativeResponse.pResponseInfo = responseAlloc; } - knownHeaderInfo[_nativeResponse.ResponseInfoCount].Type = HTTP_RESPONSE_INFO_TYPE.HttpResponseInfoTypeMultipleKnownHeaders; - knownHeaderInfo[_nativeResponse.ResponseInfoCount].Length = (uint)sizeof(HTTP_MULTIPLE_KNOWN_HEADERS); + knownHeaderInfo[_nativeResponse.ResponseInfoCount].Type = HttpApiTypes.HTTP_RESPONSE_INFO_TYPE.HttpResponseInfoTypeMultipleKnownHeaders; + knownHeaderInfo[_nativeResponse.ResponseInfoCount].Length = (uint)sizeof(HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS); - var header = allocator.AllocAsPointer(1); + HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS* header = allocator.AllocAsPointer(1); - 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->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->KnownHeaderCount = 0; - var headerAlloc = allocator.AllocAsPointer(headerValues.Count); - var nativeHeaderValues = new Span(headerAlloc, headerValues.Count); + HttpApiTypes.HTTP_KNOWN_HEADER* headerAlloc = allocator.AllocAsPointer(headerValues.Count); + var nativeHeaderValues = new Span(headerAlloc, headerValues.Count); header->KnownHeaders = headerAlloc; - for (var headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++) + for (int 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 = (PCSTR)bytes; + nativeHeaderValues[header->KnownHeaderCount].pRawValue = bytes; header->KnownHeaderCount++; } @@ -597,7 +600,7 @@ private unsafe void SerializeHeaders(ref UnmanagedBufferAllocator allocator, boo } } - internal unsafe void SerializeTrailers(ref UnmanagedBufferAllocator allocator, out HTTP_DATA_CHUNK dataChunk) + internal unsafe void SerializeTrailers(ref UnmanagedBufferAllocator allocator, out HttpApiTypes.HTTP_DATA_CHUNK dataChunk) { Debug.Assert(HasTrailers); MakeTrailersReadOnly(); @@ -608,14 +611,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 = HTTP_DATA_CHUNK_TYPE.HttpDataChunkTrailers; - dataChunk.Anonymous.Trailers.TrailerCount = checked((ushort)trailerCount); - dataChunk.Anonymous.Trailers.pTrailers = unknownHeaders; + dataChunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkTrailers; + dataChunk.trailers.trailerCount = checked((ushort)trailerCount); + dataChunk.trailers.pTrailers = (nint)unknownHeaders; var unknownHeadersOffset = 0; @@ -629,18 +632,18 @@ internal unsafe void SerializeTrailers(ref UnmanagedBufferAllocator allocator, o var headerName = headerPair.Key; var headerValues = headerPair.Value; - for (var headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++) + for (int headerValueIndex = 0; headerValueIndex < headerValues.Count; headerValueIndex++) { // Add Name - var bytes = allocator.GetHeaderEncodedBytes(headerName, out var bytesLength); + var bytes = allocator.GetHeaderEncodedBytes(headerName, out int bytesLength); unknownHeaders[unknownHeadersOffset].NameLength = checked((ushort)bytesLength); - unknownHeaders[unknownHeadersOffset].pName = (PCSTR)bytes; + unknownHeaders[unknownHeadersOffset].pName = bytes; // Add Value var headerValue = headerValues[headerValueIndex] ?? string.Empty; bytes = allocator.GetHeaderEncodedBytes(headerValue, out bytesLength); unknownHeaders[unknownHeadersOffset].RawValueLength = checked((ushort)bytesLength); - unknownHeaders[unknownHeadersOffset].pRawValue = (PCSTR)bytes; + unknownHeaders[unknownHeadersOffset].pRawValue = bytes; unknownHeadersOffset++; } } @@ -658,9 +661,9 @@ internal void SendOpaqueUpgrade() { // TODO: Send headers async? ulong errorCode = SendHeaders(ref allocator, null, null, - PInvoke.HTTP_SEND_RESPONSE_FLAG_OPAQUE | - PInvoke.HTTP_SEND_RESPONSE_FLAG_MORE_DATA | - PInvoke.HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA, + 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, 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 8bb2f555e900..b848318e6b5d 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 Windows.Win32; -using Windows.Win32.Networking.HttpServer; +using static Microsoft.AspNetCore.HttpSys.Internal.UnsafeNclNativeMethods; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -144,21 +144,28 @@ public void MarkDelegated() Span pinnedBuffers = default; try { - BuildDataChunks(ref allocator, endOfRequest, data, out var dataChunks, out pinnedBuffers); + Span dataChunks; + BuildDataChunks(ref allocator, endOfRequest, data, out dataChunks, out pinnedBuffers); if (!started) { statusCode = _requestContext.Response.SendHeaders(ref allocator, dataChunks, null, flags, false); } else { - statusCode = PInvoke.HttpSendResponseEntityBody( - RequestQueueHandle, - RequestId, - flags, - dataChunks, - null, - null, - null); + 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); + } } } finally @@ -187,7 +194,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; @@ -199,7 +206,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; @@ -237,7 +244,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) { @@ -273,7 +280,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) @@ -283,17 +290,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 = HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; + chunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; fixed (byte* ptr = bytes) { - chunk.Anonymous.FromMemory.pBuffer = ptr; + chunk.fromMemory.pBuffer = (IntPtr)ptr; } - chunk.Anonymous.FromMemory.BufferLength = (uint)bytes.Length; + chunk.fromMemory.BufferLength = (uint)bytes.Length; } private static void FreeDataBuffers(Span pinnedBuffers) @@ -357,7 +364,7 @@ private unsafe Task FlushInternalAsync(ArraySegment data, CancellationToke statusCode = HttpApi.HttpSendResponseEntityBody( RequestQueueHandle, RequestId, - flags, + (uint)flags, asyncResult.DataChunkCount, asyncResult.DataChunks, &bytesSent, @@ -409,7 +416,7 @@ private unsafe Task FlushInternalAsync(ArraySegment data, CancellationToke } // Last write, cache it for special cancellation handling. - if ((flags & PInvoke.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0) + if ((flags & HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0) { _lastWrite = asyncResult; } @@ -459,9 +466,9 @@ internal void Abort(bool dispose = true) _requestContext.Abort(); } - private uint ComputeLeftToWrite(long writeCount, bool endOfRequest = false) + private HttpApiTypes.HTTP_FLAGS ComputeLeftToWrite(long writeCount, bool endOfRequest = false) { - var flags = 0u; + var flags = HttpApiTypes.HTTP_FLAGS.NONE; if (!_requestContext.Response.HasComputedHeaders) { flags = _requestContext.Response.ComputeHeaders(writeCount, endOfRequest); @@ -484,19 +491,19 @@ private uint ComputeLeftToWrite(long writeCount, bool endOfRequest = false) if (endOfRequest && _requestContext.Response.BoundaryType == BoundaryType.Close) { - flags |= PInvoke.HTTP_SEND_RESPONSE_FLAG_DISCONNECT; + flags |= HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT; } else if (!endOfRequest && (_leftToWrite != writeCount || _requestContext.Response.TrailersExpected)) { - flags |= PInvoke.HTTP_SEND_RESPONSE_FLAG_MORE_DATA; + flags |= HttpApiTypes.HTTP_FLAGS.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 |= PInvoke.HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA; + flags |= HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA; } // Update _leftToWrite now so we can queue up additional async writes. @@ -680,7 +687,7 @@ internal unsafe Task SendFileAsyncCore(string fileName, long offset, long? count statusCode = HttpApi.HttpSendResponseEntityBody( RequestQueueHandle, RequestId, - flags, + (uint)flags, asyncResult.DataChunkCount, asyncResult.DataChunks, &bytesSent, @@ -702,7 +709,7 @@ internal unsafe Task SendFileAsyncCore(string fileName, long offset, long? count allocator.Dispose(); } - if (statusCode != ErrorCodes.ERROR_SUCCESS && statusCode != ErrorCodes.ERROR_IO_PENDING) + if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING) { if (cancellationToken.IsCancellationRequested) { @@ -732,7 +739,7 @@ internal unsafe Task SendFileAsyncCore(string fileName, long offset, long? count } // Last write, cache it for special cancellation handling. - if ((flags & PInvoke.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0) + if ((flags & HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0) { _lastWrite = asyncResult; } @@ -772,7 +779,7 @@ internal unsafe void CancelLastWrite() ResponseStreamAsyncResult? asyncState = _lastWrite; if (asyncState != null && !asyncState.IsCompleted) { - HttpApi.CancelIoEx(RequestQueueHandle, asyncState.NativeOverlapped!); + UnsafeNclNativeMethods.CancelIoEx(RequestQueueHandle, asyncState.NativeOverlapped!); } } diff --git a/src/Servers/HttpSys/src/RequestProcessing/ResponseStreamAsyncResult.cs b/src/Servers/HttpSys/src/RequestProcessing/ResponseStreamAsyncResult.cs index be44c6595059..34f044349a00 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/ResponseStreamAsyncResult.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/ResponseStreamAsyncResult.cs @@ -3,8 +3,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; -using Windows.Win32.Foundation; -using Windows.Win32.Networking.HttpServer; +using Microsoft.AspNetCore.HttpSys.Internal; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -13,7 +12,7 @@ internal sealed unsafe partial class ResponseStreamAsyncResult : IAsyncResult, I private static readonly IOCompletionCallback IOCallback = new IOCompletionCallback(Callback); private readonly SafeNativeOverlapped? _overlapped; - private readonly HTTP_DATA_CHUNK[]? _dataChunks; + private readonly HttpApiTypes.HTTP_DATA_CHUNK[]? _dataChunks; private readonly FileStream? _fileStream; private readonly ResponseBody _responseStream; private readonly TaskCompletionSource _tcs; @@ -50,7 +49,7 @@ internal ResponseStreamAsyncResult(ResponseBody responseStream, ArraySegment segment) + private static void SetDataChunk(HttpApiTypes.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 = HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; + chunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; // The address is not set until after we pin it with Overlapped - chunk.Anonymous.FromMemory.BufferLength = (uint)segment.Count; + chunk.fromMemory.BufferLength = (uint)segment.Count; } - private static void SetDataChunkWithPinnedData(HTTP_DATA_CHUNK[] chunks, ref int chunkIndex, ReadOnlySpan bytes) + private static void SetDataChunkWithPinnedData(HttpApiTypes.HTTP_DATA_CHUNK[] chunks, ref int chunkIndex, ReadOnlySpan bytes) { ref var chunk = ref chunks[chunkIndex++]; - chunk.DataChunkType = HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; + chunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; fixed (byte* ptr = bytes) { - chunk.Anonymous.FromMemory.pBuffer = ptr; + chunk.fromMemory.pBuffer = (IntPtr)ptr; } - chunk.Anonymous.FromMemory.BufferLength = (uint)bytes.Length; + chunk.fromMemory.BufferLength = (uint)bytes.Length; } internal SafeNativeOverlapped? NativeOverlapped @@ -206,7 +205,7 @@ internal ushort DataChunkCount } } - internal HTTP_DATA_CHUNK* DataChunks + internal HttpApiTypes.HTTP_DATA_CHUNK* DataChunks { get { @@ -216,7 +215,7 @@ internal HTTP_DATA_CHUNK* DataChunks } else { - return (HTTP_DATA_CHUNK*)(Marshal.UnsafeAddrOfPinnedArrayElement(_dataChunks, 0)); + return (HttpApiTypes.HTTP_DATA_CHUNK*)(Marshal.UnsafeAddrOfPinnedArrayElement(_dataChunks, 0)); } } } @@ -233,7 +232,7 @@ private static void IOCompleted(ResponseStreamAsyncResult asyncResult, uint erro var logger = asyncResult._responseStream.RequestContext.Logger; try { - if (errorCode != ErrorCodes.ERROR_SUCCESS && errorCode != ErrorCodes.ERROR_HANDLE_EOF) + if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && errorCode != UnsafeNclNativeMethods.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 457577e2cf8f..63fe93d1786c 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 Windows.Win32.Networking.HttpServer; +using Microsoft.AspNetCore.HttpSys.Internal; 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(TimeoutType.EntityBody); + return GetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.EntityBody); } set { - SetTimeSpanTimeout(TimeoutType.EntityBody, value); + SetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.EntityBody, value); } } @@ -71,11 +71,11 @@ public TimeSpan DrainEntityBody { get { - return GetTimeSpanTimeout(TimeoutType.DrainEntityBody); + return GetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.DrainEntityBody); } set { - SetTimeSpanTimeout(TimeoutType.DrainEntityBody, value); + SetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.DrainEntityBody, value); } } @@ -89,11 +89,11 @@ public TimeSpan RequestQueue { get { - return GetTimeSpanTimeout(TimeoutType.RequestQueue); + return GetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.RequestQueue); } set { - SetTimeSpanTimeout(TimeoutType.RequestQueue, value); + SetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.RequestQueue, value); } } @@ -108,11 +108,11 @@ public TimeSpan IdleConnection { get { - return GetTimeSpanTimeout(TimeoutType.IdleConnection); + return GetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.IdleConnection); } set { - SetTimeSpanTimeout(TimeoutType.IdleConnection, value); + SetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.IdleConnection, value); } } @@ -128,11 +128,11 @@ public TimeSpan HeaderWait { get { - return GetTimeSpanTimeout(TimeoutType.HeaderWait); + return GetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.HeaderWait); } set { - SetTimeSpanTimeout(TimeoutType.HeaderWait, value); + SetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE.HeaderWait, value); } } @@ -168,13 +168,13 @@ public long MinSendBytesPerSecond #region Helpers - private TimeSpan GetTimeSpanTimeout(TimeoutType type) + private TimeSpan GetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE type) { // Since we maintain local state, GET is local. return new TimeSpan(0, 0, (int)_timeouts[(int)type]); } - private void SetTimeSpanTimeout(TimeoutType type, TimeSpan value) + private void SetTimeSpanTimeout(HttpApiTypes.HTTP_TIMEOUT_TYPE 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,33 +208,27 @@ private unsafe void SetUrlGroupTimeouts(int[] timeouts, uint minSendBytesPerSeco return; } - 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 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 infoptr = new IntPtr(&timeoutinfo); _urlGroup.SetProperty( - HTTP_SERVER_PROPERTY.HttpServerTimeoutsProperty, + HttpApiTypes.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 20cd74a55b10..217a2fa49fd7 100644 --- a/src/Servers/HttpSys/src/UrlPrefixCollection.cs +++ b/src/Servers/HttpSys/src/UrlPrefixCollection.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.HttpSys.Internal; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -232,9 +233,9 @@ private void FindHttpPortUnsynchronized(int key, UrlPrefix urlPrefix) } catch (HttpSysException ex) { - if ((ex.ErrorCode != ErrorCodes.ERROR_ACCESS_DENIED - && ex.ErrorCode != ErrorCodes.ERROR_SHARING_VIOLATION - && ex.ErrorCode != ErrorCodes.ERROR_ALREADY_EXISTS) || index == MaxRetries - 1) + 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) { throw; } diff --git a/src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs b/src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs index ec3be7a25f6e..3329d3ac41dc 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs @@ -1,18 +1,21 @@ // 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; @@ -204,20 +207,20 @@ public async Task Https_ITlsHandshakeFeature_MatchesIHttpSysExtensionInfoFeature Assert.NotNull(tlsFeature); Assert.NotNull(requestInfoFeature); Assert.True(requestInfoFeature.RequestInfo.Count > 0); - var tlsInfo = requestInfoFeature.RequestInfo[(int)HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeSslProtocol]; - HTTP_SSL_PROTOCOL_INFO tlsCopy; + var tlsInfo = requestInfoFeature.RequestInfo[(int)HttpApiTypes.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeSslProtocol]; + HttpApiTypes.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((uint)tlsFeature.CipherAlgorithm, tlsCopy.CipherType); + Assert.Equal(tlsFeature.CipherAlgorithm, tlsCopy.CipherType); Assert.Equal(tlsFeature.CipherStrength, (int)tlsCopy.CipherStrength); - Assert.Equal((uint)tlsFeature.HashAlgorithm, tlsCopy.HashType); + Assert.Equal(tlsFeature.HashAlgorithm, tlsCopy.HashType); Assert.Equal(tlsFeature.HashStrength, (int)tlsCopy.HashStrength); - Assert.Equal((uint)tlsFeature.KeyExchangeAlgorithm, tlsCopy.KeyExchangeType); + Assert.Equal(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 d75766ede7b4..ffdbeff1ad58 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/Listener/ServerOnExistingQueueTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/Listener/ServerOnExistingQueueTests.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.AspNetCore.InternalTesting; -using Windows.Win32; using Xunit; namespace Microsoft.AspNetCore.Server.HttpSys.Listener; @@ -240,14 +239,15 @@ public async Task Server_CreateOrAttach_NoUrlPrefix_NewUrlPrefixWorks() var queueName = Guid.NewGuid().ToString(); // Create a queue without a UrlGroup or any UrlPrefixes - var statusCode = PInvoke.HttpCreateRequestQueue( + HttpRequestQueueV2Handle requestQueueHandle = null; + var statusCode = HttpApi.HttpCreateRequestQueue( HttpApi.Version, queueName, - default, + IntPtr.Zero, 0, - out var requestQueueHandle); + out requestQueueHandle); - Assert.True(statusCode == ErrorCodes.ERROR_SUCCESS); + Assert.True(statusCode == UnsafeNclNativeMethods.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 8a7a9d4da76e..b61b00ac2c6e 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)ErrorCodes.ERROR_ALREADY_EXISTS, exception.ErrorCode); + Assert.Equal((int)UnsafeNclNativeMethods.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 409faaa5b13d..b7ca7e33df85 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 != ErrorCodes.ERROR_ALREADY_EXISTS - && ex.ErrorCode != ErrorCodes.ERROR_SHARING_VIOLATION - && ex.ErrorCode != ErrorCodes.ERROR_ACCESS_DENIED) + if (ex.ErrorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_ALREADY_EXISTS + && ex.ErrorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SHARING_VIOLATION + && ex.ErrorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_ACCESS_DENIED) { throw; } diff --git a/src/Servers/HttpSys/test/FunctionalTests/ResponseCachingTests.cs b/src/Servers/HttpSys/test/FunctionalTests/ResponseCachingTests.cs index 7d043d273fec..334130885725 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/ResponseCachingTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/ResponseCachingTests.cs @@ -1,29 +1,25 @@ // 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.Globalization; -using System.IO; -using System.Linq; using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.InternalTesting; -using Microsoft.Extensions.Logging; -using Xunit; namespace Microsoft.AspNetCore.Server.HttpSys.FunctionalTests; public class ResponseCachingTests : LoggedTest { - private readonly string _absoluteFilePath; - private readonly long _fileLength; + private static readonly string _absoluteFilePath; + private static readonly long _fileLength; - public ResponseCachingTests() + static ResponseCachingTests() { - _absoluteFilePath = Path.Combine(Directory.GetCurrentDirectory(), "Microsoft.AspNetCore.Server.HttpSys.dll"); - _fileLength = new FileInfo(_absoluteFilePath).Length; + _absoluteFilePath = Path.Combine(Directory.GetCurrentDirectory(), "HttpSysTestsCachePayload.bin"); + var arr = new byte[150_000]; // http.sys has default max cached payload size of 262144 bytes + Random.Shared.NextBytes(arr); + File.WriteAllBytes(_absoluteFilePath, arr); + _fileLength = arr.Length; } [ConditionalFact] diff --git a/src/Servers/HttpSys/test/FunctionalTests/ServerTests.cs b/src/Servers/HttpSys/test/FunctionalTests/ServerTests.cs index b9ebff05f721..eaae45c3ea62 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/ServerTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/ServerTests.cs @@ -16,7 +16,6 @@ using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.AspNetCore.InternalTesting; using Microsoft.Extensions.Options; -using Windows.Win32; using Xunit; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -26,7 +25,8 @@ public class ServerTests : LoggedTest [ConditionalFact] public async Task Server_200OK_Success() { - using (Utilities.CreateHttpServer(out var address, httpContext => + string address; + using (Utilities.CreateHttpServer(out address, httpContext => { return Task.FromResult(0); }, LoggerFactory)) @@ -41,20 +41,22 @@ 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. - var statusCode = PInvoke.HttpCreateRequestQueue( + HttpRequestQueueV2Handle requestQueueHandle = null; + var statusCode = HttpApi.HttpCreateRequestQueue( HttpApi.Version, queueName, - default, + IntPtr.Zero, 0, - out var requestQueueHandle); + out requestQueueHandle); - Assert.True(statusCode == ErrorCodes.ERROR_SUCCESS); + Assert.True(statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS); // Now attach to the existing one - using (Utilities.CreateHttpServer(out var address, httpContext => + using (Utilities.CreateHttpServer(out address, httpContext => { return Task.FromResult(0); }, options => @@ -91,8 +93,9 @@ 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 var address, httpContext => + using (Utilities.CreateHttpServer(out address, httpContext => { return Task.FromResult(0); }, options => @@ -114,7 +117,8 @@ public async Task Server_SetQueueName_Success() [ConditionalFact] public async Task Server_SendHelloWorld_Success() { - using (Utilities.CreateHttpServer(out var address, httpContext => + string address; + using (Utilities.CreateHttpServer(out address, httpContext => { httpContext.Response.ContentLength = 11; return httpContext.Response.WriteAsync("Hello World"); @@ -128,7 +132,8 @@ public async Task Server_SendHelloWorld_Success() [ConditionalFact] public async Task Server_EchoHelloWorld_Success() { - using (Utilities.CreateHttpServer(out var address, async httpContext => + string address; + using (Utilities.CreateHttpServer(out address, async httpContext => { var input = await new StreamReader(httpContext.Request.Body).ReadToEndAsync(); Assert.Equal("Hello World", input); @@ -207,7 +212,8 @@ public async Task Server_ShutdownDuringLongRunningRequest_TimesOut() [ConditionalFact] public async Task Server_AppException_ClientReset() { - using (Utilities.CreateHttpServer(out var address, httpContext => + string address; + using (Utilities.CreateHttpServer(out address, httpContext => { throw new InvalidOperationException(); }, LoggerFactory)) @@ -226,7 +232,8 @@ public async Task Server_AppException_ClientReset() [ConditionalFact] public async Task Server_BadHttpRequestException_SetStatusCode() { - using (Utilities.CreateHttpServer(out var address, httpContext => + string address; + using (Utilities.CreateHttpServer(out address, httpContext => { throw new BadHttpRequestException("Something happened", StatusCodes.Status418ImATeapot); }, LoggerFactory)) @@ -249,7 +256,8 @@ public void Server_MultipleOutstandingAsyncRequests_Success() int requestCount = 0; TaskCompletionSource tcs = new TaskCompletionSource(); - using (Utilities.CreateHttpServer(out var address, async httpContext => + string address; + using (Utilities.CreateHttpServer(out 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 84d963923d68..ca447f4c20b7 100644 --- a/src/Servers/HttpSys/test/Tests/RequestHeaderTests.cs +++ b/src/Servers/HttpSys/test/Tests/RequestHeaderTests.cs @@ -6,8 +6,6 @@ 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; @@ -22,7 +20,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 HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); SetHostAndContentTypeHeaders(nativeMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -43,11 +41,11 @@ public unsafe void RequestHeaders_PinnedMemory_ReturnsKnownHeadersKeys() using var handle = buffer.Pin(); Span nativeMemory = buffer.Span; - var requestStructure = new HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); SetHostAndContentTypeHeaders(nativeMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); - var nativeContext = new NativeRequestContext((HTTP_REQUEST_V1*)handle.Pointer, false); + var nativeContext = new NativeRequestContext((HttpApiTypes.HTTP_REQUEST*)handle.Pointer, false); var requestHeaders = new RequestHeaders(nativeContext); Assert.Equal(2, requestHeaders.Count); @@ -63,7 +61,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 HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); SetUnknownHeaders(nativeMemory, ref requestStructure, new[] { (CustomHeader1, "1"), (CustomHeader2, null) }); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -84,11 +82,11 @@ public unsafe void RequestHeaders_PinnedMemory_ReturnsUnknownHeadersKeys() using var handle = buffer.Pin(); Span nativeMemory = buffer.Span; - var requestStructure = new HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); SetUnknownHeaders(nativeMemory, ref requestStructure, new[] { (CustomHeader1, "1"), (CustomHeader2, null) }); MemoryMarshal.Write(nativeMemory, in requestStructure); - var nativeContext = new NativeRequestContext((HTTP_REQUEST_V1*)handle.Pointer, false); + var nativeContext = new NativeRequestContext((HttpApiTypes.HTTP_REQUEST*)handle.Pointer, false); var requestHeaders = new RequestHeaders(nativeContext); Assert.Equal(2, requestHeaders.Count); @@ -104,7 +102,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 HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); SetInvalidHostHeader(nativeMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -123,7 +121,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 HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); SetInvalidUnknownHeaders(nativeMemory, ref requestStructure, new[] { CustomHeader1 }); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -142,7 +140,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 HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); var remainingMemory = SetUnknownHeaders(nativeMemory, ref requestStructure, new[] { (CustomHeader1, "1"), (CustomHeader2, null) }); SetHostAndContentTypeHeaders(remainingMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -166,12 +164,12 @@ public unsafe void RequestHeaders_PinnedMemory_ReturnsKnownAndUnKnownHeadersKeys using var handle = buffer.Pin(); Span nativeMemory = buffer.Span; - var requestStructure = new HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); var remainingMemory = SetUnknownHeaders(nativeMemory, ref requestStructure, new[] { (CustomHeader1, "1"), (CustomHeader2, null) }); SetHostAndContentTypeHeaders(remainingMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); - var nativeContext = new NativeRequestContext((HTTP_REQUEST_V1*)handle.Pointer, false); + var nativeContext = new NativeRequestContext((HttpApiTypes.HTTP_REQUEST*)handle.Pointer, false); var requestHeaders = new RequestHeaders(nativeContext); Assert.Equal(4, requestHeaders.Count); @@ -189,7 +187,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 HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); SetUnknownHeaders(nativeMemory, ref requestStructure, new[] { (CustomHeader1, "1"), (CustomHeader2, null) }); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -211,7 +209,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 HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); SetUnknownHeaders(nativeMemory, ref requestStructure, new[] { (CustomHeader1, "1") }); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -233,7 +231,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 HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); SetUnknownHeaders(nativeMemory, ref requestStructure, new[] { (CustomHeader1, "1"), (CustomHeader2, null) }); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -254,7 +252,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 HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); SetUnknownHeaders(nativeMemory, ref requestStructure, new[] { (CustomHeader1, "1") }); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -274,7 +272,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 HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); SetHostAndContentTypeHeaders(nativeMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -296,7 +294,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 HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); SetHostAndContentTypeHeaders(nativeMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -318,7 +316,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 HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); SetHostAndContentTypeHeaders(nativeMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -339,7 +337,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 HTTP_REQUEST_V1(); + var requestStructure = new HttpApiTypes.HTTP_REQUEST(); SetHostAndContentTypeHeaders(nativeMemory, ref requestStructure); MemoryMarshal.Write(nativeMemory, in requestStructure); @@ -353,15 +351,15 @@ public unsafe void RequestHeaders_AddKnownHeader_AddsKey() Assert.Contains(HeaderNames.From, requestHeaders.Keys); } - private static unsafe Span SetHostAndContentTypeHeaders(Span nativeMemory, ref HTTP_REQUEST_V1 requestStructure) + private static unsafe Span SetHostAndContentTypeHeaders(Span nativeMemory, ref HttpApiTypes.HTTP_REQUEST 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._28.pRawValue = (PCSTR)address; - requestStructure.Headers.KnownHeaders._28.RawValueLength = (ushort)length; + requestStructure.Headers.KnownHeaders_29.pRawValue = address; + requestStructure.Headers.KnownHeaders_29.RawValueLength = (ushort)length; } // Writing application/json to Content-Type header @@ -369,25 +367,25 @@ private static unsafe Span SetHostAndContentTypeHeaders(Span nativeM length = Encoding.ASCII.GetBytes("application/json", dataDestination); fixed (byte* address = &MemoryMarshal.GetReference(dataDestination)) { - requestStructure.Headers.KnownHeaders._12.pRawValue = (PCSTR)address; - requestStructure.Headers.KnownHeaders._12.RawValueLength = (ushort)length; + requestStructure.Headers.KnownHeaders_13.pRawValue = address; + requestStructure.Headers.KnownHeaders_13.RawValueLength = (ushort)length; } dataDestination = dataDestination.Slice(length); return dataDestination; } - private static unsafe Span SetInvalidHostHeader(Span nativeMemory, ref HTTP_REQUEST_V1 requestStructure) + private static unsafe Span SetInvalidHostHeader(Span nativeMemory, ref HttpApiTypes.HTTP_REQUEST 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._28.pRawValue = (PCSTR)address; + requestStructure.Headers.KnownHeaders_29.pRawValue = address; // Set length to zero, to make it invalid. - requestStructure.Headers.KnownHeaders._28.RawValueLength = 0; + requestStructure.Headers.KnownHeaders_29.RawValueLength = 0; } dataDestination = dataDestination.Slice(length); @@ -397,24 +395,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 HTTP_REQUEST_V1 requestStructure, IReadOnlyCollection<(string Key, string Value)> headerNames) + private static unsafe Span SetUnknownHeaders(Span nativeMemory, ref HttpApiTypes.HTTP_REQUEST 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 = (HTTP_UNKNOWN_HEADER*)address; + requestStructure.Headers.pUnknownHeaders = (HttpApiTypes.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 HTTP_UNKNOWN_HEADER(); + var unknownHeaderStructure = new HttpApiTypes.HTTP_UNKNOWN_HEADER(); int nameLength = Encoding.ASCII.GetBytes(headerName.Key, dataDestination); fixed (byte* address = &MemoryMarshal.GetReference(dataDestination)) { - unknownHeaderStructure.pName = (PCSTR)address; + unknownHeaderStructure.pName = address; unknownHeaderStructure.NameLength = (ushort)nameLength; } dataDestination = dataDestination.Slice(nameLength); @@ -424,7 +422,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 = (PCSTR)address; + unknownHeaderStructure.pRawValue = address; unknownHeaderStructure.RawValueLength = (ushort)valueLength; } dataDestination = dataDestination.Slice(nameLength); @@ -435,26 +433,26 @@ private static unsafe Span SetUnknownHeaders(Span nativeMemory, ref return dataDestination; } - private static unsafe Span SetInvalidUnknownHeaders(Span nativeMemory, ref HTTP_REQUEST_V1 requestStructure, IReadOnlyCollection headerNames) + private static unsafe Span SetInvalidUnknownHeaders(Span nativeMemory, ref HttpApiTypes.HTTP_REQUEST requestStructure, IReadOnlyCollection headerNames) { - var unknownHeaderStructureDestination = nativeMemory.Slice(Marshal.SizeOf()); + var unknownHeaderStructureDestination = nativeMemory.Slice(Marshal.SizeOf()); fixed (byte* address = &MemoryMarshal.GetReference(unknownHeaderStructureDestination)) { - requestStructure.Headers.pUnknownHeaders = (HTTP_UNKNOWN_HEADER*)address; + requestStructure.Headers.pUnknownHeaders = (HttpApiTypes.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 HTTP_UNKNOWN_HEADER(); + var unknownHeaderStructure = new HttpApiTypes.HTTP_UNKNOWN_HEADER(); int nameLength = Encoding.ASCII.GetBytes(headerName, dataDestination); fixed (byte* address = &MemoryMarshal.GetReference(dataDestination)) { - unknownHeaderStructure.pName = (PCSTR)address; + unknownHeaderStructure.pName = 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 f0e04f3b2a95..5eff74efd87c 100644 --- a/src/Servers/IIS/IIS/src/Core/IISHttpContext.cs +++ b/src/Servers/IIS/IIS/src/Core/IISHttpContext.cs @@ -21,7 +21,6 @@ using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; -using Windows.Win32.Networking.HttpServer; namespace Microsoft.AspNetCore.Server.IIS.Core; @@ -78,7 +77,7 @@ internal unsafe IISHttpContext( IISHttpServer server, ILogger logger, bool useLatin1) - : base((HTTP_REQUEST_V1*)NativeMethods.HttpGetRawRequest(pInProcessHandler), useLatin1: useLatin1) + : base((HttpApiTypes.HTTP_REQUEST*)NativeMethods.HttpGetRawRequest(pInProcessHandler), useLatin1: useLatin1) { _memoryPool = memoryPool; _requestNativeHandle = pInProcessHandler; @@ -90,7 +89,7 @@ internal unsafe IISHttpContext( } private int PauseWriterThreshold => _options.MaxRequestBodyBufferSize; - private int ResumeWriterThreshold => PauseWriterThreshold / 2; + private int ResumeWriterTheshold => PauseWriterThreshold / 2; private bool IsHttps => SslStatus != SslStatus.Insecure; public Version HttpVersion { get; set; } = default!; @@ -134,7 +133,7 @@ internal unsafe IISHttpContext( private HeaderCollection HttpResponseTrailers => _trailers ??= new HeaderCollection(checkTrailers: true); internal bool HasTrailers => _trailers?.Count > 0; - internal HTTP_VERB KnownMethod { get; private set; } + internal HttpApiTypes.HTTP_VERB KnownMethod { get; private set; } private bool HasStartedConsumingRequestBody { get; set; } public long? MaxRequestBodySize { get; set; } @@ -143,7 +142,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 acquires this lock as well + // disconnect aquires this lock as well lock (_abortLock) { _thisHandle = GCHandle.Alloc(this); @@ -164,7 +163,7 @@ protected void InitializeContext() pathBase = pathBase[..^1]; } - if (KnownMethod == HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawTarget, "*", StringComparison.Ordinal)) + if (KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawTarget, "*", StringComparison.Ordinal)) { PathBase = string.Empty; Path = string.Empty; @@ -302,7 +301,7 @@ protected void InitializeContext() // schedules app code when backpressure is relieved which may block. readerScheduler: PipeScheduler.Inline, pauseWriterThreshold: PauseWriterThreshold, - resumeWriterThreshold: ResumeWriterThreshold, + resumeWriterThreshold: ResumeWriterTheshold, minimumSegmentSize: MinAllocBufferSize)); _bodyOutput = new OutputProducer(pipe); } @@ -393,27 +392,27 @@ private bool CheckRequestCanHaveBody() private void GetTlsHandshakeResults() { - var handshake = GetTlsHandshake(); - Protocol = (SslProtocols)handshake.Protocol; - CipherAlgorithm = (CipherAlgorithmType)handshake.CipherType; + var handshake = this.GetTlsHandshake(); + Protocol = handshake.Protocol; + CipherAlgorithm = handshake.CipherType; CipherStrength = (int)handshake.CipherStrength; - HashAlgorithm = (HashAlgorithmType)handshake.HashType; + HashAlgorithm = handshake.HashType; HashStrength = (int)handshake.HashStrength; - KeyExchangeAlgorithm = (ExchangeAlgorithmType)handshake.KeyExchangeType; + KeyExchangeAlgorithm = handshake.KeyExchangeType; KeyExchangeStrength = (int)handshake.KeyExchangeStrength; var sni = GetClientSni(); - SniHostName = sni.Hostname.ToString(); + SniHostName = sni.Hostname; } - private unsafe HTTP_REQUEST_PROPERTY_SNI GetClientSni() + private unsafe HttpApiTypes.HTTP_REQUEST_PROPERTY_SNI GetClientSni() { var buffer = new byte[HttpApiTypes.SniPropertySizeInBytes]; fixed (byte* pBuffer = buffer) { var statusCode = NativeMethods.HttpQueryRequestProperty( RequestId, - HTTP_REQUEST_PROPERTY.HttpRequestPropertySni, + HttpApiTypes.HTTP_REQUEST_PROPERTY.HttpRequestPropertySni, qualifier: null, qualifierSize: 0, (void*)pBuffer, @@ -421,7 +420,7 @@ private unsafe 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; } } @@ -591,7 +590,7 @@ public unsafe void SetResponseHeaders() continue; } - var isKnownHeader = HttpApiTypes.KnownResponseHeaders.TryGetValue(headerPair.Key, out var knownHeaderIndex); + var knownHeaderIndex = HttpApiTypes.HTTP_RESPONSE_HEADER_ID.IndexOfKnownHeader(headerPair.Key); for (var i = 0; i < headerValues.Count; i++) { var headerValue = headerValues[i]; @@ -606,7 +605,7 @@ public unsafe void SetResponseHeaders() fixed (byte* pHeaderValue = headerValueBytes) { - if (!isKnownHeader) + if (knownHeaderIndex == -1) { 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 ab84c8bd85fe..c786c2d28c7a 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 Windows.Win32.Networking.HttpServer; +using Microsoft.AspNetCore.HttpSys.Internal; 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, HTTP_DATA_CHUNK* dataChunks, + protected override unsafe int WriteChunks(NativeSafeHandle requestHandler, int chunkCount, HttpApiTypes.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 0636b9b18b13..11916321cb83 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 Windows.Win32.Networking.HttpServer; +using Microsoft.AspNetCore.HttpSys.Internal; 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 HTTP_DATA_CHUNK[chunkCount]; + var chunks = stackalloc HttpApiTypes.HTTP_DATA_CHUNK[chunkCount]; hr = WriteSequence(_requestHandler, chunkCount, _buffer, chunks, out completionExpected); } else { // Otherwise allocate the chunks on the heap. - var chunks = new HTTP_DATA_CHUNK[chunkCount]; - fixed (HTTP_DATA_CHUNK* pDataChunks = chunks) + var chunks = new HttpApiTypes.HTTP_DATA_CHUNK[chunkCount]; + fixed (HttpApiTypes.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, HTTP_DATA_CHUNK* pDataChunks, out bool fCompletionExpected) + private unsafe int WriteSequence(NativeSafeHandle requestHandler, int nChunks, ReadOnlySequence buffer, HttpApiTypes.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 = HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; - chunk.Anonymous.FromMemory.BufferLength = (uint)readOnlyMemory.Length; - chunk.Anonymous.FromMemory.pBuffer = handle.Pointer; + chunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; + chunk.fromMemory.BufferLength = (uint)readOnlyMemory.Length; + chunk.fromMemory.pBuffer = (IntPtr)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, HTTP_DATA_CHUNK* dataChunks, out bool completionExpected); + protected abstract unsafe int WriteChunks(NativeSafeHandle requestHandler, int chunkCount, HttpApiTypes.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 f345b7f4e614..d38c541d5960 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 Windows.Win32.Networking.HttpServer; +using Microsoft.AspNetCore.HttpSys.Internal; 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, HTTP_DATA_CHUNK* dataChunks, out bool completionExpected) + protected override unsafe int WriteChunks(NativeSafeHandle requestHandler, int chunkCount, HttpApiTypes.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 5333537bd8f3..5f18def13f0e 100644 --- a/src/Servers/IIS/IIS/src/Microsoft.AspNetCore.Server.IIS.csproj +++ b/src/Servers/IIS/IIS/src/Microsoft.AspNetCore.Server.IIS.csproj @@ -13,11 +13,10 @@ true - - $(NoWarn);CA1416;CS9195 + $(NoWarn);CA1416 - + @@ -29,12 +28,6 @@ - - - - - - @@ -58,6 +51,7 @@ + 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 6aa3d68ed35e..9d25ce793c03 100644 --- a/src/Servers/IIS/IIS/src/NativeMethods.cs +++ b/src/Servers/IIS/IIS/src/NativeMethods.cs @@ -1,10 +1,9 @@ // 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; @@ -59,13 +58,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, HTTP_DATA_CHUNK* pDataChunks, int nChunks, [MarshalAs(UnmanagedType.Bool)] out bool fCompletionExpected); + private static unsafe partial int http_write_response_bytes(NativeSafeHandle pInProcessHandler, HttpApiTypes.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 HTTP_REQUEST_V2* http_get_raw_request(NativeSafeHandle pInProcessHandler); + private static unsafe partial HttpApiTypes.HTTP_REQUEST_V2* http_get_raw_request(NativeSafeHandle pInProcessHandler); [LibraryImport(AspNetCoreModuleDll)] private static partial int http_stop_calls_into_managed(NativeSafeHandle pInProcessApplication); @@ -92,10 +91,9 @@ 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, - HTTP_REQUEST_PROPERTY propertyId, + HttpApiTypes.HTTP_REQUEST_PROPERTY propertyId, void* qualifier, uint qualifierSize, void* output, @@ -128,7 +126,7 @@ private static unsafe partial int http_websockets_read_bytes( [LibraryImport(AspNetCoreModuleDll)] private static unsafe partial int http_websockets_write_bytes( NativeSafeHandle pInProcessHandler, - HTTP_DATA_CHUNK* pDataChunks, + HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, int nChunks, delegate* unmanaged pfnCompletionCallback, IntPtr pvCompletionContext, @@ -151,7 +149,6 @@ 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); @@ -189,7 +186,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, HTTP_DATA_CHUNK* pDataChunks, int nChunks, out bool fCompletionExpected) + internal static unsafe int HttpWriteResponseBytes(NativeSafeHandle pInProcessHandler, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, int nChunks, out bool fCompletionExpected) { return http_write_response_bytes(pInProcessHandler, pDataChunks, nChunks, out fCompletionExpected); } @@ -199,7 +196,7 @@ public static int HttpFlushResponseBytes(NativeSafeHandle pInProcessHandler, boo return http_flush_response_bytes(pInProcessHandler, fMoreData, out fCompletionExpected); } - internal static unsafe HTTP_REQUEST_V2* HttpGetRawRequest(NativeSafeHandle pInProcessHandler) + internal static unsafe HttpApiTypes.HTTP_REQUEST_V2* HttpGetRawRequest(NativeSafeHandle pInProcessHandler) { return http_get_raw_request(pInProcessHandler); } @@ -245,7 +242,7 @@ internal static IISConfigurationData HttpGetApplicationProperties() return iisConfigurationData; } - public static unsafe int HttpQueryRequestProperty(ulong requestId, HTTP_REQUEST_PROPERTY propertyId, void* qualifier, uint qualifierSize, void* output, uint outputSize, uint* bytesReturned, IntPtr overlapped) + public static unsafe int HttpQueryRequestProperty(ulong requestId, HttpApiTypes.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); } @@ -273,7 +270,7 @@ public static unsafe int HttpWebsocketsReadBytes( internal static unsafe int HttpWebsocketsWriteBytes( NativeSafeHandle pInProcessHandler, - HTTP_DATA_CHUNK* pDataChunks, + HttpApiTypes.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 deleted file mode 100644 index 8d0523a197f3..000000000000 --- a/src/Servers/IIS/IIS/src/NativeMethods.txt +++ /dev/null @@ -1,16 +0,0 @@ -// 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 deleted file mode 100644 index fe50cc40db0d..000000000000 --- a/src/Servers/IIS/IIS/src/SourceBuildStubs.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. - -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 facf7dfd420b..61daae334aed 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Infrastructure/FunctionalTestsBase.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Infrastructure/FunctionalTestsBase.cs @@ -17,7 +17,6 @@ public FunctionalTestsBase(ITestOutputHelper output = null) : base(output) } protected IISDeployerBase _deployer; - protected IISDeploymentResult _deploymentResult; protected ApplicationDeployer CreateDeployer(IISDeploymentParameters parameters) { @@ -33,7 +32,7 @@ protected ApplicationDeployer CreateDeployer(IISDeploymentParameters parameters) protected virtual async Task DeployAsync(IISDeploymentParameters parameters) { _deployer = (IISDeployerBase)CreateDeployer(parameters); - return _deploymentResult = (IISDeploymentResult)await _deployer.DeployAsync(); + return (IISDeploymentResult)await _deployer.DeployAsync(); } protected virtual async Task StartAsync(IISDeploymentParameters parameters) @@ -56,7 +55,6 @@ 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 73074454b0c1..b947fc87fe68 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Infrastructure/IISTestSiteFixture.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Infrastructure/IISTestSiteFixture.cs @@ -65,7 +65,6 @@ 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 8c21d2f9a70a..8d9fb104cde4 100644 --- a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/IISExpressShutdownTests.cs +++ b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/InProcess/IISExpressShutdownTests.cs @@ -36,10 +36,6 @@ public async Task ServerShutsDownWhenMainExits() { // Server might close a connection before request completes } - finally - { - deploymentResult.HttpClient.Dispose(); - } deploymentResult.AssertWorkerProcessStop(); } @@ -66,10 +62,6 @@ 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 c334d7051166..1cf6072b547d 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, IDisposable +public class IISDeploymentResult : DeploymentResult { public ILogger Logger { get; set; } @@ -43,10 +43,5 @@ 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 2a35f095e630..5c7f2cd686ae 100644 --- a/src/Shared/Buffers.MemoryPool/DiagnosticMemoryPool.cs +++ b/src/Shared/Buffers.MemoryPool/DiagnosticMemoryPool.cs @@ -1,7 +1,9 @@ // 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 d2c0dfd18917..8c3ebce812b4 100644 --- a/src/Shared/Buffers.MemoryPool/DiagnosticPoolBlock.cs +++ b/src/Shared/Buffers.MemoryPool/DiagnosticPoolBlock.cs @@ -3,6 +3,7 @@ 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 42fc10c77978..2d5695ef4155 100644 --- a/src/Shared/HttpSys/Constants.cs +++ b/src/Shared/HttpSys/Constants.cs @@ -1,6 +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 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 314ea7bbface..784a235365ae 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 HTTP_COOKED_URL _nativeCookedUrl; + private readonly HttpApiTypes.HTTP_COOKED_URL _nativeCookedUrl; - internal CookedUrl(HTTP_COOKED_URL nativeCookedUrl) + internal CookedUrl(HttpApiTypes.HTTP_COOKED_URL nativeCookedUrl) { _nativeCookedUrl = nativeCookedUrl; } internal unsafe string? GetFullUrl() { - if (!_nativeCookedUrl.pFullUrl.Equals(null) && _nativeCookedUrl.FullUrlLength > 0) + if (_nativeCookedUrl.pFullUrl != null && _nativeCookedUrl.FullUrlLength > 0) { - return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pFullUrl.Value, _nativeCookedUrl.FullUrlLength / 2); + return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pFullUrl, _nativeCookedUrl.FullUrlLength / 2); } return null; } internal unsafe string? GetHost() { - if (!_nativeCookedUrl.pHost.Equals(null) && _nativeCookedUrl.HostLength > 0) + if (_nativeCookedUrl.pHost != null && _nativeCookedUrl.HostLength > 0) { - return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pHost.Value, _nativeCookedUrl.HostLength / 2); + return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pHost, _nativeCookedUrl.HostLength / 2); } return null; } internal unsafe string? GetAbsPath() { - if (!_nativeCookedUrl.pAbsPath.Equals(null) && _nativeCookedUrl.AbsPathLength > 0) + if (_nativeCookedUrl.pAbsPath != null && _nativeCookedUrl.AbsPathLength > 0) { - return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pAbsPath.Value, _nativeCookedUrl.AbsPathLength / 2); + return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pAbsPath, _nativeCookedUrl.AbsPathLength / 2); } return null; } internal unsafe string? GetQueryString() { - if (!_nativeCookedUrl.pQueryString.Equals(null) && _nativeCookedUrl.QueryStringLength > 0) + if (_nativeCookedUrl.pQueryString != null && _nativeCookedUrl.QueryStringLength > 0) { - return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pQueryString.Value, _nativeCookedUrl.QueryStringLength / 2); + return Marshal.PtrToStringUni((IntPtr)_nativeCookedUrl.pQueryString, _nativeCookedUrl.QueryStringLength / 2); } return null; } diff --git a/src/Shared/HttpSys/NativeInterop/HeapAllocHandle.cs b/src/Shared/HttpSys/NativeInterop/HeapAllocHandle.cs new file mode 100644 index 000000000000..68c3b26ccb7c --- /dev/null +++ b/src/Shared/HttpSys/NativeInterop/HeapAllocHandle.cs @@ -0,0 +1,24 @@ +// 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 9d950c6228c5..50cef28ddbbe 100644 --- a/src/Shared/HttpSys/NativeInterop/HttpApiTypes.cs +++ b/src/Shared/HttpSys/NativeInterop/HttpApiTypes.cs @@ -1,60 +1,818 @@ - // 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.Frozen; -using Microsoft.Net.Http.Headers; -using Windows.Win32; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Security.Authentication; +using Microsoft.AspNetCore.Http; 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 { - // 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/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); - } -} + 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 byte 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_EX_FLAGS : uint + { + NONE = 0x00000000, + HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING = 0x00000001, + HTTP_AUTH_EX_FLAG_CAPTURE_CREDENTIAL = 0x00000002, + } + + [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 + } + } +} \ No newline at end of file diff --git a/src/Shared/HttpSys/NativeInterop/NclUtilities.cs b/src/Shared/HttpSys/NativeInterop/NclUtilities.cs new file mode 100644 index 000000000000..5c25a7e08cb4 --- /dev/null +++ b/src/Shared/HttpSys/NativeInterop/NclUtilities.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. + +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 new file mode 100644 index 000000000000..5a85bba6a2d3 --- /dev/null +++ b/src/Shared/HttpSys/NativeInterop/SafeLocalFreeChannelBinding.cs @@ -0,0 +1,48 @@ +// 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 new file mode 100644 index 000000000000..049f8abc00fa --- /dev/null +++ b/src/Shared/HttpSys/NativeInterop/SafeLocalMemHandle.cs @@ -0,0 +1,26 @@ +// 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/Servers/HttpSys/src/NativeInterop/SafeNativeOverlapped.cs b/src/Shared/HttpSys/NativeInterop/SafeNativeOverlapped.cs similarity index 78% rename from src/Servers/HttpSys/src/NativeInterop/SafeNativeOverlapped.cs rename to src/Shared/HttpSys/NativeInterop/SafeNativeOverlapped.cs index a7a05623d8ce..df975315c305 100644 --- a/src/Servers/HttpSys/src/NativeInterop/SafeNativeOverlapped.cs +++ b/src/Shared/HttpSys/NativeInterop/SafeNativeOverlapped.cs @@ -1,19 +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.Runtime.InteropServices; +using System.Threading; -namespace Microsoft.AspNetCore.Server.HttpSys; +namespace Microsoft.AspNetCore.HttpSys.Internal; 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) { @@ -36,9 +35,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 during AppDomain shutdown, there may be an outstanding operation. + // Do not call free durring AppDomain shutdown, there may be an outstanding operation. // Overlapped will take care calling free when the native callback completes. - if (oldHandle != IntPtr.Zero && !HasShutdownStarted) + if (oldHandle != IntPtr.Zero && !NclUtilities.HasShutdownStarted) { unsafe { diff --git a/src/Shared/HttpSys/NativeInterop/SocketAddress.cs b/src/Shared/HttpSys/NativeInterop/SocketAddress.cs index a75ea57ba739..2d1254289f6f 100644 --- a/src/Shared/HttpSys/NativeInterop/SocketAddress.cs +++ b/src/Shared/HttpSys/NativeInterop/SocketAddress.cs @@ -1,36 +1,166 @@ // 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; - private const int IPv6AddressSize = 28; - private const int IPv4AddressSize = 16; + // 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 WriteableOffset = 2; - private readonly byte[] _buffer; private readonly int _size; + private readonly byte[] _buffer; + private int _hash; - private SocketAddress(AddressFamily family, int size) + /// + /// [To be supplied.] + /// + public 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; + } } - internal AddressFamily Family { get; } + // 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 int GetPort() { - return (_buffer[2] << 8 & 0xFF00) | (_buffer[3]); + 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; } internal IPAddress? GetIPAddress() @@ -51,46 +181,184 @@ internal int GetPort() private IPAddress GetIpv6Address() { - Contract.Assert(_size >= IPv6AddressSize); - var bytes = new byte[NumberOfIPv6Labels * 2]; + Contract.Assert(Size >= IPv6AddressSize); + byte[] 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] }); } - internal static unsafe SocketAddress? CopyOutAddress(IntPtr address) + public override string ToString() { - var addressFamily = *((ushort*)address); - if (addressFamily == (ushort)AddressFamily.InterNetwork) + StringBuilder bytes = new StringBuilder(); + for (int i = WriteableOffset; i < this.Size; i++) { - var v4address = new SocketAddress(AddressFamily.InterNetwork, IPv4AddressSize); - fixed (byte* pBuffer = v4address._buffer) + if (i > WriteableOffset) { - for (var index = 2; index < IPv4AddressSize; index++) - { - pBuffer[index] = ((byte*)address)[index]; - } + bytes.Append(','); } - return v4address; + 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(); } - if (addressFamily == (ushort)AddressFamily.InterNetworkV6) + else if (Family == AddressFamily.InterNetwork) { - var v6address = new SocketAddress(AddressFamily.InterNetworkV6, IPv6AddressSize); - fixed (byte* pBuffer = v6address._buffer) + 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) { - for (var index = 2; index < IPv6AddressSize; index++) + 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) { - pBuffer[index] = ((byte*)address)[index]; + longestSequenceLength = currentSequenceLength; + longestSequenceStart = i - currentSequenceLength + 1; } } - return v6address; + else + { + currentSequenceLength = 0; + } + } + + 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) + { + // 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)) + { + return true; + + // SIIT - 0:0 : 0:0 : FFFF:0 : x.x.x.x + } + else if (numbers[4] == 0xFFFF && numbers[5] == 0) + { + return true; + } + } + // ISATAP + if (numbers[4] == 0 && numbers[5] == 0x5EFE) + { + return true; } - return null; + return false; } -} +} // class SocketAddress diff --git a/src/Shared/HttpSys/NativeInterop/UnsafeNativeMethods.cs b/src/Shared/HttpSys/NativeInterop/UnsafeNativeMethods.cs new file mode 100644 index 000000000000..7fed60b434b1 --- /dev/null +++ b/src/Shared/HttpSys/NativeInterop/UnsafeNativeMethods.cs @@ -0,0 +1,146 @@ +// 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 f3fe743aa133..7df5134ec1d6 100644 --- a/src/Shared/HttpSys/RequestProcessing/HeaderCollection.cs +++ b/src/Shared/HttpSys/RequestProcessing/HeaderCollection.cs @@ -1,7 +1,9 @@ // 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 f9d51e9ccd82..e0cc815dc75d 100644 --- a/src/Shared/HttpSys/RequestProcessing/HeaderParser.cs +++ b/src/Shared/HttpSys/RequestProcessing/HeaderParser.cs @@ -1,6 +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 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 882afdcf1e37..b58bad9e800e 100644 --- a/src/Shared/HttpSys/RequestProcessing/NativeRequestContext.cs +++ b/src/Shared/HttpSys/RequestProcessing/NativeRequestContext.cs @@ -7,16 +7,14 @@ 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; @@ -28,7 +26,7 @@ internal unsafe class NativeRequestContext : IDisposable private const int DefaultBufferSize = 4096 - AlignmentPadding; private IntPtr _originalBufferAddress; private readonly bool _useLatin1; - private HTTP_REQUEST_V1* _nativeRequest; + private HttpApiTypes.HTTP_REQUEST* _nativeRequest; private readonly IMemoryOwner? _backingBuffer; private MemoryHandle _memoryHandle; private readonly int _bufferAlignment; @@ -64,14 +62,14 @@ internal NativeRequestContext(MemoryPool memoryPool, uint? bufferSize, ulo } _backingBuffer.Memory.Span.Clear(); _memoryHandle = _backingBuffer.Memory.Pin(); - _nativeRequest = (HTTP_REQUEST_V1*)((long)_memoryHandle.Pointer + _bufferAlignment); + _nativeRequest = (HttpApiTypes.HTTP_REQUEST*)((long)_memoryHandle.Pointer + _bufferAlignment); RequestId = requestId; _useLatin1 = useLatin1; } // To be used by IIS Integration. - internal NativeRequestContext(HTTP_REQUEST_V1* request, bool useLatin1) + internal NativeRequestContext(HttpApiTypes.HTTP_REQUEST* request, bool useLatin1) { _useLatin1 = useLatin1; _nativeRequest = request; @@ -97,7 +95,7 @@ typedef struct _HTTP_REQUEST_TIMING_INFO } HTTP_REQUEST_TIMING_INFO, *PHTTP_REQUEST_TIMING_INFO; */ - if (!RequestInfo.TryGetValue((int)HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeRequestTiming, out var timingInfo)) + if (!RequestInfo.TryGetValue((int)HttpApiTypes.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeRequestTiming, out var timingInfo)) { return ReadOnlySpan.Empty; } @@ -107,12 +105,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[sizeof(long)..])), + ref Unsafe.As(ref MemoryMarshal.GetReference(timingInfo.Span.Slice(sizeof(long)))), timingCount); } } - internal HTTP_REQUEST_V1* NativeRequest + internal HttpApiTypes.HTTP_REQUEST* NativeRequest { get { @@ -121,12 +119,12 @@ internal HTTP_REQUEST_V1* NativeRequest } } - internal HTTP_REQUEST_V2* NativeRequestV2 + internal HttpApiTypes.HTTP_REQUEST_V2* NativeRequestV2 { get { Debug.Assert(_nativeRequest != null || _backingBuffer == null, "native request accessed after ReleasePins()."); - return (HTTP_REQUEST_V2*)_nativeRequest; + return (HttpApiTypes.HTTP_REQUEST_V2*)_nativeRequest; } } @@ -140,7 +138,7 @@ internal ulong RequestId internal ulong RawConnectionId => NativeRequest->RawConnectionId; - internal HTTP_VERB VerbId => NativeRequest->Verb; + internal HttpApiTypes.HTTP_VERB VerbId => NativeRequest->Verb; internal ulong UrlContext => NativeRequest->UrlContext; @@ -156,9 +154,9 @@ internal SslStatus SslStatus } } - internal bool IsHttp2 => (NativeRequest->Flags & PInvoke.HTTP_REQUEST_FLAG_HTTP2) != 0; + internal bool IsHttp2 => NativeRequest->Flags.HasFlag(HttpApiTypes.HTTP_REQUEST_FLAGS.Http2); - internal bool IsHttp3 => (NativeRequest->Flags & PInvoke.HTTP_REQUEST_FLAG_HTTP3) != 0; + internal bool IsHttp3 => NativeRequest->Flags.HasFlag(HttpApiTypes.HTTP_REQUEST_FLAGS.Http3); // Assumes memory isn't pinned. Will fail if called by IIS. internal uint Size @@ -197,7 +195,7 @@ public bool TryGetTimestamp(HttpSysRequestTimingType timestampType, out long tim public bool TryGetElapsedTime(HttpSysRequestTimingType startingTimestampType, HttpSysRequestTimingType endingTimestampType, out TimeSpan elapsed) { - if (TryGetTimestamp(startingTimestampType, out var startTimestamp) && TryGetTimestamp(endingTimestampType, out var endTimestamp)) + if (TryGetTimestamp(startingTimestampType, out long startTimestamp) && TryGetTimestamp(endingTimestampType, out long endTimestamp)) { elapsed = Stopwatch.GetElapsedTime(startTimestamp, endTimestamp); return true; @@ -223,12 +221,11 @@ public virtual void Dispose() internal string? GetVerb() { var verb = NativeRequest->Verb; - Debug.Assert((int)HTTP_VERB.HttpVerbMaximum == HttpVerbs.Length); - if (verb > HTTP_VERB.HttpVerbUnknown && verb < HTTP_VERB.HttpVerbMaximum) + if (verb > HttpApiTypes.HTTP_VERB.HttpVerbUnknown && verb < HttpApiTypes.HTTP_VERB.HttpVerbMaximum) { - return HttpVerbs[(int)verb]; + return HttpApiTypes.HttpVerbs[(int)verb]; } - else if (verb == HTTP_VERB.HttpVerbUnknown && !NativeRequest->pUnknownVerb.Equals(null)) + else if (verb == HttpApiTypes.HTTP_VERB.HttpVerbUnknown && NativeRequest->pUnknownVerb != null) { // Never use Latin1 for the VERB return HeaderEncoding.GetString(NativeRequest->pUnknownVerb, NativeRequest->UnknownVerbLength, useLatin1: false); @@ -237,43 +234,18 @@ 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.Equals(null) && NativeRequest->RawUrlLength > 0) + if (NativeRequest->pRawUrl != null && NativeRequest->RawUrlLength > 0) { - return Marshal.PtrToStringAnsi((IntPtr)NativeRequest->pRawUrl.Value, NativeRequest->RawUrlLength); + return Marshal.PtrToStringAnsi((IntPtr)NativeRequest->pRawUrl, NativeRequest->RawUrlLength); } return null; } internal Span GetRawUrlInBytes() { - if (!NativeRequest->pRawUrl.Equals(null) && NativeRequest->RawUrlLength > 0) + if (NativeRequest->pRawUrl != null && NativeRequest->RawUrlLength > 0) { return new Span(NativeRequest->pRawUrl, NativeRequest->RawUrlLength); } @@ -314,14 +286,14 @@ internal bool CheckAuthenticated() var requestInfo = NativeRequestV2->pRequestInfo; var infoCount = NativeRequestV2->RequestInfoCount; - for (var i = 0; i < infoCount; i++) + for (int i = 0; i < infoCount; i++) { var info = &requestInfo[i]; if (info != null - && info->InfoType == HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth) + && info->InfoType == HttpApiTypes.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth) { - var authInfo = (HTTP_REQUEST_AUTH_INFO*)info->pInfo; - if (authInfo->AuthStatus == HTTP_AUTH_STATUS.HttpAuthStatusSuccess) + var authInfo = (HttpApiTypes.HTTP_REQUEST_AUTH_INFO*)info->pInfo; + if (authInfo->AuthStatus == HttpApiTypes.HTTP_AUTH_STATUS.HttpAuthStatusSuccess) { return true; } @@ -335,20 +307,20 @@ internal WindowsPrincipal GetUser() var requestInfo = NativeRequestV2->pRequestInfo; var infoCount = NativeRequestV2->RequestInfoCount; - for (var i = 0; i < infoCount; i++) + for (int i = 0; i < infoCount; i++) { var info = &requestInfo[i]; if (info != null - && info->InfoType == HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth) + && info->InfoType == HttpApiTypes.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeAuth) { - var authInfo = (HTTP_REQUEST_AUTH_INFO*)info->pInfo; - if (authInfo->AuthStatus == HTTP_AUTH_STATUS.HttpAuthStatusSuccess) + var authInfo = (HttpApiTypes.HTTP_REQUEST_AUTH_INFO*)info->pInfo; + if (authInfo->AuthStatus == HttpApiTypes.HTTP_AUTH_STATUS.HttpAuthStatusSuccess) { // Duplicates AccessToken var identity = new WindowsIdentity(authInfo->AccessToken, GetAuthTypeFromRequest(authInfo->AuthType)); // Close the original - PInvoke.CloseHandle(authInfo->AccessToken); + UnsafeNclNativeMethods.SafeNetHandles.CloseHandle(authInfo->AccessToken); return new WindowsPrincipal(identity); } @@ -358,18 +330,18 @@ internal WindowsPrincipal GetUser() return new WindowsPrincipal(WindowsIdentity.GetAnonymous()); // Anonymous / !IsAuthenticated } - internal HTTP_SSL_PROTOCOL_INFO GetTlsHandshake() + internal HttpApiTypes.HTTP_SSL_PROTOCOL_INFO GetTlsHandshake() { var requestInfo = NativeRequestV2->pRequestInfo; var infoCount = NativeRequestV2->RequestInfoCount; - for (var i = 0; i < infoCount; i++) + for (int i = 0; i < infoCount; i++) { var info = &requestInfo[i]; if (info != null - && info->InfoType == HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeSslProtocol) + && info->InfoType == HttpApiTypes.HTTP_REQUEST_INFO_TYPE.HttpRequestInfoTypeSslProtocol) { - var authInfo = *(HTTP_SSL_PROTOCOL_INFO*)info->pInfo; + var authInfo = *((HttpApiTypes.HTTP_SSL_PROTOCOL_INFO*)info->pInfo); SetSslProtocol(&authInfo); return authInfo; } @@ -378,7 +350,7 @@ internal HTTP_SSL_PROTOCOL_INFO GetTlsHandshake() return default; } - private static void SetSslProtocol(HTTP_SSL_PROTOCOL_INFO* protocolInfo) + private static void SetSslProtocol(HttpApiTypes.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. @@ -386,48 +358,54 @@ private static void SetSslProtocol(HTTP_SSL_PROTOCOL_INFO* protocolInfo) // 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 & (uint)SslProtocols.Ssl2) != 0) + if ((protocol & SslProtocols.Ssl2) != 0) { - protocol |= (uint)SslProtocols.Ssl2; + protocol |= SslProtocols.Ssl2; } - if ((protocol & (uint)SslProtocols.Ssl3) != 0) + if ((protocol & SslProtocols.Ssl3) != 0) { - protocol |= (uint)SslProtocols.Ssl3; + protocol |= 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 & (uint)SslProtocols.Tls) != 0) + if ((protocol & SslProtocols.Tls) != 0) { - protocol |= (uint)SslProtocols.Tls; + protocol |= SslProtocols.Tls; } - if ((protocol & (uint)SslProtocols.Tls11) != 0) + if ((protocol & SslProtocols.Tls11) != 0) { - protocol |= (uint)SslProtocols.Tls11; + protocol |= SslProtocols.Tls11; } #pragma warning restore SYSLIB0039 - if ((protocol & (uint)SslProtocols.Tls12) != 0) + if ((protocol & SslProtocols.Tls12) != 0) { - protocol |= (uint)SslProtocols.Tls12; + protocol |= SslProtocols.Tls12; } - if ((protocol & (uint)SslProtocols.Tls13) != 0) + if ((protocol & SslProtocols.Tls13) != 0) { - protocol |= (uint)SslProtocols.Tls13; + protocol |= SslProtocols.Tls13; } protocolInfo->Protocol = protocol; } - private static string GetAuthTypeFromRequest(HTTP_REQUEST_AUTH_TYPE input) + private static string GetAuthTypeFromRequest(HttpApiTypes.HTTP_REQUEST_AUTH_TYPE input) { - return input switch + switch (input) { - 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()), - }; + 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()); + } } internal bool HasKnownHeader(HttpSysRequestHeader header) @@ -440,21 +418,21 @@ internal bool HasKnownHeader(HttpSysRequestHeader header) { fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HTTP_REQUEST_V1*)(pMemoryBlob + _bufferAlignment); - var fixup = pMemoryBlob - (byte*)_originalBufferAddress; + var request = (HttpApiTypes.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment); + long fixup = pMemoryBlob - (byte*)_originalBufferAddress; return HasKnowHeaderHelper(header, fixup, request); } } } - private bool HasKnowHeaderHelper(HttpSysRequestHeader header, long fixup, HTTP_REQUEST_V1* request) + private bool HasKnowHeaderHelper(HttpSysRequestHeader header, long fixup, HttpApiTypes.HTTP_REQUEST* request) { - var headerIndex = (int)header; + int headerIndex = (int)header; - var pKnownHeader = request->Headers.KnownHeaders.AsSpan()[headerIndex]; + HttpApiTypes.HTTP_KNOWN_HEADER* pKnownHeader = (&request->Headers.KnownHeaders) + 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; } @@ -475,24 +453,24 @@ private bool HasKnowHeaderHelper(HttpSysRequestHeader header, long fixup, HTTP_R { fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HTTP_REQUEST_V1*)(pMemoryBlob + _bufferAlignment); - var fixup = pMemoryBlob - (byte*)_originalBufferAddress; + var request = (HttpApiTypes.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment); + long fixup = pMemoryBlob - (byte*)_originalBufferAddress; return GetKnowHeaderHelper(header, fixup, request); } } } - private string? GetKnowHeaderHelper(HttpSysRequestHeader header, long fixup, HTTP_REQUEST_V1* request) + private string? GetKnowHeaderHelper(HttpSysRequestHeader header, long fixup, HttpApiTypes.HTTP_REQUEST* request) { - var headerIndex = (int)header; + int headerIndex = (int)header; string? value = null; - var pKnownHeader = request->Headers.KnownHeaders.AsSpan()[headerIndex]; + HttpApiTypes.HTTP_KNOWN_HEADER* pKnownHeader = (&request->Headers.KnownHeaders) + 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((byte*)pKnownHeader.pRawValue + fixup, pKnownHeader.RawValueLength, _useLatin1); + value = HeaderEncoding.GetString(pKnownHeader->pRawValue + fixup, pKnownHeader->RawValueLength, _useLatin1); } return value; @@ -508,25 +486,25 @@ internal void GetUnknownKeys(Span destination) { fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HTTP_REQUEST_V1*)(pMemoryBlob + _bufferAlignment); - var fixup = pMemoryBlob - (byte*)_originalBufferAddress; + var request = (HttpApiTypes.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment); + long fixup = pMemoryBlob - (byte*)_originalBufferAddress; PopulateUnknownKeys(request, fixup, destination); } } } - private void PopulateUnknownKeys(HTTP_REQUEST_V1* request, long fixup, Span destination) + private void PopulateUnknownKeys(HttpApiTypes.HTTP_REQUEST* request, long fixup, Span destination) { if (request->Headers.UnknownHeaderCount == 0) { return; } - var pUnknownHeader = (HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders); - for (var index = 0; index < request->Headers.UnknownHeaderCount; index++) + var pUnknownHeader = (HttpApiTypes.HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders); + for (int index = 0; index < request->Headers.UnknownHeaderCount; index++) { - if (!pUnknownHeader->pName.Equals(null) && pUnknownHeader->NameLength > 0) + if (pUnknownHeader->pName != null && pUnknownHeader->NameLength > 0) { - var headerName = HeaderEncoding.GetString((byte*)pUnknownHeader->pName + fixup, pUnknownHeader->NameLength, _useLatin1); + var headerName = HeaderEncoding.GetString(pUnknownHeader->pName + fixup, pUnknownHeader->NameLength, _useLatin1); destination[index] = headerName; } pUnknownHeader++; @@ -543,26 +521,26 @@ internal int CountUnknownHeaders() { fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HTTP_REQUEST_V1*)(pMemoryBlob + _bufferAlignment); - var fixup = pMemoryBlob - (byte*)_originalBufferAddress; + var request = (HttpApiTypes.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment); + long fixup = pMemoryBlob - (byte*)_originalBufferAddress; return CountUnknownHeaders(request, fixup); } } } - private int CountUnknownHeaders(HTTP_REQUEST_V1* request, long fixup) + private int CountUnknownHeaders(HttpApiTypes.HTTP_REQUEST* request, long fixup) { if (request->Headers.UnknownHeaderCount == 0) { return 0; } - var count = 0; - var pUnknownHeader = (HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders); - for (var index = 0; index < request->Headers.UnknownHeaderCount; index++) + int count = 0; + var pUnknownHeader = (HttpApiTypes.HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders); + for (int 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.Equals(null) && pUnknownHeader->NameLength > 0) + if (pUnknownHeader->pName != null && pUnknownHeader->NameLength > 0) { count++; } @@ -582,32 +560,32 @@ internal void GetUnknownHeaders(IDictionary unknownHeaders // Return value. fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HTTP_REQUEST_V1*)(pMemoryBlob + _bufferAlignment); - var fixup = pMemoryBlob - (byte*)_originalBufferAddress; + var request = (HttpApiTypes.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment); + long fixup = pMemoryBlob - (byte*)_originalBufferAddress; GetUnknownHeadersHelper(unknownHeaders, fixup, request); } } } - private void GetUnknownHeadersHelper(IDictionary unknownHeaders, long fixup, HTTP_REQUEST_V1* request) + private void GetUnknownHeadersHelper(IDictionary unknownHeaders, long fixup, HttpApiTypes.HTTP_REQUEST* request) { int index; // unknown headers if (request->Headers.UnknownHeaderCount != 0) { - var pUnknownHeader = (HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders); + var pUnknownHeader = (HttpApiTypes.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.Equals(null) && pUnknownHeader->NameLength > 0) + if (pUnknownHeader->pName != null && pUnknownHeader->NameLength > 0) { - var headerName = HeaderEncoding.GetString((byte*)pUnknownHeader->pName + fixup, pUnknownHeader->NameLength, _useLatin1); + var headerName = HeaderEncoding.GetString(pUnknownHeader->pName + fixup, pUnknownHeader->NameLength, _useLatin1); string headerValue; - if (!pUnknownHeader->pRawValue.Equals(null) && pUnknownHeader->RawValueLength > 0) + if (pUnknownHeader->pRawValue != null && pUnknownHeader->RawValueLength > 0) { - headerValue = HeaderEncoding.GetString((byte*)pUnknownHeader->pRawValue + fixup, pUnknownHeader->RawValueLength, _useLatin1); + headerValue = HeaderEncoding.GetString(pUnknownHeader->pRawValue + fixup, pUnknownHeader->RawValueLength, _useLatin1); } else { @@ -642,13 +620,13 @@ private void GetUnknownHeadersHelper(IDictionary unknownHe { fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HTTP_REQUEST_V1*)(pMemoryBlob + _bufferAlignment); + var request = (HttpApiTypes.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment); return GetEndPointHelper(localEndpoint, request, pMemoryBlob); } } } - private SocketAddress? GetEndPointHelper(bool localEndpoint, HTTP_REQUEST_V1* request, byte* pMemoryBlob) + private SocketAddress? GetEndPointHelper(bool localEndpoint, HttpApiTypes.HTTP_REQUEST* request, byte* pMemoryBlob) { var source = localEndpoint ? (byte*)request->Address.pLocalAddress : (byte*)request->Address.pRemoteAddress; @@ -657,7 +635,38 @@ private void GetUnknownHeadersHelper(IDictionary unknownHe return null; } var address = (IntPtr)(pMemoryBlob + _bufferAlignment - (byte*)_originalBufferAddress + source); - return SocketAddress.CopyOutAddress(address); + 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; } internal uint GetChunks(ref int dataChunkIndex, ref uint dataChunkOffset, byte[] buffer, int offset, int size) @@ -671,28 +680,28 @@ internal uint GetChunks(ref int dataChunkIndex, ref uint dataChunkOffset, byte[] { fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HTTP_REQUEST_V1*)(pMemoryBlob + _bufferAlignment); - var fixup = pMemoryBlob - (byte*)_originalBufferAddress; + var request = (HttpApiTypes.HTTP_REQUEST*)(pMemoryBlob + _bufferAlignment); + long 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, HTTP_REQUEST_V1* request) + private uint GetChunksHelper(ref int dataChunkIndex, ref uint dataChunkOffset, byte[] buffer, int offset, int size, long fixup, HttpApiTypes.HTTP_REQUEST* request) { uint dataRead = 0; if (request->EntityChunkCount > 0 && dataChunkIndex < request->EntityChunkCount && dataChunkIndex != -1) { - var pDataChunk = (HTTP_DATA_CHUNK*)(fixup + (byte*)&request->pEntityChunks[dataChunkIndex]); + var pDataChunk = (HttpApiTypes.HTTP_DATA_CHUNK*)(fixup + (byte*)&request->pEntityChunks[dataChunkIndex]); fixed (byte* pReadBuffer = buffer) { - var pTo = &pReadBuffer[offset]; + byte* pTo = &pReadBuffer[offset]; while (dataChunkIndex < request->EntityChunkCount && dataRead < size) { - if (dataChunkOffset >= pDataChunk->Anonymous.FromMemory.BufferLength) + if (dataChunkOffset >= pDataChunk->fromMemory.BufferLength) { dataChunkOffset = 0; dataChunkIndex++; @@ -700,9 +709,9 @@ private uint GetChunksHelper(ref int dataChunkIndex, ref uint dataChunkOffset, b } else { - var pFrom = (byte*)pDataChunk->Anonymous.FromMemory.pBuffer + dataChunkOffset + fixup; + byte* pFrom = (byte*)pDataChunk->fromMemory.pBuffer + dataChunkOffset + fixup; - var bytesToRead = pDataChunk->Anonymous.FromMemory.BufferLength - (uint)dataChunkOffset; + uint bytesToRead = pDataChunk->fromMemory.BufferLength - (uint)dataChunkOffset; if (bytesToRead > (uint)size) { bytesToRead = (uint)size; @@ -729,19 +738,19 @@ internal IReadOnlyDictionary> GetRequestInfo() { if (PermanentlyPinned) { - return GetRequestInfo((IntPtr)_nativeRequest, (HTTP_REQUEST_V2*)_nativeRequest); + return GetRequestInfo((IntPtr)_nativeRequest, (HttpApiTypes.HTTP_REQUEST_V2*)_nativeRequest); } else { fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HTTP_REQUEST_V2*)(pMemoryBlob + _bufferAlignment); + var request = (HttpApiTypes.HTTP_REQUEST_V2*)(pMemoryBlob + _bufferAlignment); return GetRequestInfo(_originalBufferAddress, request); } } } - private IReadOnlyDictionary> GetRequestInfo(IntPtr baseAddress, HTTP_REQUEST_V2* nativeRequest) + private IReadOnlyDictionary> GetRequestInfo(IntPtr baseAddress, HttpApiTypes.HTTP_REQUEST_V2* nativeRequest) { var count = nativeRequest->RequestInfoCount; if (count == 0) @@ -751,8 +760,8 @@ private IReadOnlyDictionary> GetRequestInfo(IntPtr bas var info = new Dictionary>(count); - var fixup = (byte*)nativeRequest - (byte*)baseAddress; - var pRequestInfo = (HTTP_REQUEST_INFO*)((byte*)nativeRequest->pRequestInfo + fixup); + long fixup = (byte*)nativeRequest - (byte*)baseAddress; + var pRequestInfo = (HttpApiTypes.HTTP_REQUEST_INFO*)((byte*)nativeRequest->pRequestInfo + fixup); for (var i = 0; i < count; i++) { @@ -772,42 +781,42 @@ private IReadOnlyDictionary> GetRequestInfo(IntPtr bas { if (PermanentlyPinned) { - return GetClientCertificate((IntPtr)_nativeRequest, (HTTP_REQUEST_V2*)_nativeRequest); + return GetClientCertificate((IntPtr)_nativeRequest, (HttpApiTypes.HTTP_REQUEST_V2*)_nativeRequest); } else { fixed (byte* pMemoryBlob = _backingBuffer.Memory.Span) { - var request = (HTTP_REQUEST_V2*)(pMemoryBlob + _bufferAlignment); + var request = (HttpApiTypes.HTTP_REQUEST_V2*)(pMemoryBlob + _bufferAlignment); return GetClientCertificate(_originalBufferAddress, request); } } } // Throws CryptographicException - private X509Certificate2? GetClientCertificate(IntPtr baseAddress, HTTP_REQUEST_V2* nativeRequest) + private X509Certificate2? GetClientCertificate(IntPtr baseAddress, HttpApiTypes.HTTP_REQUEST_V2* nativeRequest) { - var request = nativeRequest->Base; - var fixup = (byte*)nativeRequest - (byte*)baseAddress; + var request = nativeRequest->Request; + long fixup = (byte*)nativeRequest - (byte*)baseAddress; if (request.pSslInfo == null) { return null; } - var sslInfo = (HTTP_SSL_INFO*)((byte*)request.pSslInfo + fixup); + var sslInfo = (HttpApiTypes.HTTP_SSL_INFO*)((byte*)request.pSslInfo + fixup); if (sslInfo->SslClientCertNegotiated == 0 || sslInfo->pClientCertInfo == null) { return null; } - var clientCertInfo = (HTTP_SSL_CLIENT_CERT_INFO*)((byte*)sslInfo->pClientCertInfo + fixup); + var clientCertInfo = (HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO*)((byte*)sslInfo->pClientCertInfo + fixup); if (clientCertInfo->pCertEncoded == null) { return null; } var clientCert = clientCertInfo->pCertEncoded + fixup; - var certEncoded = new byte[clientCertInfo->CertEncodedSize]; + byte[] 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 d29d26624578..b1bc8c7e22f2 100644 --- a/src/Shared/HttpSys/RequestProcessing/PathNormalizer.cs +++ b/src/Shared/HttpSys/RequestProcessing/PathNormalizer.cs @@ -1,6 +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.Diagnostics; namespace Microsoft.AspNetCore.HttpSys.Internal; diff --git a/src/Shared/HttpSys/RequestProcessing/RawUrlHelper.cs b/src/Shared/HttpSys/RequestProcessing/RawUrlHelper.cs index e9b1551d4c80..e59bf434b435 100644 --- a/src/Shared/HttpSys/RequestProcessing/RawUrlHelper.cs +++ b/src/Shared/HttpSys/RequestProcessing/RawUrlHelper.cs @@ -1,6 +1,9 @@ // 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 27ed6bcdf1b4..84d1235f4462 100644 --- a/src/Shared/HttpSys/RequestProcessing/RequestUriBuilder.cs +++ b/src/Shared/HttpSys/RequestProcessing/RequestUriBuilder.cs @@ -1,6 +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.Diagnostics; using System.Text; diff --git a/src/Shared/ServerInfrastructure/StringUtilities.cs b/src/Shared/ServerInfrastructure/StringUtilities.cs index c018582212e0..904cda0fc7f6 100644 --- a/src/Shared/ServerInfrastructure/StringUtilities.cs +++ b/src/Shared/ServerInfrastructure/StringUtilities.cs @@ -1,6 +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.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 95e769782841..3cfc47e7e76f 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;CS9195 + $(NoWarn);CS0649;CS0436 annotations @@ -51,10 +51,6 @@ - - all - runtime; build; native; contentfiles; analyzers - diff --git a/src/Shared/test/Shared.Tests/NativeMethods.txt b/src/Shared/test/Shared.Tests/NativeMethods.txt deleted file mode 100644 index 268b666b0111..000000000000 --- a/src/Shared/test/Shared.Tests/NativeMethods.txt +++ /dev/null @@ -1,2 +0,0 @@ -// https://github.com/microsoft/cswin32 -Windows.Win32.Networking.HttpServer