-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Description
Background and Motivation
Windows Http.sys supports an HttpQueryRequestProperty API that provides access to various details about the HTTP request and connection. This is currently used in the ASP.NET HttpSys server to make the TLS ClientHello and TLS SNI hostname available to the user.
It would be useful to expose the HttpQueryRequestProperty
API through a generic interface, allowing direct access to all the information it provides. The benefits are:
- Future-proofing: If Windows ships new telemetry that is available through this API, we'll be able to consume it without needing code changes in ASP.NET Core. For the TLS ClientHello and SNI hostname features, the support was added in a piecemeal fashion, requiring a feature ask, implementation, and backporting. The generic API would make it possible to consume HttpSys features more easily and with a shorter turnaround time.
- Existing telemetry: Some information that is already available through this API - such as TCP statistics - is useful for debugging and network monitoring.
This API would be similar to IHttpSysRequestInfoFeature in that it provides raw data to the user, who must then parse it themselves using the http.h header as a reference.
Proposed API
namespace Microsoft.AspNetCore.Server.HttpSys;
public interface IHttpSysRequestPropertyFeature
{
bool TryGetTlsClientHello(Span<byte> tlsClientHelloBytesDestination, out int bytesReturned);
+ ValueTask<bool> TryGetRequestPropertyAsync(int propertyId, ReadOnlyMemory<byte> qualifier, Memory<byte> output, out int bytesReturned);
}
- The method returns
true
on success and fills the user-providedoutput
buffer with the value of the requested property.bytesReturned
returns the number of bytes written to the buffer. - The method returns
false
if the buffer is too small. In this case,bytesReturned
returns the required buffer size. - For any other error, the method throws an exception.
- Both
qualifier
andoutput
can be set to an emptyMemory<byte>
. The user would setqualifier
to empty for property IDs that don't use a qualifier (this must be mapped to null when calling the underlyingHttpQueryRequestProperty
API, since the API will returnERROR_INVALID_PARAMETER
for any other pointer value). The user would setoutput
to empty to query for the required buffer size.
The method is asynchronous since the HttpQueryRequestProperty
Windows API supports async completion. The docs suggest that most properties are fetched synchronously, but that async completion is possible:
While you can provide an OVERLAPPED structure for asynchronous operation, setting this parameter is not a guarantee that the API will run asynchronously; most operations on this API are always synchronous.
Usage Examples
Example of retrieving the HttpRequestPropertyStreamError
property using this API:
[StructLayout(LayoutKind.Sequential)]
struct HTTP_REQUEST_PROPERTY_STREAM_ERROR
{
public uint ErrorCode;
}
async ValueTask<uint> GetStreamErrorAsync(HttpContext context)
{
const int HttpRequestPropertyStreamError = 5;
var feature = context.Features.Get<IHttpSysRequestPropertyFeature>();
int bufferSize = Marshal.SizeOf<HTTP_REQUEST_PROPERTY_STREAM_ERROR>();
byte[] pooledArray = ArrayPool<byte>.Shared.Rent(bufferSize);
try
{
var buffer = pooledArray.AsMemory(0, bufferSize);
// error handling omitted..
await feature.TryGetRequestPropertyAsync(
HttpRequestPropertyStreamError,
default,
buffer,
out int _);
var streamError = MemoryMarshal.AsRef<HTTP_REQUEST_PROPERTY_STREAM_ERROR>(buffer.Span);
return streamError.ErrorCode;
}
finally
{
ArrayPool<byte>.Shared.Return(pooledArray);
}
}