Perf
This commit is contained in:
parent
a91238d8d2
commit
dca31cc6f6
|
|
@ -0,0 +1,51 @@
|
||||||
|
// 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 System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Benchmarks.Middleware
|
||||||
|
{
|
||||||
|
public class PlaintextMiddleware
|
||||||
|
{
|
||||||
|
private static readonly PathString _path = new PathString("/plaintext");
|
||||||
|
private static readonly byte[] _helloWorldPayload = Encoding.UTF8.GetBytes("Hello, World!");
|
||||||
|
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
|
||||||
|
public PlaintextMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Invoke(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
if (httpContext.Request.Path.StartsWithSegments(_path, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return WriteResponse(httpContext.Response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _next(httpContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task WriteResponse(HttpResponse response)
|
||||||
|
{
|
||||||
|
var payloadLength = _helloWorldPayload.Length;
|
||||||
|
response.StatusCode = 200;
|
||||||
|
response.ContentType = "text/plain";
|
||||||
|
response.ContentLength = payloadLength;
|
||||||
|
return response.Body.WriteAsync(_helloWorldPayload, 0, payloadLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PlaintextMiddlewareExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UsePlainText(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseMiddleware<PlaintextMiddleware>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Benchmarks.Middleware;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
|
@ -18,6 +19,7 @@ namespace NativeIISSample
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAuthenticationSchemeProvider authSchemeProvider)
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAuthenticationSchemeProvider authSchemeProvider)
|
||||||
{
|
{
|
||||||
|
app.UsePlainText();
|
||||||
app.Run(async (context) =>
|
app.Run(async (context) =>
|
||||||
{
|
{
|
||||||
context.Response.ContentType = "text/plain";
|
context.Response.ContentType = "text/plain";
|
||||||
|
|
|
||||||
|
|
@ -60,4 +60,11 @@ SAFEIsDigit(UCHAR c)
|
||||||
return isdigit( c );
|
return isdigit( c );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define __RETURN_GLE_FAIL(str) return HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
#define __RETURN_HR_FAIL(hr, str) do { HRESULT __hr = (hr); return __hr; } while (0, 0)
|
||||||
|
|
||||||
|
#define RETURN_IF_FAILED(hr) do { HRESULT __hrRet = hr; if (FAILED(__hrRet)) { __RETURN_HR_FAIL(__hrRet, #hr); }} while (0, 0)
|
||||||
|
#define RETURN_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { __RETURN_GLE_FAIL(#ptr); }} while (0, 0)
|
||||||
|
#define RETURN_IF_HANDLE_INVALID(handle) do { HANDLE __hRet = (handle); if (__hRet == INVALID_HANDLE_VALUE) { __RETURN_GLE_FAIL(#handle); }} while (0, 0)
|
||||||
|
|
||||||
#endif // _MACROS_H
|
#endif // _MACROS_H
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
{
|
||||||
|
InitializeRemoteEndpoint();
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
InitializeRemoteEndpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
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,8 +28,6 @@ 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;
|
||||||
|
|
||||||
|
|
@ -60,7 +58,6 @@ 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))
|
||||||
|
|
@ -72,103 +69,64 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
_server = server;
|
_server = server;
|
||||||
|
|
||||||
NativeMethods.HttpSetManagedContext(pInProcessHandler, (IntPtr)_thisHandle);
|
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;
|
||||||
|
StatusCode = 200;
|
||||||
|
|
||||||
|
var originalPath = RequestUriBuilder.DecodeAndUnescapePath(GetRawUrlInBytes());
|
||||||
|
|
||||||
|
if (KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawTarget, "*", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
Method = GetVerb();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
RawTarget = GetRawUrl();
|
var cookedUrl = GetCookedUrl();
|
||||||
// TODO version is slow.
|
QueryString = cookedUrl.GetQueryString() ?? string.Empty;
|
||||||
HttpVersion = GetVersion();
|
|
||||||
Scheme = SslStatus != SslStatus.Insecure ? Constants.HttpsScheme : Constants.HttpScheme;
|
|
||||||
KnownMethod = VerbId;
|
|
||||||
|
|
||||||
var originalPath = RequestUriBuilder.DecodeAndUnescapePath(GetRawUrlInBytes());
|
RequestHeaders = new RequestHeaders(this);
|
||||||
|
HttpResponseHeaders = new HeaderCollection();
|
||||||
|
ResponseHeaders = HttpResponseHeaders;
|
||||||
|
|
||||||
if (KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawTarget, "*", StringComparison.Ordinal))
|
if (options.ForwardWindowsAuthentication)
|
||||||
|
{
|
||||||
|
WindowsUser = GetWindowsPrincipal();
|
||||||
|
if (options.AutomaticAuthentication)
|
||||||
{
|
{
|
||||||
PathBase = string.Empty;
|
User = WindowsUser;
|
||||||
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();
|
ResetFeatureCollection();
|
||||||
QueryString = cookedUrl.GetQueryString() ?? string.Empty;
|
|
||||||
|
|
||||||
// TODO: Avoid using long.ToString, it's pretty slow
|
if (_server.IsWebSocketAvailible(pInProcessHandler))
|
||||||
RequestConnectionId = ConnectionId.ToString(CultureInfo.InvariantCulture);
|
{
|
||||||
|
_currentIHttpUpgradeFeature = null;
|
||||||
// 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);
|
RequestBody = new IISHttpRequestBody(this);
|
||||||
ResponseBody = new IISHttpResponseBody(this);
|
ResponseBody = new IISHttpResponseBody(this);
|
||||||
|
|
||||||
Input = new Pipe(new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.ThreadPool, minimumSegmentSize: MinAllocBufferSize));
|
Input = new Pipe(new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.ThreadPool, minimumSegmentSize: MinAllocBufferSize));
|
||||||
var pipe = new Pipe(new PipeOptions(
|
var pipe = new Pipe(
|
||||||
_memoryPool,
|
new PipeOptions(
|
||||||
readerScheduler: PipeScheduler.ThreadPool,
|
_memoryPool,
|
||||||
pauseWriterThreshold: PauseWriterThreshold,
|
readerScheduler: PipeScheduler.ThreadPool,
|
||||||
resumeWriterThreshold: ResumeWriterTheshold,
|
pauseWriterThreshold: PauseWriterThreshold,
|
||||||
minimumSegmentSize: MinAllocBufferSize));
|
resumeWriterThreshold: ResumeWriterTheshold,
|
||||||
|
minimumSegmentSize: MinAllocBufferSize));
|
||||||
Output = new OutputProducer(pipe);
|
Output = new OutputProducer(pipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -545,12 +503,5 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum WebsocketAvailabilityStatus
|
|
||||||
{
|
|
||||||
Uninitialized = 1,
|
|
||||||
Available,
|
|
||||||
NotAvailable
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -122,6 +144,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
|
||||||
var server = (IISHttpServer)GCHandle.FromIntPtr(pvRequestContext).Target;
|
var server = (IISHttpServer)GCHandle.FromIntPtr(pvRequestContext).Target;
|
||||||
Interlocked.Increment(ref server._outstandingRequests);
|
Interlocked.Increment(ref server._outstandingRequests);
|
||||||
|
|
||||||
|
// TODO: Add try/catch and logging here
|
||||||
var context = server._iisContextFactory.CreateHttpContext(pInProcessHandler);
|
var context = server._iisContextFactory.CreateHttpContext(pInProcessHandler);
|
||||||
|
|
||||||
var task = Task.Run(() => context.ProcessRequestAsync());
|
var task = Task.Run(() => context.ProcessRequestAsync());
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue