Merge pull request #1486 from dotnet-maestro-bot/merge/release/2.2-to-master
This commit is contained in:
commit
7cd5b2cbb0
|
|
@ -7,6 +7,7 @@
|
||||||
#include "applicationinfo.h"
|
#include "applicationinfo.h"
|
||||||
#include "exceptions.h"
|
#include "exceptions.h"
|
||||||
#include "DisconnectHandler.h"
|
#include "DisconnectHandler.h"
|
||||||
|
#include "SRWExclusiveLock.h"
|
||||||
|
|
||||||
extern BOOL g_fInShutdown;
|
extern BOOL g_fInShutdown;
|
||||||
|
|
||||||
|
|
@ -67,6 +68,7 @@ ASPNET_CORE_PROXY_MODULE::ASPNET_CORE_PROXY_MODULE(HTTP_MODULE_ID moduleId, std:
|
||||||
m_moduleId(moduleId),
|
m_moduleId(moduleId),
|
||||||
m_pDisconnectHandler(nullptr)
|
m_pDisconnectHandler(nullptr)
|
||||||
{
|
{
|
||||||
|
InitializeSRWLock(&m_requestLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASPNET_CORE_PROXY_MODULE::~ASPNET_CORE_PROXY_MODULE()
|
ASPNET_CORE_PROXY_MODULE::~ASPNET_CORE_PROXY_MODULE()
|
||||||
|
|
@ -84,6 +86,9 @@ ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler(
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
REQUEST_NOTIFICATION_STATUS retVal = RQ_NOTIFICATION_CONTINUE;
|
REQUEST_NOTIFICATION_STATUS retVal = RQ_NOTIFICATION_CONTINUE;
|
||||||
|
|
||||||
|
// We don't want OnAsyncCompletion to complete request before OnExecuteRequestHandler exits
|
||||||
|
auto lock = SRWExclusiveLock(m_requestLock);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (g_fInShutdown)
|
if (g_fInShutdown)
|
||||||
|
|
@ -134,6 +139,9 @@ ASPNET_CORE_PROXY_MODULE::OnAsyncCompletion(
|
||||||
IHttpCompletionInfo * pCompletionInfo
|
IHttpCompletionInfo * pCompletionInfo
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
// We don't want OnAsyncCompletion to complete request before OnExecuteRequestHandler exits
|
||||||
|
auto lock = SRWExclusiveLock(m_requestLock);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return HandleNotificationStatus(m_pHandler->OnAsyncCompletion(
|
return HandleNotificationStatus(m_pHandler->OnAsyncCompletion(
|
||||||
|
|
@ -159,9 +167,16 @@ REQUEST_NOTIFICATION_STATUS ASPNET_CORE_PROXY_MODULE::HandleNotificationStatus(R
|
||||||
|
|
||||||
void ASPNET_CORE_PROXY_MODULE::SetupDisconnectHandler(IHttpContext * pHttpContext)
|
void ASPNET_CORE_PROXY_MODULE::SetupDisconnectHandler(IHttpContext * pHttpContext)
|
||||||
{
|
{
|
||||||
auto moduleContainer = pHttpContext
|
auto connection = pHttpContext
|
||||||
->GetConnection()
|
->GetConnection();
|
||||||
->GetModuleContextContainer();
|
|
||||||
|
// connection might be null in when applicationInitialization is running
|
||||||
|
if (connection == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto moduleContainer = connection->GetModuleContextContainer();
|
||||||
|
|
||||||
#pragma warning( push )
|
#pragma warning( push )
|
||||||
#pragma warning ( disable : 26466 ) // Disable "Don't use static_cast downcasts". We build without RTTI support so dynamic_cast is not available
|
#pragma warning ( disable : 26466 ) // Disable "Don't use static_cast downcasts". We build without RTTI support so dynamic_cast is not available
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ class ASPNET_CORE_PROXY_MODULE : NonCopyable, public CHttpModule
|
||||||
std::unique_ptr<IREQUEST_HANDLER, IREQUEST_HANDLER_DELETER> m_pHandler;
|
std::unique_ptr<IREQUEST_HANDLER, IREQUEST_HANDLER_DELETER> m_pHandler;
|
||||||
HTTP_MODULE_ID m_moduleId;
|
HTTP_MODULE_ID m_moduleId;
|
||||||
DisconnectHandler * m_pDisconnectHandler;
|
DisconnectHandler * m_pDisconnectHandler;
|
||||||
|
SRWLOCK m_requestLock {};
|
||||||
};
|
};
|
||||||
|
|
||||||
class ASPNET_CORE_PROXY_MODULE_FACTORY : NonCopyable, public IHttpModuleFactory
|
class ASPNET_CORE_PROXY_MODULE_FACTORY : NonCopyable, public IHttpModuleFactory
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
KnownMethod = VerbId;
|
KnownMethod = VerbId;
|
||||||
StatusCode = 200;
|
StatusCode = 200;
|
||||||
|
|
||||||
var originalPath = RequestUriBuilder.DecodeAndUnescapePath(GetRawUrlInBytes());
|
var originalPath = GetOriginalPath();
|
||||||
|
|
||||||
if (KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawTarget, "*", StringComparison.Ordinal))
|
if (KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawTarget, "*", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
|
|
@ -172,6 +172,22 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
|
||||||
_bodyOutput = new OutputProducer(pipe);
|
_bodyOutput = new OutputProducer(pipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetOriginalPath()
|
||||||
|
{
|
||||||
|
// applicationInitialization request might have trailing \0 character included in the length
|
||||||
|
// check and skip it
|
||||||
|
var rawUrlInBytes = GetRawUrlInBytes();
|
||||||
|
if (rawUrlInBytes.Length > 0 && rawUrlInBytes[rawUrlInBytes.Length - 1] == 0)
|
||||||
|
{
|
||||||
|
var newRawUrlInBytes = new byte[rawUrlInBytes.Length - 1];
|
||||||
|
Array.Copy(rawUrlInBytes, newRawUrlInBytes, newRawUrlInBytes.Length);
|
||||||
|
rawUrlInBytes = newRawUrlInBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
var originalPath = RequestUriBuilder.DecodeAndUnescapePath(rawUrlInBytes);
|
||||||
|
return originalPath;
|
||||||
|
}
|
||||||
|
|
||||||
public int StatusCode
|
public int StatusCode
|
||||||
{
|
{
|
||||||
get { return _statusCode; }
|
get { return _statusCode; }
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ namespace Microsoft.AspNetCore.Server.IIS
|
||||||
internal const int HR_OK = 0;
|
internal const int HR_OK = 0;
|
||||||
internal const int ERROR_NOT_FOUND = unchecked((int)0x80070490);
|
internal const int ERROR_NOT_FOUND = unchecked((int)0x80070490);
|
||||||
internal const int ERROR_OPERATION_ABORTED = unchecked((int)0x800703E3);
|
internal const int ERROR_OPERATION_ABORTED = unchecked((int)0x800703E3);
|
||||||
|
internal const int ERROR_INVALID_PARAMETER = unchecked((int)0x80070057);
|
||||||
internal const int COR_E_IO = unchecked((int)0x80131620);
|
internal const int COR_E_IO = unchecked((int)0x80131620);
|
||||||
|
|
||||||
private const string KERNEL32 = "kernel32.dll";
|
private const string KERNEL32 = "kernel32.dll";
|
||||||
|
|
@ -262,9 +263,10 @@ namespace Microsoft.AspNetCore.Server.IIS
|
||||||
public static bool HttpTryCancelIO(IntPtr pInProcessHandler)
|
public static bool HttpTryCancelIO(IntPtr pInProcessHandler)
|
||||||
{
|
{
|
||||||
var hr = http_cancel_io(pInProcessHandler);
|
var hr = http_cancel_io(pInProcessHandler);
|
||||||
// Async operation finished
|
// ERROR_NOT_FOUND is expected if async operation finished
|
||||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363792(v=vs.85).aspx
|
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363792(v=vs.85).aspx
|
||||||
if (hr == ERROR_NOT_FOUND)
|
// ERROR_INVALID_PARAMETER is expected for "fake" requests like applicationInitialization ones
|
||||||
|
if (hr == ERROR_NOT_FOUND || hr == ERROR_INVALID_PARAMETER)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
|
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
|
||||||
using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests;
|
using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests;
|
||||||
|
|
@ -27,24 +30,44 @@ namespace IIS.FunctionalTests
|
||||||
[RequiresIIS(IISCapability.ApplicationInitialization)]
|
[RequiresIIS(IISCapability.ApplicationInitialization)]
|
||||||
[InlineData(HostingModel.InProcess)]
|
[InlineData(HostingModel.InProcess)]
|
||||||
[InlineData(HostingModel.OutOfProcess)]
|
[InlineData(HostingModel.OutOfProcess)]
|
||||||
public async Task ApplicationInitializationInitializedInProc(HostingModel hostingModel)
|
public async Task ApplicationPreloadStartsApp(HostingModel hostingModel)
|
||||||
{
|
{
|
||||||
var baseDeploymentParameters = _fixture.GetBaseDeploymentParameters(hostingModel);
|
var baseDeploymentParameters = _fixture.GetBaseDeploymentParameters(hostingModel, publish: true);
|
||||||
EnableAppInitialization(baseDeploymentParameters);
|
baseDeploymentParameters.TransformArguments((args, contentRoot)=> $"{args} CreateFile \"{Path.Combine(contentRoot, "Started.txt")}\"");
|
||||||
|
EnablePreload(baseDeploymentParameters);
|
||||||
|
|
||||||
var result = await DeployAsync(baseDeploymentParameters);
|
var result = await DeployAsync(baseDeploymentParameters);
|
||||||
|
|
||||||
// Allow IIS a bit of time to complete starting before we start checking
|
await Helpers.Retry(async () => await File.ReadAllTextAsync(Path.Combine(result.ContentRoot, "Started.txt")), 10, 200);
|
||||||
await Task.Delay(100);
|
|
||||||
// There is always a race between which Init request arrives first
|
|
||||||
// retry couple times to see if we ever get the one comming from ApplicationInitialization module
|
|
||||||
await result.HttpClient.RetryRequestAsync("/ApplicationInitialization", async message => await message.Content.ReadAsStringAsync() == "True");
|
|
||||||
|
|
||||||
StopServer();
|
StopServer();
|
||||||
EventLogHelpers.VerifyEventLogEvent(result, EventLogHelpers.Started(result));
|
EventLogHelpers.VerifyEventLogEvent(result, EventLogHelpers.Started(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EnableAppInitialization(IISDeploymentParameters baseDeploymentParameters)
|
[ConditionalTheory]
|
||||||
|
[RequiresIIS(IISCapability.ApplicationInitialization)]
|
||||||
|
[InlineData(HostingModel.InProcess)]
|
||||||
|
[InlineData(HostingModel.OutOfProcess)]
|
||||||
|
public async Task ApplicationInitializationPageIsRequested(HostingModel hostingModel)
|
||||||
|
{
|
||||||
|
var baseDeploymentParameters = _fixture.GetBaseDeploymentParameters(hostingModel, publish: true);
|
||||||
|
EnablePreload(baseDeploymentParameters);
|
||||||
|
|
||||||
|
baseDeploymentParameters.ServerConfigActionList.Add(
|
||||||
|
(config, _) => {
|
||||||
|
config
|
||||||
|
.RequiredElement("system.webServer")
|
||||||
|
.GetOrAdd("applicationInitialization")
|
||||||
|
.GetOrAdd("add", "initializationPage", "/CreateFile");
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await DeployAsync(baseDeploymentParameters);
|
||||||
|
|
||||||
|
await Helpers.Retry(async () => await File.ReadAllTextAsync(Path.Combine(result.ContentRoot, "Started.txt")), 10, 200);
|
||||||
|
StopServer();
|
||||||
|
EventLogHelpers.VerifyEventLogEvent(result, EventLogHelpers.Started(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnablePreload(IISDeploymentParameters baseDeploymentParameters)
|
||||||
{
|
{
|
||||||
baseDeploymentParameters.ServerConfigActionList.Add(
|
baseDeploymentParameters.ServerConfigActionList.Add(
|
||||||
(config, _) => {
|
(config, _) => {
|
||||||
|
|
@ -60,11 +83,6 @@ namespace IIS.FunctionalTests
|
||||||
.RequiredElement("site")
|
.RequiredElement("site")
|
||||||
.RequiredElement("application")
|
.RequiredElement("application")
|
||||||
.SetAttributeValue("preloadEnabled", true);
|
.SetAttributeValue("preloadEnabled", true);
|
||||||
|
|
||||||
config
|
|
||||||
.RequiredElement("system.webServer")
|
|
||||||
.GetOrAdd("applicationInitialization")
|
|
||||||
.GetOrAdd("add", "initializationPage", "/ApplicationInitialization?IISInit=true");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
baseDeploymentParameters.EnableModule("ApplicationInitializationModule", "%IIS_BIN%\\warmup.dll");
|
baseDeploymentParameters.EnableModule("ApplicationInitializationModule", "%IIS_BIN%\\warmup.dll");
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
Assert.Equal("The client has disconnected", exception.Message);
|
Assert.Equal("The client has disconnected", exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ConditionalFact(Skip = "See: https://github.com/aspnet/IISIntegration/issues/1075")]
|
[ConditionalFact]
|
||||||
public async Task WriterThrowsCancelledException()
|
public async Task WriterThrowsCancelledException()
|
||||||
{
|
{
|
||||||
var requestStartedCompletionSource = CreateTaskCompletionSource();
|
var requestStartedCompletionSource = CreateTaskCompletionSource();
|
||||||
|
|
@ -136,7 +136,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
Exception exception = null;
|
Exception exception = null;
|
||||||
var cancellationTokenSource = new CancellationTokenSource();
|
var cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
var data = new byte[1024];
|
var data = new byte[1];
|
||||||
using (var testServer = await TestServer.Create(async ctx =>
|
using (var testServer = await TestServer.Create(async ctx =>
|
||||||
{
|
{
|
||||||
requestStartedCompletionSource.SetResult(true);
|
requestStartedCompletionSource.SetResult(true);
|
||||||
|
|
@ -167,7 +167,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
Assert.IsType<OperationCanceledException>(exception);
|
Assert.IsType<OperationCanceledException>(exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[ConditionalFact(Skip = "See: https://github.com/aspnet/IISIntegration/issues/1075")]
|
|
||||||
|
[ConditionalFact]
|
||||||
public async Task ReaderThrowsCancelledException()
|
public async Task ReaderThrowsCancelledException()
|
||||||
{
|
{
|
||||||
var requestStartedCompletionSource = CreateTaskCompletionSource();
|
var requestStartedCompletionSource = CreateTaskCompletionSource();
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
@ -21,7 +22,9 @@ namespace TestSite
|
||||||
var mode = args.FirstOrDefault();
|
var mode = args.FirstOrDefault();
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
// Semicolons are appended to env variables; removing them.
|
case "CreateFile":
|
||||||
|
File.WriteAllText(args[1], "");
|
||||||
|
return StartServer();
|
||||||
case "CheckLargeStdOutWrites":
|
case "CheckLargeStdOutWrites":
|
||||||
Console.WriteLine(new string('a', 30000));
|
Console.WriteLine(new string('a', 30000));
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
|
@ -9,14 +10,27 @@ namespace TestSite
|
||||||
{
|
{
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static int Main(string[] args)
|
||||||
|
{
|
||||||
|
var mode = args.FirstOrDefault();
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case "CreateFile":
|
||||||
|
File.WriteAllText(args[1], "");
|
||||||
|
return StartServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
return StartServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int StartServer()
|
||||||
{
|
{
|
||||||
var host = new WebHostBuilder()
|
var host = new WebHostBuilder()
|
||||||
.ConfigureLogging((_, factory) =>
|
.ConfigureLogging(
|
||||||
{
|
(_, factory) => {
|
||||||
factory.AddConsole();
|
factory.AddConsole();
|
||||||
factory.AddFilter("Console", level => level >= LogLevel.Information);
|
factory.AddFilter("Console", level => level >= LogLevel.Information);
|
||||||
})
|
})
|
||||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||||
.UseIISIntegration()
|
.UseIISIntegration()
|
||||||
.UseStartup<Startup>()
|
.UseStartup<Startup>()
|
||||||
|
|
@ -24,6 +38,7 @@ namespace TestSite
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
host.Run();
|
host.Run();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -72,15 +73,11 @@ namespace TestSite
|
||||||
await context.Response.WriteAsync(_waitingRequestCount.ToString());
|
await context.Response.WriteAsync(_waitingRequestCount.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool _applicationInitializationCalled;
|
public Task CreateFile(HttpContext context)
|
||||||
|
|
||||||
public async Task ApplicationInitialization(HttpContext context)
|
|
||||||
{
|
{
|
||||||
if (context.Request.Query["IISInit"] == "true")
|
var hostingEnv = context.RequestServices.GetService<IHostingEnvironment>();
|
||||||
{
|
File.WriteAllText(System.IO.Path.Combine(hostingEnv.ContentRootPath, "Started.txt"), "");
|
||||||
_applicationInitializationCalled = true;
|
return Task.CompletedTask;
|
||||||
}
|
|
||||||
await context.Response.WriteAsync(_applicationInitializationCalled.ToString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task OverrideServer(HttpContext context)
|
public Task OverrideServer(HttpContext context)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue