commit
6629236a0e
|
|
@ -1,4 +1,4 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Description>ASP.NET Core components for working with the IIS AspNetCoreModule.</Description>
|
<Description>ASP.NET Core components for working with the IIS AspNetCoreModule.</Description>
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
[DllImport(AspNetCoreModuleDll)]
|
[DllImport(AspNetCoreModuleDll)]
|
||||||
private static extern int http_get_server_variable(
|
private static extern int http_get_server_variable(
|
||||||
IntPtr pInProcessHandler,
|
IntPtr pInProcessHandler,
|
||||||
[MarshalAs(UnmanagedType.AnsiBStr)] string variableName,
|
[MarshalAs(UnmanagedType.LPStr)] string variableName,
|
||||||
[MarshalAs(UnmanagedType.BStr)] out string value);
|
[MarshalAs(UnmanagedType.BStr)] out string value);
|
||||||
|
|
||||||
[DllImport(AspNetCoreModuleDll)]
|
[DllImport(AspNetCoreModuleDll)]
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
IHttpRequestFeature,
|
IHttpRequestFeature,
|
||||||
IHttpResponseFeature,
|
IHttpResponseFeature,
|
||||||
IHttpUpgradeFeature,
|
IHttpUpgradeFeature,
|
||||||
IHttpConnectionFeature,
|
|
||||||
IHttpRequestLifetimeFeature,
|
IHttpRequestLifetimeFeature,
|
||||||
IHttpRequestIdentifierFeature,
|
|
||||||
IHttpAuthenticationFeature,
|
IHttpAuthenticationFeature,
|
||||||
IServerVariablesFeature
|
IServerVariablesFeature
|
||||||
{
|
{
|
||||||
|
|
@ -183,49 +181,12 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
|
|
||||||
bool IHttpResponseFeature.HasStarted => HasResponseStarted;
|
bool IHttpResponseFeature.HasStarted => HasResponseStarted;
|
||||||
|
|
||||||
// The UpgradeAvailable Feature is set on the first request to the server.
|
bool IHttpUpgradeFeature.IsUpgradableRequest => _server.IsWebSocketAvailible(_pInProcessHandler);
|
||||||
bool IHttpUpgradeFeature.IsUpgradableRequest => _websocketAvailability == WebsocketAvailabilityStatus.Available;
|
|
||||||
|
|
||||||
bool IFeatureCollection.IsReadOnly => false;
|
bool IFeatureCollection.IsReadOnly => false;
|
||||||
|
|
||||||
int IFeatureCollection.Revision => _featureRevision;
|
int IFeatureCollection.Revision => _featureRevision;
|
||||||
|
|
||||||
IPAddress IHttpConnectionFeature.RemoteIpAddress
|
|
||||||
{
|
|
||||||
get => RemoteIpAddress;
|
|
||||||
set => RemoteIpAddress = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
IPAddress IHttpConnectionFeature.LocalIpAddress
|
|
||||||
{
|
|
||||||
get => LocalIpAddress;
|
|
||||||
set => LocalIpAddress = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
int IHttpConnectionFeature.RemotePort
|
|
||||||
{
|
|
||||||
get => RemotePort;
|
|
||||||
set => RemotePort = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
int IHttpConnectionFeature.LocalPort
|
|
||||||
{
|
|
||||||
get => LocalPort;
|
|
||||||
set => LocalPort = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
string IHttpConnectionFeature.ConnectionId
|
|
||||||
{
|
|
||||||
get => RequestConnectionId;
|
|
||||||
set => RequestConnectionId = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
string IHttpRequestIdentifierFeature.TraceIdentifier
|
|
||||||
{
|
|
||||||
get => TraceIdentifier;
|
|
||||||
set => TraceIdentifier = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClaimsPrincipal IHttpAuthenticationFeature.User
|
ClaimsPrincipal IHttpAuthenticationFeature.User
|
||||||
{
|
{
|
||||||
get => User;
|
get => User;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
|
{
|
||||||
|
internal partial class IISHttpContext : IHttpConnectionFeature
|
||||||
|
{
|
||||||
|
IPAddress IHttpConnectionFeature.RemoteIpAddress
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (RemoteIpAddress == null)
|
||||||
|
{
|
||||||
|
InitializeRemoteEndpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
return RemoteIpAddress;
|
||||||
|
}
|
||||||
|
set => RemoteIpAddress = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress IHttpConnectionFeature.LocalIpAddress
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (LocalIpAddress == null)
|
||||||
|
{
|
||||||
|
InitializeLocalEndpoint();
|
||||||
|
}
|
||||||
|
return LocalIpAddress;
|
||||||
|
}
|
||||||
|
set => LocalIpAddress = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IHttpConnectionFeature.RemotePort
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (RemoteIpAddress == null)
|
||||||
|
{
|
||||||
|
InitializeRemoteEndpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
return RemotePort;
|
||||||
|
}
|
||||||
|
set => RemotePort = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IHttpConnectionFeature.LocalPort
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (LocalIpAddress == null)
|
||||||
|
{
|
||||||
|
InitializeLocalEndpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
return LocalPort;
|
||||||
|
}
|
||||||
|
set => LocalPort = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
string IHttpConnectionFeature.ConnectionId
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (RequestConnectionId == null)
|
||||||
|
{
|
||||||
|
InitializeConnectionId();
|
||||||
|
}
|
||||||
|
|
||||||
|
return RequestConnectionId;
|
||||||
|
}
|
||||||
|
set => RequestConnectionId = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeLocalEndpoint()
|
||||||
|
{
|
||||||
|
var localEndPoint = GetLocalEndPoint();
|
||||||
|
LocalIpAddress = localEndPoint.GetIPAddress();
|
||||||
|
LocalPort = localEndPoint.GetPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeRemoteEndpoint()
|
||||||
|
{
|
||||||
|
var remoteEndPoint = GetRemoteEndPoint();
|
||||||
|
RemoteIpAddress = remoteEndPoint.GetIPAddress();
|
||||||
|
RemotePort = remoteEndPoint.GetPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeConnectionId()
|
||||||
|
{
|
||||||
|
RequestConnectionId = ConnectionId.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
|
{
|
||||||
|
internal partial class IISHttpContext : IHttpRequestIdentifierFeature
|
||||||
|
{
|
||||||
|
string IHttpRequestIdentifierFeature.TraceIdentifier
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (TraceIdentifier == null)
|
||||||
|
{
|
||||||
|
InitializeHttpRequestIdentifierFeature();
|
||||||
|
}
|
||||||
|
|
||||||
|
return TraceIdentifier;
|
||||||
|
}
|
||||||
|
set => TraceIdentifier = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void InitializeHttpRequestIdentifierFeature()
|
||||||
|
{
|
||||||
|
// Copied from WebListener
|
||||||
|
// This is the base GUID used by HTTP.SYS for generating the activity ID.
|
||||||
|
// HTTP.SYS overwrites the first 8 bytes of the base GUID with RequestId to generate ETW activity ID.
|
||||||
|
// The requestId should be set by the NativeRequestContext
|
||||||
|
var guid = new Guid(0xffcb4c93, 0xa57f, 0x453c, 0xb6, 0x3f, 0x84, 0x71, 0xc, 0x79, 0x67, 0xbb);
|
||||||
|
*((ulong*)&guid) = RequestId;
|
||||||
|
|
||||||
|
// TODO: Also make this not slow
|
||||||
|
TraceIdentifier = guid.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -28,11 +28,11 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
private const int PauseWriterThreshold = 65536;
|
private const int PauseWriterThreshold = 65536;
|
||||||
private const int ResumeWriterTheshold = PauseWriterThreshold / 2;
|
private const int ResumeWriterTheshold = PauseWriterThreshold / 2;
|
||||||
|
|
||||||
// TODO make this static again.
|
|
||||||
private static WebsocketAvailabilityStatus _websocketAvailability = WebsocketAvailabilityStatus.Uninitialized;
|
|
||||||
|
|
||||||
protected readonly IntPtr _pInProcessHandler;
|
protected readonly IntPtr _pInProcessHandler;
|
||||||
|
|
||||||
|
private readonly IISOptions _options;
|
||||||
|
|
||||||
private bool _reading; // To know whether we are currently in a read operation.
|
private bool _reading; // To know whether we are currently in a read operation.
|
||||||
private volatile bool _hasResponseStarted;
|
private volatile bool _hasResponseStarted;
|
||||||
|
|
||||||
|
|
@ -60,116 +60,14 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
private const string NtlmString = "NTLM";
|
private const string NtlmString = "NTLM";
|
||||||
private const string NegotiateString = "Negotiate";
|
private const string NegotiateString = "Negotiate";
|
||||||
private const string BasicString = "Basic";
|
private const string BasicString = "Basic";
|
||||||
private const string WebSocketVersionString = "WEBSOCKET_VERSION";
|
|
||||||
|
|
||||||
internal unsafe IISHttpContext(MemoryPool<byte> memoryPool, IntPtr pInProcessHandler, IISOptions options, IISHttpServer server)
|
internal unsafe IISHttpContext(MemoryPool<byte> memoryPool, IntPtr pInProcessHandler, IISOptions options, IISHttpServer server)
|
||||||
: base((HttpApiTypes.HTTP_REQUEST*)NativeMethods.HttpGetRawRequest(pInProcessHandler))
|
: base((HttpApiTypes.HTTP_REQUEST*)NativeMethods.HttpGetRawRequest(pInProcessHandler))
|
||||||
{
|
{
|
||||||
_thisHandle = GCHandle.Alloc(this);
|
|
||||||
|
|
||||||
_memoryPool = memoryPool;
|
_memoryPool = memoryPool;
|
||||||
_pInProcessHandler = pInProcessHandler;
|
_pInProcessHandler = pInProcessHandler;
|
||||||
|
_options = options;
|
||||||
_server = server;
|
_server = server;
|
||||||
|
|
||||||
NativeMethods.HttpSetManagedContext(pInProcessHandler, (IntPtr)_thisHandle);
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
Method = GetVerb();
|
|
||||||
|
|
||||||
RawTarget = GetRawUrl();
|
|
||||||
// TODO version is slow.
|
|
||||||
HttpVersion = GetVersion();
|
|
||||||
Scheme = SslStatus != SslStatus.Insecure ? Constants.HttpsScheme : Constants.HttpScheme;
|
|
||||||
KnownMethod = VerbId;
|
|
||||||
|
|
||||||
var originalPath = RequestUriBuilder.DecodeAndUnescapePath(GetRawUrlInBytes());
|
|
||||||
|
|
||||||
if (KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawTarget, "*", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
PathBase = string.Empty;
|
|
||||||
Path = string.Empty;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Path and pathbase are unescaped by RequestUriBuilder
|
|
||||||
// The UsePathBase middleware will modify the pathbase and path correctly
|
|
||||||
PathBase = string.Empty;
|
|
||||||
Path = originalPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cookedUrl = GetCookedUrl();
|
|
||||||
QueryString = cookedUrl.GetQueryString() ?? string.Empty;
|
|
||||||
|
|
||||||
// TODO: Avoid using long.ToString, it's pretty slow
|
|
||||||
RequestConnectionId = ConnectionId.ToString(CultureInfo.InvariantCulture);
|
|
||||||
|
|
||||||
// Copied from WebListener
|
|
||||||
// This is the base GUID used by HTTP.SYS for generating the activity ID.
|
|
||||||
// HTTP.SYS overwrites the first 8 bytes of the base GUID with RequestId to generate ETW activity ID.
|
|
||||||
// The requestId should be set by the NativeRequestContext
|
|
||||||
var guid = new Guid(0xffcb4c93, 0xa57f, 0x453c, 0xb6, 0x3f, 0x84, 0x71, 0xc, 0x79, 0x67, 0xbb);
|
|
||||||
*((ulong*)&guid) = RequestId;
|
|
||||||
|
|
||||||
// TODO: Also make this not slow
|
|
||||||
TraceIdentifier = guid.ToString();
|
|
||||||
|
|
||||||
var localEndPoint = GetLocalEndPoint();
|
|
||||||
LocalIpAddress = localEndPoint.GetIPAddress();
|
|
||||||
LocalPort = localEndPoint.GetPort();
|
|
||||||
|
|
||||||
var remoteEndPoint = GetRemoteEndPoint();
|
|
||||||
RemoteIpAddress = remoteEndPoint.GetIPAddress();
|
|
||||||
RemotePort = remoteEndPoint.GetPort();
|
|
||||||
StatusCode = 200;
|
|
||||||
|
|
||||||
RequestHeaders = new RequestHeaders(this);
|
|
||||||
HttpResponseHeaders = new HeaderCollection();
|
|
||||||
ResponseHeaders = HttpResponseHeaders;
|
|
||||||
|
|
||||||
if (options.ForwardWindowsAuthentication)
|
|
||||||
{
|
|
||||||
WindowsUser = GetWindowsPrincipal();
|
|
||||||
if (options.AutomaticAuthentication)
|
|
||||||
{
|
|
||||||
User = WindowsUser;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ResetFeatureCollection();
|
|
||||||
|
|
||||||
// Check if the Http upgrade feature is available in IIS.
|
|
||||||
// To check this, we can look at the server variable WEBSOCKET_VERSION
|
|
||||||
// And see if there is a version. Same check that Katana did:
|
|
||||||
// https://github.com/aspnet/AspNetKatana/blob/9f6e09af6bf203744feb5347121fe25f6eec06d8/src/Microsoft.Owin.Host.SystemWeb/OwinAppContext.cs#L125
|
|
||||||
// Actively not locking here as acquiring a lock on every request will hurt perf more than checking the
|
|
||||||
// server variables a few extra times if a bunch of requests hit the server at the same time.
|
|
||||||
if (_websocketAvailability == WebsocketAvailabilityStatus.Uninitialized)
|
|
||||||
{
|
|
||||||
var webSocketsAvailable = NativeMethods.HttpTryGetServerVariable(pInProcessHandler, WebSocketVersionString, out var webSocketsSupported)
|
|
||||||
&& !string.IsNullOrEmpty(webSocketsSupported);
|
|
||||||
|
|
||||||
_websocketAvailability = webSocketsAvailable ?
|
|
||||||
WebsocketAvailabilityStatus.Available :
|
|
||||||
WebsocketAvailabilityStatus.NotAvailable;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_websocketAvailability == WebsocketAvailabilityStatus.NotAvailable)
|
|
||||||
{
|
|
||||||
_currentIHttpUpgradeFeature = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RequestBody = new IISHttpRequestBody(this);
|
|
||||||
ResponseBody = new IISHttpResponseBody(this);
|
|
||||||
|
|
||||||
Input = new Pipe(new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.ThreadPool, minimumSegmentSize: MinAllocBufferSize));
|
|
||||||
var pipe = new Pipe(new PipeOptions(
|
|
||||||
_memoryPool,
|
|
||||||
readerScheduler: PipeScheduler.ThreadPool,
|
|
||||||
pauseWriterThreshold: PauseWriterThreshold,
|
|
||||||
resumeWriterThreshold: ResumeWriterTheshold,
|
|
||||||
minimumSegmentSize: MinAllocBufferSize));
|
|
||||||
Output = new OutputProducer(pipe);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Version HttpVersion { get; set; }
|
public Version HttpVersion { get; set; }
|
||||||
|
|
@ -197,7 +95,74 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
public IHeaderDictionary RequestHeaders { get; set; }
|
public IHeaderDictionary RequestHeaders { get; set; }
|
||||||
public IHeaderDictionary ResponseHeaders { get; set; }
|
public IHeaderDictionary ResponseHeaders { get; set; }
|
||||||
private HeaderCollection HttpResponseHeaders { get; set; }
|
private HeaderCollection HttpResponseHeaders { get; set; }
|
||||||
internal HttpApiTypes.HTTP_VERB KnownMethod { get; }
|
internal HttpApiTypes.HTTP_VERB KnownMethod { get; private set; }
|
||||||
|
|
||||||
|
protected void InitializeContext()
|
||||||
|
{
|
||||||
|
_thisHandle = GCHandle.Alloc(this);
|
||||||
|
|
||||||
|
NativeMethods.HttpSetManagedContext(_pInProcessHandler, (IntPtr)_thisHandle);
|
||||||
|
|
||||||
|
Method = GetVerb();
|
||||||
|
|
||||||
|
RawTarget = GetRawUrl();
|
||||||
|
// TODO version is slow.
|
||||||
|
HttpVersion = GetVersion();
|
||||||
|
Scheme = SslStatus != SslStatus.Insecure ? Constants.HttpsScheme : Constants.HttpScheme;
|
||||||
|
KnownMethod = VerbId;
|
||||||
|
StatusCode = 200;
|
||||||
|
|
||||||
|
var originalPath = RequestUriBuilder.DecodeAndUnescapePath(GetRawUrlInBytes());
|
||||||
|
|
||||||
|
if (KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawTarget, "*", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
PathBase = string.Empty;
|
||||||
|
Path = string.Empty;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Path and pathbase are unescaped by RequestUriBuilder
|
||||||
|
// The UsePathBase middleware will modify the pathbase and path correctly
|
||||||
|
PathBase = string.Empty;
|
||||||
|
Path = originalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cookedUrl = GetCookedUrl();
|
||||||
|
QueryString = cookedUrl.GetQueryString() ?? string.Empty;
|
||||||
|
|
||||||
|
RequestHeaders = new RequestHeaders(this);
|
||||||
|
HttpResponseHeaders = new HeaderCollection();
|
||||||
|
ResponseHeaders = HttpResponseHeaders;
|
||||||
|
|
||||||
|
if (_options.ForwardWindowsAuthentication)
|
||||||
|
{
|
||||||
|
WindowsUser = GetWindowsPrincipal();
|
||||||
|
if (_options.AutomaticAuthentication)
|
||||||
|
{
|
||||||
|
User = WindowsUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetFeatureCollection();
|
||||||
|
|
||||||
|
if (!_server.IsWebSocketAvailible(_pInProcessHandler))
|
||||||
|
{
|
||||||
|
_currentIHttpUpgradeFeature = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestBody = new IISHttpRequestBody(this);
|
||||||
|
ResponseBody = new IISHttpResponseBody(this);
|
||||||
|
|
||||||
|
Input = new Pipe(new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.ThreadPool, minimumSegmentSize: MinAllocBufferSize));
|
||||||
|
var pipe = new Pipe(
|
||||||
|
new PipeOptions(
|
||||||
|
_memoryPool,
|
||||||
|
readerScheduler: PipeScheduler.ThreadPool,
|
||||||
|
pauseWriterThreshold: PauseWriterThreshold,
|
||||||
|
resumeWriterThreshold: ResumeWriterTheshold,
|
||||||
|
minimumSegmentSize: MinAllocBufferSize));
|
||||||
|
Output = new OutputProducer(pipe);
|
||||||
|
}
|
||||||
|
|
||||||
public int StatusCode
|
public int StatusCode
|
||||||
{
|
{
|
||||||
|
|
@ -545,12 +510,5 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum WebsocketAvailabilityStatus
|
|
||||||
{
|
|
||||||
Uninitialized = 1,
|
|
||||||
Available,
|
|
||||||
NotAvailable
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
|
|
||||||
public override async Task<bool> ProcessRequestAsync()
|
public override async Task<bool> ProcessRequestAsync()
|
||||||
{
|
{
|
||||||
|
InitializeContext();
|
||||||
|
|
||||||
var context = default(TContext);
|
var context = default(TContext);
|
||||||
var success = true;
|
var success = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
{
|
{
|
||||||
internal class IISHttpServer : IServer
|
internal class IISHttpServer : IServer
|
||||||
{
|
{
|
||||||
|
private const string WebSocketVersionString = "WEBSOCKET_VERSION";
|
||||||
|
|
||||||
private static NativeMethods.PFN_REQUEST_HANDLER _requestHandler = HandleRequest;
|
private static NativeMethods.PFN_REQUEST_HANDLER _requestHandler = HandleRequest;
|
||||||
private static NativeMethods.PFN_SHUTDOWN_HANDLER _shutdownHandler = HandleShutdown;
|
private static NativeMethods.PFN_SHUTDOWN_HANDLER _shutdownHandler = HandleShutdown;
|
||||||
private static NativeMethods.PFN_ASYNC_COMPLETION _onAsyncCompletion = OnAsyncCompletion;
|
private static NativeMethods.PFN_ASYNC_COMPLETION _onAsyncCompletion = OnAsyncCompletion;
|
||||||
|
|
@ -32,8 +34,28 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
private bool Stopping => _stopping == 1;
|
private bool Stopping => _stopping == 1;
|
||||||
private int _outstandingRequests;
|
private int _outstandingRequests;
|
||||||
private readonly TaskCompletionSource<object> _shutdownSignal = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
private readonly TaskCompletionSource<object> _shutdownSignal = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||||
|
private bool? _websocketAvailable;
|
||||||
|
|
||||||
public IFeatureCollection Features { get; } = new FeatureCollection();
|
public IFeatureCollection Features { get; } = new FeatureCollection();
|
||||||
|
|
||||||
|
// TODO: Remove pInProcessHandler argument
|
||||||
|
public bool IsWebSocketAvailible(IntPtr pInProcessHandler)
|
||||||
|
{
|
||||||
|
// Check if the Http upgrade feature is available in IIS.
|
||||||
|
// To check this, we can look at the server variable WEBSOCKET_VERSION
|
||||||
|
// And see if there is a version. Same check that Katana did:
|
||||||
|
// https://github.com/aspnet/AspNetKatana/blob/9f6e09af6bf203744feb5347121fe25f6eec06d8/src/Microsoft.Owin.Host.SystemWeb/OwinAppContext.cs#L125
|
||||||
|
// Actively not locking here as acquiring a lock on every request will hurt perf more than checking the
|
||||||
|
// server variables a few extra times if a bunch of requests hit the server at the same time.
|
||||||
|
if (!_websocketAvailable.HasValue)
|
||||||
|
{
|
||||||
|
_websocketAvailable = NativeMethods.HttpTryGetServerVariable(pInProcessHandler, WebSocketVersionString, out var webSocketsSupported)
|
||||||
|
&& !string.IsNullOrEmpty(webSocketsSupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _websocketAvailable.Value;
|
||||||
|
}
|
||||||
|
|
||||||
public IISHttpServer(IApplicationLifetime applicationLifetime, IAuthenticationSchemeProvider authentication, IOptions<IISOptions> options)
|
public IISHttpServer(IApplicationLifetime applicationLifetime, IAuthenticationSchemeProvider authentication, IOptions<IISOptions> options)
|
||||||
{
|
{
|
||||||
_applicationLifetime = applicationLifetime;
|
_applicationLifetime = applicationLifetime;
|
||||||
|
|
@ -124,13 +146,17 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
|
|
||||||
var context = server._iisContextFactory.CreateHttpContext(pInProcessHandler);
|
var context = server._iisContextFactory.CreateHttpContext(pInProcessHandler);
|
||||||
|
|
||||||
var task = Task.Run(() => context.ProcessRequestAsync());
|
ThreadPool.QueueUserWorkItem(state => _ = HandleRequest((IISHttpContext)state), context);
|
||||||
|
|
||||||
task.ContinueWith((t, state) => CompleteRequest((IISHttpContext)state, t), context);
|
|
||||||
|
|
||||||
return NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_PENDING;
|
return NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_PENDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task HandleRequest(IISHttpContext context)
|
||||||
|
{
|
||||||
|
var result = await context.ProcessRequestAsync();
|
||||||
|
CompleteRequest(context, result);
|
||||||
|
}
|
||||||
|
|
||||||
private static bool HandleShutdown(IntPtr pvRequestContext)
|
private static bool HandleShutdown(IntPtr pvRequestContext)
|
||||||
{
|
{
|
||||||
var server = (IISHttpServer)GCHandle.FromIntPtr(pvRequestContext).Target;
|
var server = (IISHttpServer)GCHandle.FromIntPtr(pvRequestContext).Target;
|
||||||
|
|
@ -145,10 +171,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
return NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_PENDING;
|
return NativeMethods.REQUEST_NOTIFICATION_STATUS.RQ_NOTIFICATION_PENDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CompleteRequest(IISHttpContext context, Task<bool> completedTask)
|
private static void CompleteRequest(IISHttpContext context, bool result)
|
||||||
{
|
{
|
||||||
// Post completion after completing the request to resume the state machine
|
// Post completion after completing the request to resume the state machine
|
||||||
context.PostCompletion(ConvertRequestCompletionResults(completedTask.Result));
|
context.PostCompletion(ConvertRequestCompletionResults(result));
|
||||||
|
|
||||||
if (Interlocked.Decrement(ref context.Server._outstandingRequests) == 0 && context.Server.Stopping)
|
if (Interlocked.Decrement(ref context.Server._outstandingRequests) == 0 && context.Server.Stopping)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,7 @@ http_read_request_bytes(
|
||||||
_Out_ BOOL* pfCompletionPending
|
_Out_ BOOL* pfCompletionPending
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
if (pInProcessHandler == NULL)
|
if (pInProcessHandler == NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -206,19 +206,27 @@ http_read_request_bytes(
|
||||||
}
|
}
|
||||||
IHttpRequest *pHttpRequest = (IHttpRequest*)pInProcessHandler->QueryHttpContext()->GetRequest();
|
IHttpRequest *pHttpRequest = (IHttpRequest*)pInProcessHandler->QueryHttpContext()->GetRequest();
|
||||||
|
|
||||||
BOOL fAsync = TRUE;
|
// Check if there is anything to read
|
||||||
|
if (pHttpRequest->GetRemainingEntityBytes() > 0)
|
||||||
hr = pHttpRequest->ReadEntityBody(
|
|
||||||
pvBuffer,
|
|
||||||
dwCbBuffer,
|
|
||||||
fAsync,
|
|
||||||
pdwBytesReceived,
|
|
||||||
pfCompletionPending);
|
|
||||||
|
|
||||||
if (hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF))
|
|
||||||
{
|
{
|
||||||
// We reached the end of the data
|
BOOL fAsync = TRUE;
|
||||||
hr = S_OK;
|
hr = pHttpRequest->ReadEntityBody(
|
||||||
|
pvBuffer,
|
||||||
|
dwCbBuffer,
|
||||||
|
fAsync,
|
||||||
|
pdwBytesReceived,
|
||||||
|
pfCompletionPending);
|
||||||
|
|
||||||
|
if (hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF))
|
||||||
|
{
|
||||||
|
// We reached the end of the data
|
||||||
|
hr = S_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*pdwBytesReceived = 0;
|
||||||
|
*pfCompletionPending = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue