Reduce usage of static inprocess application (#962)
This commit is contained in:
parent
d3d257b90e
commit
bc345bc8e7
|
|
@ -9,8 +9,9 @@
|
|||
// Initialization export
|
||||
//
|
||||
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
|
||||
VOID
|
||||
HRESULT
|
||||
register_callbacks(
|
||||
_In_ IN_PROCESS_APPLICATION* pInProcessApplication,
|
||||
_In_ PFN_REQUEST_HANDLER request_handler,
|
||||
_In_ PFN_SHUTDOWN_HANDLER shutdown_handler,
|
||||
_In_ PFN_MANAGED_CONTEXT_HANDLER async_completion_handler,
|
||||
|
|
@ -18,13 +19,20 @@ register_callbacks(
|
|||
_In_ VOID* pvShutdownHandlerContext
|
||||
)
|
||||
{
|
||||
IN_PROCESS_APPLICATION::GetInstance()->SetCallbackHandles(
|
||||
if (pInProcessApplication == NULL)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
pInProcessApplication->SetCallbackHandles(
|
||||
request_handler,
|
||||
shutdown_handler,
|
||||
async_completion_handler,
|
||||
pvRequstHandlerContext,
|
||||
pvShutdownHandlerContext
|
||||
);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
|
||||
|
|
@ -150,12 +158,12 @@ http_get_completion_info(
|
|||
}
|
||||
|
||||
//
|
||||
// todo: we should not rely on IN_PROCESS_APPLICATION::GetInstance()
|
||||
// the signature should be changed. application's based address should be passed in
|
||||
//
|
||||
|
||||
struct IISConfigurationData
|
||||
{
|
||||
IN_PROCESS_APPLICATION* pInProcessApplication;
|
||||
BSTR pwzFullApplicationPath;
|
||||
BSTR pwzVirtualApplicationPath;
|
||||
BOOL fWindowsAuthEnabled;
|
||||
|
|
@ -169,16 +177,15 @@ http_get_application_properties(
|
|||
_In_ IISConfigurationData* pIISCofigurationData
|
||||
)
|
||||
{
|
||||
REQUESTHANDLER_CONFIG* pConfiguration = NULL;
|
||||
IN_PROCESS_APPLICATION* pApplication = IN_PROCESS_APPLICATION::GetInstance();
|
||||
|
||||
if (pApplication == NULL)
|
||||
auto pInProcessApplication = IN_PROCESS_APPLICATION::GetInstance();
|
||||
if (pInProcessApplication == NULL)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
pConfiguration = pApplication->QueryConfig();
|
||||
auto pConfiguration = pInProcessApplication->QueryConfig();
|
||||
|
||||
pIISCofigurationData->pInProcessApplication = pInProcessApplication;
|
||||
pIISCofigurationData->pwzFullApplicationPath = SysAllocString(pConfiguration->QueryApplicationPhysicalPath()->QueryStr());
|
||||
pIISCofigurationData->pwzVirtualApplicationPath = SysAllocString(pConfiguration->QueryApplicationVirtualPath()->QueryStr());
|
||||
pIISCofigurationData->fWindowsAuthEnabled = pConfiguration->QueryWindowsAuthEnabled();
|
||||
|
|
@ -433,17 +440,29 @@ http_get_authentication_information(
|
|||
}
|
||||
|
||||
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
|
||||
VOID
|
||||
http_stop_calls_into_managed()
|
||||
HRESULT
|
||||
http_stop_calls_into_managed(_In_ IN_PROCESS_APPLICATION* pInProcessApplication)
|
||||
{
|
||||
IN_PROCESS_APPLICATION::GetInstance()->StopCallsIntoManaged();
|
||||
if (pInProcessApplication == NULL)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
pInProcessApplication->StopCallsIntoManaged();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
|
||||
VOID
|
||||
http_stop_incoming_requests()
|
||||
HRESULT
|
||||
http_stop_incoming_requests(_In_ IN_PROCESS_APPLICATION* pInProcessApplication)
|
||||
{
|
||||
IN_PROCESS_APPLICATION::GetInstance()->StopIncomingRequests();
|
||||
if (pInProcessApplication == NULL)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
pInProcessApplication->StopIncomingRequests();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
// 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.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct IISConfigurationData
|
||||
internal struct IISConfigurationData
|
||||
{
|
||||
public IntPtr pNativeApplication;
|
||||
[MarshalAs(UnmanagedType.BStr)]
|
||||
public string pwzFullApplicationPath;
|
||||
[MarshalAs(UnmanagedType.BStr)]
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Builder;
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||
|
|
@ -27,8 +28,9 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
|||
private readonly MemoryPool<byte> _memoryPool = new SlabMemoryPool();
|
||||
private GCHandle _httpServerHandle;
|
||||
private readonly IApplicationLifetime _applicationLifetime;
|
||||
private readonly IAuthenticationSchemeProvider _authentication;
|
||||
private readonly ILogger<IISHttpServer> _logger;
|
||||
private readonly IISServerOptions _options;
|
||||
private readonly IISNativeApplication _nativeApplication;
|
||||
|
||||
private volatile int _stopping;
|
||||
private bool Stopping => _stopping == 1;
|
||||
|
|
@ -56,10 +58,17 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
|||
return _websocketAvailable.Value;
|
||||
}
|
||||
|
||||
public IISHttpServer(IApplicationLifetime applicationLifetime, IAuthenticationSchemeProvider authentication, IOptions<IISServerOptions> options)
|
||||
public IISHttpServer(
|
||||
IISNativeApplication nativeApplication,
|
||||
IApplicationLifetime applicationLifetime,
|
||||
IAuthenticationSchemeProvider authentication,
|
||||
IOptions<IISServerOptions> options,
|
||||
ILogger<IISHttpServer> logger
|
||||
)
|
||||
{
|
||||
_nativeApplication = nativeApplication;
|
||||
_applicationLifetime = applicationLifetime;
|
||||
_authentication = authentication;
|
||||
_logger = logger;
|
||||
_options = options.Value;
|
||||
|
||||
if (_options.ForwardWindowsAuthentication)
|
||||
|
|
@ -73,10 +82,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
|||
_httpServerHandle = GCHandle.Alloc(this);
|
||||
|
||||
_iisContextFactory = new IISContextFactory<TContext>(_memoryPool, application, _options, this);
|
||||
|
||||
// Start the server by registering the callback
|
||||
NativeMethods.HttpRegisterCallbacks(_requestHandler, _shutdownHandler, _onAsyncCompletion, (IntPtr)_httpServerHandle, (IntPtr)_httpServerHandle);
|
||||
|
||||
_nativeApplication.RegisterCallbacks(_requestHandler, _shutdownHandler, _onAsyncCompletion, (IntPtr)_httpServerHandle, (IntPtr)_httpServerHandle);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +92,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
|||
{
|
||||
cancellationToken.Register(() =>
|
||||
{
|
||||
NativeMethods.HttpStopCallsIntoManaged();
|
||||
_nativeApplication.StopCallsIntoManaged();
|
||||
_shutdownSignal.TrySetResult(null);
|
||||
});
|
||||
}
|
||||
|
|
@ -98,7 +104,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
|||
}
|
||||
|
||||
// First call back into native saying "DON'T SEND ME ANY MORE REQUESTS"
|
||||
NativeMethods.HttpStopIncomingRequests();
|
||||
_nativeApplication.StopIncomingRequests();
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -110,7 +116,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
|||
else
|
||||
{
|
||||
// We have drained all requests. Block any callbacks into managed at this point.
|
||||
NativeMethods.HttpStopCallsIntoManaged();
|
||||
_nativeApplication.StopCallsIntoManaged();
|
||||
_shutdownSignal.TrySetResult(null);
|
||||
}
|
||||
}
|
||||
|
|
@ -127,7 +133,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
|||
_stopping = 1;
|
||||
|
||||
// Block any more calls into managed from native as we are unloading.
|
||||
NativeMethods.HttpStopCallsIntoManaged();
|
||||
_nativeApplication.StopCallsIntoManaged();
|
||||
_shutdownSignal.TrySetResult(null);
|
||||
|
||||
if (_httpServerHandle.IsAllocated)
|
||||
|
|
@ -136,8 +142,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
|||
}
|
||||
|
||||
_memoryPool.Dispose();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
_nativeApplication.Dispose();
|
||||
}
|
||||
|
||||
private static NativeMethods.REQUEST_NOTIFICATION_STATUS HandleRequest(IntPtr pInProcessHandler, IntPtr pvRequestContext)
|
||||
|
|
@ -155,8 +160,19 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
|||
|
||||
private static async Task HandleRequest(IISHttpContext context)
|
||||
{
|
||||
var result = await context.ProcessRequestAsync();
|
||||
CompleteRequest(context, result);
|
||||
bool successfulRequest = false;
|
||||
try
|
||||
{
|
||||
successfulRequest = await context.ProcessRequestAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
context.Server._logger.LogError("Exception in ProcessRequestAsync", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
CompleteRequest(context, successfulRequest);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool HandleShutdown(IntPtr pvRequestContext)
|
||||
|
|
@ -181,7 +197,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
|||
if (Interlocked.Decrement(ref context.Server._outstandingRequests) == 0 && context.Server.Stopping)
|
||||
{
|
||||
// All requests have been drained.
|
||||
NativeMethods.HttpStopCallsIntoManaged();
|
||||
context.Server._nativeApplication.StopCallsIntoManaged();
|
||||
context.Server._shutdownSignal.TrySetResult(null);
|
||||
}
|
||||
|
||||
|
|
@ -215,12 +231,6 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
|||
return new IISHttpContextOfT<T>(_memoryPool, _application, pInProcessHandler, _options, _server);
|
||||
}
|
||||
}
|
||||
|
||||
~IISHttpServer()
|
||||
{
|
||||
// If this finalize is invoked, try our best to block all calls into managed.
|
||||
NativeMethods.HttpStopCallsIntoManaged();
|
||||
}
|
||||
}
|
||||
|
||||
// Over engineering to avoid allocations...
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||
{
|
||||
internal class IISNativeApplication
|
||||
{
|
||||
private readonly IntPtr _nativeApplication;
|
||||
|
||||
public IISNativeApplication(IntPtr nativeApplication)
|
||||
{
|
||||
_nativeApplication = nativeApplication;
|
||||
}
|
||||
|
||||
public void StopIncomingRequests()
|
||||
{
|
||||
NativeMethods.HttpStopIncomingRequests(_nativeApplication);
|
||||
}
|
||||
|
||||
public void StopCallsIntoManaged()
|
||||
{
|
||||
NativeMethods.HttpStopCallsIntoManaged(_nativeApplication);
|
||||
}
|
||||
|
||||
public void RegisterCallbacks(
|
||||
NativeMethods.PFN_REQUEST_HANDLER requestHandler,
|
||||
NativeMethods.PFN_SHUTDOWN_HANDLER shutdownHandler,
|
||||
NativeMethods.PFN_ASYNC_COMPLETION onAsyncCompletion,
|
||||
IntPtr requestContext,
|
||||
IntPtr shutdownContext)
|
||||
{
|
||||
NativeMethods.HttpRegisterCallbacks(_nativeApplication, requestHandler, shutdownHandler, onAsyncCompletion, requestContext, shutdownContext);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~IISNativeApplication()
|
||||
{
|
||||
// If this finalize is invoked, try our best to block all calls into managed.
|
||||
StopCallsIntoManaged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Server.IIS
|
|||
private static extern void http_indicate_completion(IntPtr pInProcessHandler, REQUEST_NOTIFICATION_STATUS notificationStatus);
|
||||
|
||||
[DllImport(AspNetCoreModuleDll)]
|
||||
private static extern void register_callbacks(PFN_REQUEST_HANDLER request_callback, PFN_SHUTDOWN_HANDLER shutdown_callback, PFN_ASYNC_COMPLETION managed_context_handler, IntPtr pvRequestContext, IntPtr pvShutdownContext);
|
||||
private static extern int register_callbacks(IntPtr pInProcessApplication, PFN_REQUEST_HANDLER requestCallback, PFN_SHUTDOWN_HANDLER shutdownCallback, PFN_ASYNC_COMPLETION asyncCallback, IntPtr pvRequestContext, IntPtr pvShutdownContext);
|
||||
|
||||
[DllImport(AspNetCoreModuleDll)]
|
||||
private static extern unsafe int http_write_response_bytes(IntPtr pInProcessHandler, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, int nChunks, out bool fCompletionExpected);
|
||||
|
|
@ -65,10 +65,10 @@ namespace Microsoft.AspNetCore.Server.IIS
|
|||
private static extern unsafe HttpApiTypes.HTTP_REQUEST_V2* http_get_raw_request(IntPtr pInProcessHandler);
|
||||
|
||||
[DllImport(AspNetCoreModuleDll)]
|
||||
private static extern void http_stop_calls_into_managed();
|
||||
private static extern int http_stop_calls_into_managed(IntPtr pInProcessApplication);
|
||||
|
||||
[DllImport(AspNetCoreModuleDll)]
|
||||
private static extern void http_stop_incoming_requests();
|
||||
private static extern int http_stop_incoming_requests(IntPtr pInProcessApplication);
|
||||
|
||||
[DllImport(AspNetCoreModuleDll)]
|
||||
private static extern unsafe HttpApiTypes.HTTP_RESPONSE_V2* http_get_raw_response(IntPtr pInProcessHandler);
|
||||
|
|
@ -142,9 +142,9 @@ namespace Microsoft.AspNetCore.Server.IIS
|
|||
{
|
||||
http_indicate_completion(pInProcessHandler, notificationStatus);
|
||||
}
|
||||
public static void HttpRegisterCallbacks(PFN_REQUEST_HANDLER request_callback, PFN_SHUTDOWN_HANDLER shutdown_callback, PFN_ASYNC_COMPLETION managed_context_handler, IntPtr pvRequestContext, IntPtr pvShutdownContext)
|
||||
public static void HttpRegisterCallbacks(IntPtr pInProcessApplication, PFN_REQUEST_HANDLER requestCallback, PFN_SHUTDOWN_HANDLER shutdownCallback, PFN_ASYNC_COMPLETION asyncCallback, IntPtr pvRequestContext, IntPtr pvShutdownContext)
|
||||
{
|
||||
register_callbacks(request_callback, shutdown_callback, managed_context_handler, pvRequestContext, pvShutdownContext);
|
||||
Validate(register_callbacks(pInProcessApplication, requestCallback, shutdownCallback, asyncCallback, pvRequestContext, pvShutdownContext));
|
||||
}
|
||||
|
||||
public static unsafe int HttpWriteResponseBytes(IntPtr pInProcessHandler, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, int nChunks, out bool fCompletionExpected)
|
||||
|
|
@ -161,14 +161,14 @@ namespace Microsoft.AspNetCore.Server.IIS
|
|||
return http_get_raw_request(pInProcessHandler);
|
||||
}
|
||||
|
||||
public static void HttpStopCallsIntoManaged()
|
||||
public static void HttpStopCallsIntoManaged(IntPtr pInProcessApplication)
|
||||
{
|
||||
http_stop_calls_into_managed();
|
||||
Validate(http_stop_calls_into_managed(pInProcessApplication));
|
||||
}
|
||||
|
||||
public static void HttpStopIncomingRequests()
|
||||
public static void HttpStopIncomingRequests(IntPtr pInProcessApplication)
|
||||
{
|
||||
http_stop_incoming_requests();
|
||||
Validate(http_stop_incoming_requests(pInProcessApplication));
|
||||
}
|
||||
|
||||
public static unsafe HttpApiTypes.HTTP_RESPONSE_V2* HttpGetRawResponse(IntPtr pInProcessHandler)
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ namespace Microsoft.AspNetCore.Hosting
|
|||
hostBuilder.UseContentRoot(iisConfigData.pwzFullApplicationPath);
|
||||
return hostBuilder.ConfigureServices(
|
||||
services => {
|
||||
services.AddSingleton(new IISNativeApplication(iisConfigData.pNativeApplication));
|
||||
services.AddSingleton<IServer, IISHttpServer>();
|
||||
services.AddSingleton<IStartupFilter>(new IISServerSetupFilter(iisConfigData.pwzVirtualApplicationPath));
|
||||
services.AddAuthenticationCore();
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ namespace IISIntegration.FunctionalTests.Inprocess
|
|||
|
||||
var hostShutdownToken = deploymentResult.DeploymentResult.HostShutdownToken;
|
||||
|
||||
Assert.True(hostShutdownToken.WaitHandle.WaitOne(millisecondsTimeout: 1000));
|
||||
Assert.True(hostShutdownToken.WaitHandle.WaitOne(Helpers.DefaultTimeout));
|
||||
Assert.True(hostShutdownToken.IsCancellationRequested);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
// 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.Threading.Tasks;
|
||||
using IISIntegration.FunctionalTests.Utilities;
|
||||
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||
{
|
||||
[SkipIfIISExpressSchemaMissingInProcess]
|
||||
public class ShutdownTests : IISFunctionalTestBase
|
||||
{
|
||||
|
||||
public ShutdownTests(ITestOutputHelper output) : base(output)
|
||||
{
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
public async Task ServerShutsDownWhenMainExits()
|
||||
{
|
||||
var parameters = Helpers.GetBaseDeploymentParameters();
|
||||
var result = await DeployAsync(parameters);
|
||||
|
||||
var response = await result.RetryingHttpClient.GetAsync("/Shutdown");
|
||||
Assert.True(result.DeploymentResult.HostShutdownToken.WaitHandle.WaitOne(Helpers.DefaultTimeout));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
|
@ -11,6 +12,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
|||
{
|
||||
public class Helpers
|
||||
{
|
||||
public static TimeSpan DefaultTimeout = TimeSpan.FromSeconds(3);
|
||||
|
||||
public static string GetTestWebSitePath(string name)
|
||||
{
|
||||
return Path.Combine(TestPathUtilities.GetSolutionRootDirectory("IISIntegration"),"test", "WebSites", name);
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.AspNetCore.IISIntegration.FunctionalTests;
|
||||
using Microsoft.AspNetCore.Server.IIS;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -725,5 +727,14 @@ namespace IISTestSite
|
|||
});
|
||||
}
|
||||
|
||||
private void Shutdown(IApplicationBuilder app)
|
||||
{
|
||||
app.Run(async ctx =>
|
||||
{
|
||||
await ctx.Response.WriteAsync("Shutting down");
|
||||
ctx.RequestServices.GetService<IApplicationLifetime>().StopApplication();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue