Add client abort tests (#1051)

This commit is contained in:
Pavel Krymets 2018-07-17 20:26:57 -07:00 committed by GitHub
parent b741743537
commit 54471a2930
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 444 additions and 37 deletions

View File

@ -113,6 +113,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IIS.FunctionalTests", "test
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IntegrationTesting.IIS", "src\Microsoft.AspNetCore.Server.IntegrationTesting.IIS\Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj", "{34135ED7-313D-4E68-860C-D6B51AA28523}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IIS.Tests", "test\IIS.Tests\IIS.Tests.csproj", "{C0310D84-BC2F-4B2E-870E-D35044DB3E3E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common.Tests", "test\Common.Tests\Common.Tests.csproj", "{D17B7B35-5361-4A50-B499-E03E5C3CC095}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -381,6 +385,30 @@ Global
{34135ED7-313D-4E68-860C-D6B51AA28523}.Release|x64.Build.0 = Release|Any CPU
{34135ED7-313D-4E68-860C-D6B51AA28523}.Release|x86.ActiveCfg = Release|Any CPU
{34135ED7-313D-4E68-860C-D6B51AA28523}.Release|x86.Build.0 = Release|Any CPU
{C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Debug|x64.ActiveCfg = Debug|Any CPU
{C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Debug|x64.Build.0 = Debug|Any CPU
{C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Debug|x86.ActiveCfg = Debug|Any CPU
{C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Debug|x86.Build.0 = Debug|Any CPU
{C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Release|Any CPU.Build.0 = Release|Any CPU
{C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Release|x64.ActiveCfg = Release|Any CPU
{C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Release|x64.Build.0 = Release|Any CPU
{C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Release|x86.ActiveCfg = Release|Any CPU
{C0310D84-BC2F-4B2E-870E-D35044DB3E3E}.Release|x86.Build.0 = Release|Any CPU
{D17B7B35-5361-4A50-B499-E03E5C3CC095}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D17B7B35-5361-4A50-B499-E03E5C3CC095}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D17B7B35-5361-4A50-B499-E03E5C3CC095}.Debug|x64.ActiveCfg = Debug|Any CPU
{D17B7B35-5361-4A50-B499-E03E5C3CC095}.Debug|x64.Build.0 = Debug|Any CPU
{D17B7B35-5361-4A50-B499-E03E5C3CC095}.Debug|x86.ActiveCfg = Debug|Any CPU
{D17B7B35-5361-4A50-B499-E03E5C3CC095}.Debug|x86.Build.0 = Debug|Any CPU
{D17B7B35-5361-4A50-B499-E03E5C3CC095}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D17B7B35-5361-4A50-B499-E03E5C3CC095}.Release|Any CPU.Build.0 = Release|Any CPU
{D17B7B35-5361-4A50-B499-E03E5C3CC095}.Release|x64.ActiveCfg = Release|Any CPU
{D17B7B35-5361-4A50-B499-E03E5C3CC095}.Release|x64.Build.0 = Release|Any CPU
{D17B7B35-5361-4A50-B499-E03E5C3CC095}.Release|x86.ActiveCfg = Release|Any CPU
{D17B7B35-5361-4A50-B499-E03E5C3CC095}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -411,6 +439,8 @@ Global
{1533E271-F61B-441B-8B74-59FB61DF0552} = {06CA2C2B-83B0-4D83-905A-E0C74790009E}
{D182103F-8405-4647-B158-C36F598657EF} = {EF30B533-D715-421A-92B7-92FEF460AC9C}
{34135ED7-313D-4E68-860C-D6B51AA28523} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD}
{C0310D84-BC2F-4B2E-870E-D35044DB3E3E} = {EF30B533-D715-421A-92B7-92FEF460AC9C}
{D17B7B35-5361-4A50-B499-E03E5C3CC095} = {EF30B533-D715-421A-92B7-92FEF460AC9C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DB4F868D-E1AE-4FD7-9333-69FA15B268C5}

View File

@ -120,6 +120,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IIS.FunctionalTests", "test
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IntegrationTesting.IIS", "src\Microsoft.AspNetCore.Server.IntegrationTesting.IIS\Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj", "{CE4FB142-91FB-4B34-BC96-A31120EF4009}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IIS.Tests", "test\IIS.Tests\IIS.Tests.csproj", "{A091777D-66B3-42E1-B95C-85322DE40706}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common.Tests", "test\Common.Tests\Common.Tests.csproj", "{A641A208-2974-4E48-BCFF-54E3AAFA4FB9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -408,6 +412,30 @@ Global
{CE4FB142-91FB-4B34-BC96-A31120EF4009}.Release|x64.Build.0 = Release|Any CPU
{CE4FB142-91FB-4B34-BC96-A31120EF4009}.Release|x86.ActiveCfg = Release|Any CPU
{CE4FB142-91FB-4B34-BC96-A31120EF4009}.Release|x86.Build.0 = Release|Any CPU
{A091777D-66B3-42E1-B95C-85322DE40706}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A091777D-66B3-42E1-B95C-85322DE40706}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A091777D-66B3-42E1-B95C-85322DE40706}.Debug|x64.ActiveCfg = Debug|Any CPU
{A091777D-66B3-42E1-B95C-85322DE40706}.Debug|x64.Build.0 = Debug|Any CPU
{A091777D-66B3-42E1-B95C-85322DE40706}.Debug|x86.ActiveCfg = Debug|Any CPU
{A091777D-66B3-42E1-B95C-85322DE40706}.Debug|x86.Build.0 = Debug|Any CPU
{A091777D-66B3-42E1-B95C-85322DE40706}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A091777D-66B3-42E1-B95C-85322DE40706}.Release|Any CPU.Build.0 = Release|Any CPU
{A091777D-66B3-42E1-B95C-85322DE40706}.Release|x64.ActiveCfg = Release|Any CPU
{A091777D-66B3-42E1-B95C-85322DE40706}.Release|x64.Build.0 = Release|Any CPU
{A091777D-66B3-42E1-B95C-85322DE40706}.Release|x86.ActiveCfg = Release|Any CPU
{A091777D-66B3-42E1-B95C-85322DE40706}.Release|x86.Build.0 = Release|Any CPU
{A641A208-2974-4E48-BCFF-54E3AAFA4FB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A641A208-2974-4E48-BCFF-54E3AAFA4FB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A641A208-2974-4E48-BCFF-54E3AAFA4FB9}.Debug|x64.ActiveCfg = Debug|Any CPU
{A641A208-2974-4E48-BCFF-54E3AAFA4FB9}.Debug|x64.Build.0 = Debug|Any CPU
{A641A208-2974-4E48-BCFF-54E3AAFA4FB9}.Debug|x86.ActiveCfg = Debug|Any CPU
{A641A208-2974-4E48-BCFF-54E3AAFA4FB9}.Debug|x86.Build.0 = Debug|Any CPU
{A641A208-2974-4E48-BCFF-54E3AAFA4FB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A641A208-2974-4E48-BCFF-54E3AAFA4FB9}.Release|Any CPU.Build.0 = Release|Any CPU
{A641A208-2974-4E48-BCFF-54E3AAFA4FB9}.Release|x64.ActiveCfg = Release|Any CPU
{A641A208-2974-4E48-BCFF-54E3AAFA4FB9}.Release|x64.Build.0 = Release|Any CPU
{A641A208-2974-4E48-BCFF-54E3AAFA4FB9}.Release|x86.ActiveCfg = Release|Any CPU
{A641A208-2974-4E48-BCFF-54E3AAFA4FB9}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -441,6 +469,8 @@ Global
{1533E271-F61B-441B-8B74-59FB61DF0552} = {06CA2C2B-83B0-4D83-905A-E0C74790009E}
{1F0C8D9B-F47B-41F3-9FC9-6954B6DC7712} = {EF30B533-D715-421A-92B7-92FEF460AC9C}
{CE4FB142-91FB-4B34-BC96-A31120EF4009} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD}
{A091777D-66B3-42E1-B95C-85322DE40706} = {EF30B533-D715-421A-92B7-92FEF460AC9C}
{A641A208-2974-4E48-BCFF-54E3AAFA4FB9} = {EF30B533-D715-421A-92B7-92FEF460AC9C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DB4F868D-E1AE-4FD7-9333-69FA15B268C5}

View File

@ -25,6 +25,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.IISIntegration\Microsoft.AspNetCore.Server.IISIntegration.csproj" />
<ProjectReference Include="..\..\test\IISExpress.FunctionalTests\IISExpress.FunctionalTests.csproj" />
<ProjectReference Include="..\..\test\IIS.Tests\IIS.Tests.csproj" />
<ProjectReference Include="..\..\test\WebSites\**\*.csproj">
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
</ProjectReference>

View File

@ -22,6 +22,8 @@ ASPNET_CORE_GLOBAL_MODULE::OnGlobalStopListening(
{
UNREFERENCED_PARAMETER(pProvider);
LOG_INFO("ASPNET_CORE_GLOBAL_MODULE::OnGlobalStopListening");
if (g_fInShutdown)
{
// Avoid receiving two shutudown notifications.
@ -54,6 +56,8 @@ ASPNET_CORE_GLOBAL_MODULE::OnGlobalConfigurationChange(
// Retrieve the path that has changed.
PCWSTR pwszChangePath = pProvider->GetChangePath();
LOG_INFOF("ASPNET_CORE_GLOBAL_MODULE::OnGlobalConfigurationChange %S", pwszChangePath);
// Test for an error.
if (NULL != pwszChangePath &&
_wcsicmp(pwszChangePath, L"MACHINE") != 0 &&

View File

@ -20,6 +20,7 @@ public:
VOID Terminate()
{
LOG_INFO("ASPNET_CORE_GLOBAL_MODULE::Terminate");
// Remove the class from memory.
delete this;
}

View File

@ -26,7 +26,6 @@ DebugInitialize()
HKEY hKey;
InitializeSRWLock(&g_logFileLock);
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\IIS Extensions\\IIS AspNetCore Module V2\\Parameters",
0,
@ -71,6 +70,11 @@ DebugInitialize()
{
// ignore
}
if (IsDebuggerPresent())
{
DEBUG_FLAGS_VAR |= DEBUG_FLAGS_INFO;
}
}
HRESULT

View File

@ -561,14 +561,19 @@ IN_PROCESS_APPLICATION::RunDotnetApplication(DWORD argc, CONST PCWSTR* argv, hos
__try
{
LOG_INFO("Starting managed application");
m_ProcessExitCode = pProc(argc, argv);
if (m_ProcessExitCode != 0)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
LOG_INFOF("Managed application exited with code %d", m_ProcessExitCode);
}
__except(GetExceptionCode() != 0)
{
LOG_INFOF("Managed threw an exception %d", GetExceptionCode());
hr = HRESULT_FROM_WIN32(GetLastError());
}

View File

@ -3,6 +3,7 @@
#include "stdafx.h"
#include "filewatcher.h"
#include "debugutil.h"
FILE_WATCHER::FILE_WATCHER() :
m_hCompletionPort(NULL),
@ -123,6 +124,7 @@ Win32 error
DWORD dwErrorStatus;
ULONG_PTR completionKey;
LOG_INFO("Starting file watcher thread");
pFileMonitor = (FILE_WATCHER*)pvArg;
DBG_ASSERT(pFileMonitor != NULL);
@ -156,6 +158,8 @@ Win32 error
}
pFileMonitor->m_fThreadExit = TRUE;
LOG_INFO("Stopping file watcher thread");
ExitThread(0);
}

View File

@ -3,6 +3,7 @@
using System;
using System.Buffers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@ -25,7 +26,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
while (true)
{
var result = await _bodyInputPipe.Reader.ReadAsync();
var result = await _bodyInputPipe.Reader.ReadAsync(cancellationToken);
var readableBuffer = result.Buffer;
try
{
@ -158,9 +159,13 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
}
}
}
// We want to swallow IO exception and allow app to finish writing
catch (Exception ex)
{
_bodyOutput.Reader.Complete(ex);
if (!(ex is IOException))
{
_bodyOutput.Reader.Complete(ex);
}
}
finally
{

View File

@ -217,7 +217,16 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
if (flushHeaders)
{
await AsyncIO.FlushAsync();
try
{
await AsyncIO.FlushAsync();
}
// Client might be disconnected at this point
// don't leak the exception
catch (IOException)
{
// ignore
}
}
_writeBodyTask = WriteBody(!flushHeaders);

View File

@ -90,8 +90,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
await _writeBodyTask;
}
// Cancell all remaining IO, thre might be reads pending if not entire request body was sent
// by client
// Cancel all remaining IO, there might be reads pending if not entire request body was sent by client
AsyncIO.Dispose();
if (_readBodyTask != null)

View File

@ -2,8 +2,10 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks.Sources;
@ -101,7 +103,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core.IO
_result = bytes;
if (hr != NativeMethods.HR_OK)
{
_exception = new IOException("IO exception occurred", hr);
_exception = new IOException("Native IO operation failed", Marshal.GetExceptionForHR(hr));
}
}
else

View File

@ -121,6 +121,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
}
catch (OperationCanceledException)
{
_pipe.Writer.Complete();
_completed = true;
throw;
}

View File

@ -13,6 +13,7 @@ namespace Microsoft.AspNetCore.Server.IIS
internal const int HR_OK = 0;
internal const int ERROR_NOT_FOUND = unchecked((int)0x80070490);
internal const int ERROR_OPERATION_ABORTED = unchecked((int)0x800703E3);
internal const int COR_E_IO = unchecked((int)0x80131620);
private const string KERNEL32 = "kernel32.dll";

View File

@ -0,0 +1,47 @@
// 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 Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(IISTestSiteCollection.Name)]
public class ClientDisconnectTests: FixtureLoggedTest
{
private readonly IISTestSiteFixture _fixture;
public ClientDisconnectTests(IISTestSiteFixture fixture): base(fixture)
{
_fixture = fixture;
}
[ConditionalFact]
public async Task ServerWorksAfterClientDisconnect()
{
using (var connection = _fixture.CreateTestConnection())
{
var message = "Hello";
await connection.Send(
"POST /ReadAndWriteSynchronously HTTP/1.1",
$"Content-Length: {100000}",
"Host: localhost",
"Connection: close",
"",
"");
await connection.Send(message);
await connection.Receive(
"HTTP/1.1 200 OK",
"");
}
var response = await _fixture.Client.GetAsync("HelloWorld");
var responseText = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello World", responseText);
}
}
}

View File

@ -1,8 +1,6 @@
// 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.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -14,10 +12,8 @@ using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
public class Helpers
public static class Helpers
{
public static TimeSpan DefaultTimeout = TimeSpan.FromSeconds(3);
public static string GetTestWebSitePath(string name)
{
return Path.Combine(TestPathUtilities.GetSolutionRootDirectory("IISIntegration"),"test", "WebSites", name);

View File

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp2.2</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.IntegrationTesting.IIS\Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Server.IntegrationTesting" Version="$(MicrosoftAspNetCoreServerIntegrationTestingPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(MicrosoftAspNetCoreHostingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
<PackageReference Include="System.Diagnostics.EventLog" Version="$(SystemDiagnosticsEventLogPackageVersion)" />
<PackageReference Include="System.Net.WebSockets.WebSocketProtocol" Version="$(SystemNetWebSocketsWebSocketProtocolPackageVersion)" />
<PackageReference Include="xunit" Version="$(XunitPackageVersion)" />
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitRunnerVisualStudioPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,21 @@
// 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.CompilerServices;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing;
namespace Microsoft.AspNetCore.Server.IntegrationTesting
{
public static class TimeoutExtensions
{
public static TimeSpan DefaultTimeout = TimeSpan.FromSeconds(300);
public static Task TimeoutAfterDefault(this Task task, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = -1)
{
return task.TimeoutAfter(DefaultTimeout, filePath, lineNumber);
}
}
}

View File

@ -13,6 +13,7 @@
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.IIS\Microsoft.AspNetCore.Server.IIS.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.IISIntegration\Microsoft.AspNetCore.Server.IISIntegration.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.IntegrationTesting.IIS\Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj" />
<ProjectReference Include="..\Common.Tests\Common.Tests.csproj" />
<ProjectReference Include="..\WebSites\**\*.csproj">
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
</ProjectReference>

View File

@ -177,8 +177,6 @@
-->
<globalModules>
<add name="UriCacheModule" image="%windir%\System32\inetsrv\cachuri.dll" />
<add name="ProtocolSupportModule" image="%windir%\System32\inetsrv\protsup.dll" />
<add name="AnonymousAuthenticationModule" image="%windir%\System32\inetsrv\authanon.dll" />
<add name="AspNetCoreModuleV2" image="aspnetcorev2.dll" />
</globalModules>
@ -193,8 +191,7 @@
<add name="AspNetCoreModuleV2" lockItem="true" />
</modules>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" hostingModel="inprocess" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" hostingModel="inprocess" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" />
</system.webServer>

View File

@ -0,0 +1,177 @@
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[SkipIfHostableWebCoreNotAvailable]
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, "https://github.com/aspnet/IISIntegration/issues/866")]
public class ClientDisconnectTests : LoggedTest
{
[ConditionalFact]
public async Task WritesSucceedAfterClientDisconnect()
{
var requestStartedCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
var clientDisconnectedCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
var requestCompletedCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
var data = new byte[1024];
using (var testServer = await TestServer.Create(
async ctx =>
{
requestStartedCompletionSource.SetResult(true);
await clientDisconnectedCompletionSource.Task;
for (var i = 0; i < 1000; i++)
{
await ctx.Response.Body.WriteAsync(data);
}
requestCompletedCompletionSource.SetResult(true);
}, LoggerFactory))
{
using (var connection = testServer.CreateConnection())
{
await SendContentLength1Post(connection);
await requestStartedCompletionSource.Task.TimeoutAfterDefault();
}
clientDisconnectedCompletionSource.SetResult(true);
await requestCompletedCompletionSource.Task.TimeoutAfterDefault();
}
}
[ConditionalFact]
public async Task ReadThrowsAfterClientDisconnect()
{
var requestStartedCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
var requestCompletedCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
Exception exception = null;
var data = new byte[1024];
using (var testServer = await TestServer.Create(async ctx =>
{
requestStartedCompletionSource.SetResult(true);
try
{
await ctx.Request.Body.ReadAsync(data);
}
catch (Exception e)
{
exception = e;
}
requestCompletedCompletionSource.SetResult(true);
}, LoggerFactory))
{
using (var connection = testServer.CreateConnection())
{
await SendContentLength1Post(connection);
await requestStartedCompletionSource.Task.TimeoutAfterDefault();
}
await requestCompletedCompletionSource.Task.TimeoutAfterDefault();
}
Assert.IsType<IOException>(exception);
Assert.Equal("Native IO operation failed", exception.Message);
}
[ConditionalFact]
public async Task WriterThrowsCancelledException()
{
var requestStartedCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
var requestCompletedCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
Exception exception = null;
var cancellationTokenSource = new CancellationTokenSource();
var data = new byte[1024];
using (var testServer = await TestServer.Create(async ctx =>
{
requestStartedCompletionSource.SetResult(true);
try
{
while (true)
{
await ctx.Response.Body.WriteAsync(data, cancellationTokenSource.Token);
}
}
catch (Exception e)
{
exception = e;
}
requestCompletedCompletionSource.SetResult(true);
}, LoggerFactory))
{
using (var connection = testServer.CreateConnection())
{
await SendContentLength1Post(connection);
await requestStartedCompletionSource.Task.TimeoutAfterDefault();
cancellationTokenSource.Cancel();
await requestCompletedCompletionSource.Task.TimeoutAfterDefault();
}
Assert.IsType<OperationCanceledException>(exception);
}
}
[ConditionalFact]
public async Task ReaderThrowsCancelledException()
{
var requestStartedCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
var requestCompletedCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
Exception exception = null;
var cancellationTokenSource = new CancellationTokenSource();
var data = new byte[1024];
using (var testServer = await TestServer.Create(async ctx =>
{
requestStartedCompletionSource.SetResult(true);
try
{
await ctx.Request.Body.ReadAsync(data, cancellationTokenSource.Token);
}
catch (Exception e)
{
exception = e;
}
requestCompletedCompletionSource.SetResult(true);
}, LoggerFactory))
{
using (var connection = testServer.CreateConnection())
{
await SendContentLength1Post(connection);
await requestStartedCompletionSource.Task.TimeoutAfterDefault();
cancellationTokenSource.Cancel();
await requestCompletedCompletionSource.Task.TimeoutAfterDefault();
}
Assert.IsType<OperationCanceledException>(exception);
}
}
private static async Task SendContentLength1Post(TestConnection connection)
{
await connection.Send(
"POST / HTTP/1.1",
"Content-Length: 1",
"Host: localhost",
"Connection: close",
"",
"");
}
}
}

View File

@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp2.2</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.IIS\Microsoft.AspNetCore.Server.IIS.csproj" />
<ProjectReference Include="..\Common.Tests\Common.Tests.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
<None Include="$(MSBuildThisFileDirectory)..\..\src\AspNetCoreModuleV2\InProcessRequestHandler\bin\$(Configuration)\x64\aspnetcorev2_inprocess.dll" CopyToOutputDirectory="PreserveNewest" Visible="true" Link="%(FileName)%(Extension)" />
<None Include="$(MSBuildThisFileDirectory)..\..\src\AspNetCoreModuleV2\InProcessRequestHandler\bin\$(Configuration)\x64\aspnetcorev2_inprocess.pdb" CopyToOutputDirectory="PreserveNewest" Visible="true" Link="%(FileName)%(Extension)" />
<None Include="$(MSBuildThisFileDirectory)..\..\src\AspNetCoreModuleV2\AspNetCore\bin\$(Configuration)\x64\aspnetcorev2.dll" CopyToOutputDirectory="PreserveNewest" Visible="true" Link="%(FileName)%(Extension)" />
<None Include="$(MSBuildThisFileDirectory)..\..\src\AspNetCoreModuleV2\AspNetCore\bin\$(Configuration)\x64\aspnetcorev2.pdb" CopyToOutputDirectory="PreserveNewest" Visible="true" Link="%(FileName)%(Extension)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Server.IntegrationTesting" Version="$(MicrosoftAspNetCoreServerIntegrationTestingPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(MicrosoftAspNetCoreHostingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
<PackageReference Include="System.Diagnostics.EventLog" Version="$(SystemDiagnosticsEventLogPackageVersion)" />
<PackageReference Include="System.Net.WebSockets.WebSocketProtocol" Version="$(SystemNetWebSocketsWebSocketProtocolPackageVersion)" />
<PackageReference Include="xunit" Version="$(XunitPackageVersion)" />
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitRunnerVisualStudioPackageVersion)" />
</ItemGroup>
<ItemGroup>
<None Include="AppHostConfig\HostableWebCore.config" CopyToOutputDirectory="PreserveNewest" Link="%(FileName)%(Extension)" />
</ItemGroup>
</Project>

View File

@ -2,31 +2,26 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[SkipIfHostableWebCoreNotAvailible]
public class TestServerTest: LoggedTest
[SkipIfHostableWebCoreNotAvailable]
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, "https://github.com/aspnet/IISIntegration/issues/866")]
public class TestServerTest : LoggedTest
{
public TestServerTest(ITestOutputHelper output = null) : base(output)
{
}
[ConditionalFact]
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, "https://github.com/aspnet/IISIntegration/issues/866")]
public async Task SingleProcessTestServer_HelloWorld()
{
var helloWorld = "Hello World";
var expectedPath = "/Path";
string path = null;
using (var testServer = await TestServer.Create(ctx => {
using (var testServer = await TestServer.Create(ctx =>
{
path = ctx.Request.Path.ToString();
return ctx.Response.WriteAsync(helloWorld);
}, LoggerFactory))

View File

@ -8,10 +8,10 @@ using Microsoft.AspNetCore.Testing.xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)]
public sealed class SkipIfHostableWebCoreNotAvailibleAttribute : Attribute, ITestCondition
public sealed class SkipIfHostableWebCoreNotAvailableAttribute : Attribute, ITestCondition
{
public bool IsMet { get; } = File.Exists(TestServer.HostableWebCoreLocation);
public string SkipReason { get; } = $"Hostable Web Core not availible, {TestServer.HostableWebCoreLocation} not found.";
public string SkipReason { get; } = $"Hostable Web Core not available, {TestServer.HostableWebCoreLocation} not found.";
}
}

View File

@ -6,8 +6,11 @@ using System.IO;
using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Xml.XPath;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
@ -24,6 +27,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
private const string HWebCoreDll = "hwebcore.dll";
internal static string HostableWebCoreLocation => Environment.ExpandEnvironmentVariables($@"%windir%\system32\inetsrv\{HWebCoreDll}");
internal static string BasePath => Path.GetDirectoryName(new Uri(typeof(TestServer).Assembly.CodeBase).AbsolutePath);
internal static string InProcessHandlerLocation => Path.Combine(BasePath, InProcessHandlerDll);
internal static string AspNetCoreModuleLocation => Path.Combine(BasePath, AspNetCoreModuleDll);
private static readonly SemaphoreSlim WebCoreLock = new SemaphoreSlim(1, 1);
@ -35,14 +42,18 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
private readonly Action<IApplicationBuilder> _appBuilder;
private readonly ILoggerFactory _loggerFactory;
private readonly hostfxr_main_fn _hostfxrMainFn;
public HttpClient HttpClient { get; }
public TestConnection CreateConnection() => new TestConnection(BasePort);
private IWebHost _host;
private string _appHostConfigPath;
private TestServer(Action<IApplicationBuilder> appBuilder, ILoggerFactory loggerFactory)
{
_hostfxrMainFn = Main;
_appBuilder = appBuilder;
_loggerFactory = loggerFactory;
@ -57,7 +68,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
await WebCoreLock.WaitAsync();
var server = new TestServer(appBuilder, loggerFactory);
server.Start();
await server.HttpClient.GetAsync("/start");
(await server.HttpClient.GetAsync("/start")).EnsureSuccessStatusCode();
await server._startedTaskCompletionSource.Task;
return server;
}
@ -70,11 +81,16 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
private void Start()
{
LoadLibrary(HostableWebCoreLocation);
LoadLibrary(InProcessHandlerDll);
LoadLibrary(AspNetCoreModuleDll);
_appHostConfigPath = Path.GetTempFileName();
set_main_handler(Main);
var startResult = WebCoreActivate(Path.GetFullPath("HostableWebCore.config"), null, "Instance");
var webHostConfig = XDocument.Load(Path.GetFullPath("HostableWebCore.config"));
webHostConfig.XPathSelectElement("/configuration/system.webServer/globalModules/add[@name='AspNetCoreModuleV2']")
.SetAttributeValue("image", AspNetCoreModuleLocation);
webHostConfig.Save(_appHostConfigPath);
set_main_handler(_hostfxrMainFn);
var startResult = WebCoreActivate(_appHostConfigPath, null, "Instance");
if (startResult != 0)
{
throw new InvalidOperationException($"Error while running WebCoreActivate: {startResult}");
@ -87,8 +103,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
.UseIIS()
.ConfigureServices(services => {
services.AddSingleton<IStartup>(this);
services.AddSingleton<ILoggerFactory>(_loggerFactory);
})
services.AddSingleton(_loggerFactory);
})
.UseSetting(WebHostDefaults.ApplicationKey, typeof(TestServer).GetTypeInfo().Assembly.FullName)
.Build();

View File

@ -12,6 +12,7 @@
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.IIS\Microsoft.AspNetCore.Server.IIS.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.IISIntegration\Microsoft.AspNetCore.Server.IISIntegration.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.IntegrationTesting.IIS\Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj" />
<ProjectReference Include="..\Common.Tests\Common.Tests.csproj" />
<ProjectReference Include="..\WebSites\**\*.csproj">
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
</ProjectReference>

View File

@ -134,7 +134,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
var hostShutdownToken = deploymentResult.DeploymentResult.HostShutdownToken;
Assert.True(hostShutdownToken.WaitHandle.WaitOne(Helpers.DefaultTimeout));
Assert.True(hostShutdownToken.WaitHandle.WaitOne(TimeoutExtensions.DefaultTimeout));
Assert.True(hostShutdownToken.IsCancellationRequested);
}

View File

@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
using Xunit.Abstractions;
@ -23,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
var result = await DeployAsync(parameters);
var response = await result.RetryingHttpClient.GetAsync("/Shutdown");
Assert.True(result.DeploymentResult.HostShutdownToken.WaitHandle.WaitOne(Helpers.DefaultTimeout));
Assert.True(result.DeploymentResult.HostShutdownToken.WaitHandle.WaitOne(TimeoutExtensions.DefaultTimeout));
}
}
}