Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove some GCHandles from SendHeaders() #44561

Merged
merged 25 commits into from Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7bd202b
Remove some GCHandles from SendHeaders()
AaronRobinsonMSFT Oct 14, 2022
b31af3f
Comments and style
AaronRobinsonMSFT Oct 15, 2022
def7b1b
Change concrete type to interface in signature
AaronRobinsonMSFT Oct 15, 2022
ff8ecda
Match the semantics of the GetBytes() API
AaronRobinsonMSFT Oct 15, 2022
dbbf758
Fix use of wrong class.
AaronRobinsonMSFT Oct 15, 2022
2555da8
Use MemoryMarshal.GetReference to deref
AaronRobinsonMSFT Oct 15, 2022
54810a2
Review comments.
AaronRobinsonMSFT Oct 16, 2022
e9d1f71
Respond to review feedback.
AaronRobinsonMSFT Oct 16, 2022
91e2eb6
Remove unused using.
AaronRobinsonMSFT Oct 16, 2022
d06e33a
Remove unused usings.
AaronRobinsonMSFT Oct 16, 2022
9194c6f
Review feedback
AaronRobinsonMSFT Oct 16, 2022
e35a5ac
Revert argument Span<> back to [].
AaronRobinsonMSFT Oct 17, 2022
6f7827b
Missed forgiveness operator.
AaronRobinsonMSFT Oct 17, 2022
72360b3
Update stackalloc usage
AaronRobinsonMSFT Oct 17, 2022
be7fbac
Convert more allocations to unmanged
AaronRobinsonMSFT Oct 17, 2022
379a95d
Provide an Alloc for pointer or Span<T>
AaronRobinsonMSFT Oct 17, 2022
3bce273
Fix spelling.
AaronRobinsonMSFT Oct 17, 2022
743c36d
Improve exception safety.
AaronRobinsonMSFT Oct 17, 2022
1625d2b
Remove unnecessary allocation
AaronRobinsonMSFT Oct 18, 2022
99bc134
Remove the notion of max allocation and instead
AaronRobinsonMSFT Oct 18, 2022
b154b55
Remove more GCHandles
AaronRobinsonMSFT Oct 18, 2022
113ea60
Review feedback.
AaronRobinsonMSFT Oct 18, 2022
6096423
Review feedback
AaronRobinsonMSFT Oct 18, 2022
faf92cf
Review feedback
AaronRobinsonMSFT Oct 18, 2022
2d50420
Remove unused using.
AaronRobinsonMSFT Oct 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Servers/HttpSys/src/Helpers.cs
Expand Up @@ -8,8 +8,8 @@ namespace Microsoft.AspNetCore.Server.HttpSys;

internal static class Helpers
{
internal static readonly byte[] ChunkTerminator = new byte[] { (byte)'0', (byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n' };
internal static readonly byte[] CRLF = new byte[] { (byte)'\r', (byte)'\n' };
public static ReadOnlySpan<byte> ChunkTerminator => "0\r\n\r\n"u8;
public static ReadOnlySpan<byte> CRLF => "\r\n"u8;

internal static ArraySegment<byte> GetChunkHeader(long size)
{
Expand Down
147 changes: 61 additions & 86 deletions src/Servers/HttpSys/src/HttpSysListener.cs
Expand Up @@ -3,7 +3,6 @@

using System.Buffers;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpSys.Internal;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -310,103 +309,79 @@ internal unsafe void SendError(ulong requestId, int httpStatusCode, IList<string
httpResponse.Response_V1.Version.MajorVersion = (ushort)1;
httpResponse.Response_V1.Version.MinorVersion = (ushort)1;

List<GCHandle>? pinnedHeaders = null;
GCHandle gcHandle;
try
{
// Copied from the multi-value headers section of SerializeHeaders
if (authChallenges != null && authChallenges.Count > 0)
{
pinnedHeaders = new List<GCHandle>(authChallenges.Count + 3);
using UnmanagedBufferAllocator allocator = new();

HttpApiTypes.HTTP_RESPONSE_INFO[] knownHeaderInfo = new HttpApiTypes.HTTP_RESPONSE_INFO[1];
gcHandle = GCHandle.Alloc(knownHeaderInfo, GCHandleType.Pinned);
pinnedHeaders.Add(gcHandle);
httpResponse.pResponseInfo = (HttpApiTypes.HTTP_RESPONSE_INFO*)gcHandle.AddrOfPinnedObject();
byte* bytes;
int bytesLength;

knownHeaderInfo[httpResponse.ResponseInfoCount].Type = HttpApiTypes.HTTP_RESPONSE_INFO_TYPE.HttpResponseInfoTypeMultipleKnownHeaders;
knownHeaderInfo[httpResponse.ResponseInfoCount].Length =
(uint)Marshal.SizeOf<HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS>();
// Copied from the multi-value headers section of SerializeHeaders
if (authChallenges != null && authChallenges.Count > 0)
{
HttpApiTypes.HTTP_RESPONSE_INFO* knownHeaderInfo = allocator.AllocAsPointer<HttpApiTypes.HTTP_RESPONSE_INFO>(1);
httpResponse.pResponseInfo = knownHeaderInfo;

HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS header = new HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS();
knownHeaderInfo[httpResponse.ResponseInfoCount].Type = HttpApiTypes.HTTP_RESPONSE_INFO_TYPE.HttpResponseInfoTypeMultipleKnownHeaders;
knownHeaderInfo[httpResponse.ResponseInfoCount].Length =
(uint)sizeof(HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS);

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.
HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS* header = allocator.AllocAsPointer<HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS>(1);

HttpApiTypes.HTTP_KNOWN_HEADER[] nativeHeaderValues = new HttpApiTypes.HTTP_KNOWN_HEADER[authChallenges.Count];
gcHandle = GCHandle.Alloc(nativeHeaderValues, GCHandleType.Pinned);
pinnedHeaders.Add(gcHandle);
header.KnownHeaders = (HttpApiTypes.HTTP_KNOWN_HEADER*)gcHandle.AddrOfPinnedObject();
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;

for (int headerValueIndex = 0; headerValueIndex < authChallenges.Count; headerValueIndex++)
{
// Add Value
string headerValue = authChallenges[headerValueIndex];
byte[] bytes = HeaderEncoding.GetBytes(headerValue);
nativeHeaderValues[header.KnownHeaderCount].RawValueLength = (ushort)bytes.Length;
gcHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
pinnedHeaders.Add(gcHandle);
nativeHeaderValues[header.KnownHeaderCount].pRawValue = (byte*)gcHandle.AddrOfPinnedObject();
header.KnownHeaderCount++;
}
HttpApiTypes.HTTP_KNOWN_HEADER* nativeHeaderValues = allocator.AllocAsPointer<HttpApiTypes.HTTP_KNOWN_HEADER>(authChallenges.Count);
header->KnownHeaders = nativeHeaderValues;

// This type is a struct, not an object, so pinning it causes a boxed copy to be created. We can't do that until after all the fields are set.
gcHandle = GCHandle.Alloc(header, GCHandleType.Pinned);
pinnedHeaders.Add(gcHandle);
knownHeaderInfo[0].pInfo = (HttpApiTypes.HTTP_MULTIPLE_KNOWN_HEADERS*)gcHandle.AddrOfPinnedObject();

httpResponse.ResponseInfoCount = 1;
for (int headerValueIndex = 0; headerValueIndex < authChallenges.Count; headerValueIndex++)
{
// Add Value
string headerValue = authChallenges[headerValueIndex];
bytes = allocator.GetHeaderEncodedBytes(headerValue, out bytesLength);
nativeHeaderValues[header->KnownHeaderCount].RawValueLength = checked((ushort)bytesLength);
nativeHeaderValues[header->KnownHeaderCount].pRawValue = bytes;
header->KnownHeaderCount++;
}

httpResponse.Response_V1.StatusCode = (ushort)httpStatusCode;
string? statusDescription = HttpReasonPhrase.Get(httpStatusCode);
uint dataWritten = 0;
uint statusCode;
byte[] byteReason = statusDescription != null ? HeaderEncoding.GetBytes(statusDescription) : Array.Empty<byte>();
fixed (byte* pReason = byteReason)
{
httpResponse.Response_V1.pReason = (byte*)pReason;
httpResponse.Response_V1.ReasonLength = (ushort)byteReason.Length;
knownHeaderInfo[0].pInfo = header;

byte[] byteContentLength = new byte[] { (byte)'0' };
fixed (byte* pContentLength = byteContentLength)
{
(&httpResponse.Response_V1.Headers.KnownHeaders)[(int)HttpSysResponseHeader.ContentLength].pRawValue = (byte*)pContentLength;
(&httpResponse.Response_V1.Headers.KnownHeaders)[(int)HttpSysResponseHeader.ContentLength].RawValueLength = (ushort)byteContentLength.Length;
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
HttpApi.HttpCancelHttpRequest(_requestQueue.Handle, requestId, IntPtr.Zero);
}
httpResponse.ResponseInfoCount = 1;
}
finally

httpResponse.Response_V1.StatusCode = checked((ushort)httpStatusCode);
string statusDescription = HttpReasonPhrase.Get(httpStatusCode) ?? string.Empty;
uint dataWritten = 0;
uint statusCode;

bytes = allocator.GetHeaderEncodedBytes(statusDescription, out bytesLength);
httpResponse.Response_V1.pReason = bytes;
httpResponse.Response_V1.ReasonLength = checked((ushort)bytesLength);

const int contentLengthLength = 1;
byte* pContentLength = allocator.AllocAsPointer<byte>(contentLengthLength + 1);
pContentLength[0] = (byte)'0';
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
pContentLength[1] = 0; // null terminator

(&httpResponse.Response_V1.Headers.KnownHeaders)[(int)HttpSysResponseHeader.ContentLength].pRawValue = pContentLength;
(&httpResponse.Response_V1.Headers.KnownHeaders)[(int)HttpSysResponseHeader.ContentLength].RawValueLength = contentLengthLength;
httpResponse.Response_V1.Headers.UnknownHeaderCount = 0;

statusCode =
HttpApi.HttpSendHttpResponse(
_requestQueue.Handle,
requestId,
0,
&httpResponse,
null,
&dataWritten,
IntPtr.Zero,
0,
SafeNativeOverlapped.Zero,
IntPtr.Zero);
if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
{
if (pinnedHeaders != null)
{
foreach (GCHandle handle in pinnedHeaders)
{
if (handle.IsAllocated)
{
handle.Free();
}
}
}
// if we fail to send a 401 something's seriously wrong, abort the request
HttpApi.HttpCancelHttpRequest(_requestQueue.Handle, requestId, IntPtr.Zero);
}
}

Expand Down