Merge pull request #1486 from dotnet-maestro-bot/merge/release/2.2-to-master

This commit is contained in:
Justin Kotalik 2018-10-10 16:06:14 -07:00 committed by GitHub
commit 7cd5b2cbb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 107 additions and 39 deletions

View File

@ -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

View File

@ -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

View File

@ -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; }

View File

@ -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;
} }

View File

@ -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");

View File

@ -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();

View File

@ -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;

View File

@ -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;
} }
} }
} }

View File

@ -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)