Added new test scenarios for websocket (#153)

This commit is contained in:
jhkimnew 2017-09-12 11:39:55 -07:00 коммит произвёл GitHub
Родитель a732b106f5
Коммит c5f59e06c3
23 изменённых файлов: 1552 добавлений и 149 удалений

Просмотреть файл

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26224.1
VisualStudioVersion = 15.0.26815.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AspNetCore", "src\AspNetCore\AspNetCore.vcxproj", "{439824F9-1455-4CC4-BD79-B44FA0A16552}"
ProjectSection(ProjectDependencies) = postProject
@ -23,6 +23,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
NuGet.Config = NuGet.Config
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebSocketClientEXE", "test\WebSocketClientEXE\WebSocketClientEXE.csproj", "{4062EA94-75F5-4691-86DC-C8594BA896DE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -77,6 +79,18 @@ Global
{030225D8-4EE8-47E5-B692-2A96B3B51A38}.Release|Win32.Build.0 = Release|Any CPU
{030225D8-4EE8-47E5-B692-2A96B3B51A38}.Release|x64.ActiveCfg = Release|Any CPU
{030225D8-4EE8-47E5-B692-2A96B3B51A38}.Release|x64.Build.0 = Release|Any CPU
{4062EA94-75F5-4691-86DC-C8594BA896DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4062EA94-75F5-4691-86DC-C8594BA896DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4062EA94-75F5-4691-86DC-C8594BA896DE}.Debug|Win32.ActiveCfg = Debug|Any CPU
{4062EA94-75F5-4691-86DC-C8594BA896DE}.Debug|Win32.Build.0 = Debug|Any CPU
{4062EA94-75F5-4691-86DC-C8594BA896DE}.Debug|x64.ActiveCfg = Debug|Any CPU
{4062EA94-75F5-4691-86DC-C8594BA896DE}.Debug|x64.Build.0 = Debug|Any CPU
{4062EA94-75F5-4691-86DC-C8594BA896DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4062EA94-75F5-4691-86DC-C8594BA896DE}.Release|Any CPU.Build.0 = Release|Any CPU
{4062EA94-75F5-4691-86DC-C8594BA896DE}.Release|Win32.ActiveCfg = Release|Any CPU
{4062EA94-75F5-4691-86DC-C8594BA896DE}.Release|Win32.Build.0 = Release|Any CPU
{4062EA94-75F5-4691-86DC-C8594BA896DE}.Release|x64.ActiveCfg = Release|Any CPU
{4062EA94-75F5-4691-86DC-C8594BA896DE}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -86,5 +100,6 @@ Global
{4787A64F-9A3E-4867-A55A-70CB4B2B2FFE} = {FDD2EDF8-1B62-4978-9815-9D95260B8B91}
{4DDA7560-AA29-4161-A5EA-A7E8F3997321} = {02F461DC-5166-4E88-AAD5-CF110016A647}
{030225D8-4EE8-47E5-B692-2A96B3B51A38} = {02F461DC-5166-4E88-AAD5-CF110016A647}
{4062EA94-75F5-4691-86DC-C8594BA896DE} = {02F461DC-5166-4E88-AAD5-CF110016A647}
EndGlobalSection
EndGlobal

Просмотреть файл

@ -5,6 +5,7 @@
<TargetFramework>net46</TargetFramework>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<AssemblyName>AspNetCoreModule.Test</AssemblyName>
</PropertyGroup>
<ItemGroup>
@ -21,8 +22,8 @@
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.4.0-*" />
<PackageReference Include="xunit" Version="$(XunitVersion)" />
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitVersion)" />
<PackageReference Include="xunit" Version="2.3.0-beta3-build3705" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta3-build3705" />
<PackageReference Include="Microsoft.Web.Administration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="$(AspNetCoreVersion)" />
@ -44,8 +45,13 @@
<Reference Include="System.ServiceProcess" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' ">
<Reference Include="System.Management.Automation" />
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
<Folder Include="Properties\" />
</ItemGroup>
</Project>

Просмотреть файл

@ -79,6 +79,24 @@ namespace AspNetCoreModule.Test.Framework
return result;
}
public static void RestoreAppHostConfig(string fileExtenstion, bool overWriteMode)
{
string tofile = Strings.AppHostConfigPath;
string fromfile = Strings.AppHostConfigPath + fileExtenstion;
if (File.Exists(fromfile))
{
try
{
TestUtility.FileCopy(fromfile, tofile, overWrite:overWriteMode);
}
catch
{
TestUtility.LogInformation("Failed to Restore applicationhost.config");
throw;
}
}
}
public static void RestoreAppHostConfig(bool restoreFromMasterBackupFile = true)
{
string masterBackupFileExtension = ".ancmtest.masterbackup";

Просмотреть файл

@ -14,7 +14,7 @@ namespace AspNetCoreModule.Test.Framework
public const string ANCMTestFlagsDefaultContext = "AdminAnd64Bit";
public const string ANCMTestFlagsTestSkipContext = "SkipTest";
public const string ANCMTestFlagsUsePrivateAspNetCoreFileContext = "UsePrivate";
public const string ANCMTestFlagsUseIISExpressContext = "UseIISExpress";
private const string ANCMTestFlagsUseIISExpressContext = "UseIISExpress";
private static bool? _usePrivateAspNetCoreFile = null;
public static bool? UsePrivateAspNetCoreFile

Просмотреть файл

@ -791,7 +791,47 @@ namespace AspNetCoreModule.Test.Framework
return stringBuilder.ToString().Trim(new char[] { ' ', '\r', '\n' });
}
public static void RunPowershellScript(string scriptText, string expectedResult, int retryCount = 3)
{
bool isReady = false;
string result = string.Empty;
for (int i = 0; i < retryCount; i++)
{
try
{
result = TestUtility.RunPowershellScript(scriptText);
}
catch
{
result = "ExceptionError";
}
if (expectedResult != null)
{
if (expectedResult == result)
{
isReady = true;
break;
}
else
{
System.Threading.Thread.Sleep(1000);
}
}
else
{
isReady = true;
break;
}
}
if (!isReady)
{
throw new ApplicationException("Failed to execute command: " + scriptText + ", expected result: " + expectedResult + ", actual result = " + result);
}
}
public static int RunCommand(string fileName, string arguments = null, bool checkStandardError = true, bool waitForExit=true)
{
int pid = -1;
@ -809,7 +849,7 @@ namespace AspNetCoreModule.Test.Framework
}
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.CreateNoWindow = true;
p.Start();
pid = p.Id;
string standardOutput = string.Empty;

Просмотреть файл

@ -99,12 +99,43 @@ namespace AspNetCoreModule.Test.Framework
}
}
private int _workerProcessID = 0;
public int WorkerProcessID
{
get
{
if (_workerProcessID == 0)
{
try
{
if (IisServerType == ServerType.IISExpress)
{
_workerProcessID = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("iisexpress.exe", "Handle", null));
}
else
{
_workerProcessID = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("w3wp.exe", "Handle", null));
}
}
catch
{
TestUtility.LogInformation("Failed to get process id of w3wp.exe");
}
}
return _workerProcessID;
}
set
{
_workerProcessID = value;
}
}
public ServerType IisServerType { get; set; }
public string IisExpressConfigPath { get; set; }
private int _siteId { get; set; }
private IISConfigUtility.AppPoolBitness _appPoolBitness { get; set; }
public TestWebSite(IISConfigUtility.AppPoolBitness appPoolBitness, string loggerPrefix = "ANCMTest", bool startIISExpress = true, bool copyAllPublishedFiles = false)
public TestWebSite(IISConfigUtility.AppPoolBitness appPoolBitness, string loggerPrefix = "ANCMTest", bool startIISExpress = true, bool copyAllPublishedFiles = false, bool attachAppVerifier = false)
{
_appPoolBitness = appPoolBitness;
@ -165,9 +196,9 @@ namespace AspNetCoreModule.Test.Framework
}
//
// Currently we use only DotnetCore v1.1
// Currently we use DotnetCore v1.0
//
string publishPath = Path.Combine(srcPath, "bin", "Debug", "netcoreapp1.1", "publish");
string publishPath = Path.Combine(srcPath, "bin", "Debug", "netcoreapp1.0", "publish");
string publishPathOutput = Path.Combine(Environment.ExpandEnvironmentVariables("%SystemDrive%") + @"\", "inetpub", "ANCMTest", "publishPathOutput");
//
@ -177,7 +208,12 @@ namespace AspNetCoreModule.Test.Framework
{
string argumentForDotNet = "publish " + srcPath;
TestUtility.LogInformation("TestWebSite::TestWebSite() StandardTestApp is not published, trying to publish on the fly: dotnet.exe " + argumentForDotNet);
TestUtility.DeleteDirectory(publishPath);
TestUtility.RunCommand("dotnet", argumentForDotNet);
if (!File.Exists(Path.Combine(publishPath, "AspNetCoreModule.TestSites.Standard.dll")))
{
throw new Exception("Failed to publish");
}
TestUtility.DirectoryCopy(publishPath, publishPathOutput);
TestUtility.FileCopy(Path.Combine(publishPathOutput, "web.config"), Path.Combine(publishPathOutput, "web.config.bak"));
@ -283,19 +319,27 @@ namespace AspNetCoreModule.Test.Framework
if (startIISExpress)
{
StartIISExpress();
}
// clean up IISExpress before starting a new instance
TestUtility.KillIISExpressProcess();
StartIISExpress();
// send a startup request to IISExpress instance to make sure that it is fully ready to use before starting actual test scenarios
TestUtility.RunPowershellScript("( invoke-webrequest http://localhost:" + TcpPort + " ).StatusCode", "200");
}
TestUtility.LogInformation("TestWebSite::TestWebSite() End");
}
public void StartIISExpress(string verificationCommand = null)
public void StartIISExpress()
{
if (IisServerType == ServerType.IIS)
{
return;
}
// reset workerProcessID
this.WorkerProcessID = 0;
string cmdline;
string argument = "/siteid:" + _siteId + " /config:" + IisExpressConfigPath;
@ -309,41 +353,126 @@ namespace AspNetCoreModule.Test.Framework
}
TestUtility.LogInformation("TestWebSite::TestWebSite() Start IISExpress: " + cmdline + " " + argument);
_iisExpressPidBackup = TestUtility.RunCommand(cmdline, argument, false, false);
bool isIISExpressReady = false;
int timeout = 3;
for (int i = 0; i < timeout * 5; i++)
}
public void AttachAppverifier()
{
string cmdline;
string processName = "iisexpress.exe";
if (IisServerType == ServerType.IIS)
{
string statusCode = string.Empty;
try
{
if (verificationCommand == null)
{
verificationCommand = "( invoke-webrequest http://localhost:" + TcpPort + " ).StatusCode";
}
statusCode = TestUtility.RunPowershellScript(verificationCommand);
}
catch
{
statusCode = "ExceptionError";
}
if ("200" == statusCode)
{
isIISExpressReady = true;
break;
}
else
{
System.Threading.Thread.Sleep(200);
}
processName = "w3wp.exe";
}
if (isIISExpressReady)
string argument = "-enable Heaps COM RPC Handles Locks Memory TLS Exceptions Threadpool Leak SRWLock -for " + processName;
if (Directory.Exists(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%")) && _appPoolBitness == IISConfigUtility.AppPoolBitness.enable32Bit)
{
TestUtility.LogInformation("IISExpress is ready to use");
cmdline = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "syswow64", "appverif.exe");
if (!File.Exists(cmdline))
{
throw new System.ApplicationException("Not found :" + cmdline + "; this test requires appverif.exe.");
}
}
else
{
throw new ApplicationException("IISExpress is not responding within " + timeout + " seconds");
cmdline = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "system32", "appverif.exe");
if (!File.Exists(cmdline))
{
throw new System.ApplicationException("Not found :" + cmdline + "; this test requires appverif.exe.");
}
}
try
{
TestUtility.LogInformation("Configure Appverifier: " + cmdline + " " + argument);
TestUtility.RunCommand(cmdline, argument, true, false);
}
catch
{
throw new System.ApplicationException("Failed to configure Appverifier");
}
}
public void AttachWinDbg(int processIdOfWorkerProcess)
{
string processName = "iisexpress.exe";
string debuggerCmdline;
if (IisServerType == ServerType.IIS)
{
processName = "w3wp.exe";
}
string argument = "-enable Heaps COM RPC Handles Locks Memory TLS Exceptions Threadpool Leak SRWLock -for " + processName;
if (Directory.Exists(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%")) && _appPoolBitness == IISConfigUtility.AppPoolBitness.enable32Bit)
{
debuggerCmdline = Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "Debugging Tools for Windows (x64)", "wow64", "windbg.exe");
if (!File.Exists(debuggerCmdline))
{
throw new System.ApplicationException("Not found :" + debuggerCmdline + "; this test requires windbg.exe.");
}
}
else
{
debuggerCmdline = Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "Debugging Tools for Windows (x64)", "windbg.exe");
if (!File.Exists(debuggerCmdline))
{
throw new System.ApplicationException("Not found :" + debuggerCmdline + "; this test requires windbg.exe.");
}
}
try
{
TestUtility.RunCommand(debuggerCmdline, " -g -G -p " + processIdOfWorkerProcess.ToString(), true, false);
System.Threading.Thread.Sleep(3000);
}
catch
{
throw new System.ApplicationException("Failed to attach debuger");
}
}
public void DetachAppverifier()
{
try
{
string cmdline;
string processName = "iisexpress.exe";
string debuggerCmdline;
if (IisServerType == ServerType.IIS)
{
processName = "w3wp.exe";
}
string argument = "-disable * -for " + processName;
if (Directory.Exists(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%")) && _appPoolBitness == IISConfigUtility.AppPoolBitness.enable32Bit)
{
cmdline = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "syswow64", "appverif.exe");
if (!File.Exists(cmdline))
{
throw new System.ApplicationException("Not found :" + cmdline);
}
debuggerCmdline = Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "Debugging Tools for Windows (x64)", "wow64", "windbg.exe");
if (!File.Exists(debuggerCmdline))
{
throw new System.ApplicationException("Not found :" + debuggerCmdline);
}
}
else
{
cmdline = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "system32", "appverif.exe");
if (!File.Exists(cmdline))
{
throw new System.ApplicationException("Not found :" + cmdline);
}
debuggerCmdline = Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "Debugging Tools for Windows (x64)", "windbg.exe");
if (!File.Exists(debuggerCmdline))
{
throw new System.ApplicationException("Not found :" + debuggerCmdline);
}
}
TestUtility.RunCommand(cmdline, argument, true, false);
}
catch
{
TestUtility.LogInformation("Failed to detach Appverifier");
}
}
}

Просмотреть файл

@ -308,6 +308,33 @@ namespace AspNetCoreModule.Test
return DoClientCertificateMappingTest(appPoolBitness, useHTTPSMiddleWare);
}
[ConditionalTheory]
[ANCMTestSkipCondition("%ANCMTestFlags%")]
[OSSkipCondition(OperatingSystems.Linux)]
[OSSkipCondition(OperatingSystems.MacOSX)]
[InlineData(IISConfigUtility.AppPoolBitness.noChange, false, DoAppVerifierTest_StartUpMode.UseGracefulShutdown, DoAppVerifierTest_ShutDownMode.RecycleAppPool, 1)]
[InlineData(IISConfigUtility.AppPoolBitness.noChange, false, DoAppVerifierTest_StartUpMode.DontUseGracefulShutdown, DoAppVerifierTest_ShutDownMode.RecycleAppPool, 1)]
[InlineData(IISConfigUtility.AppPoolBitness.noChange, false, DoAppVerifierTest_StartUpMode.UseGracefulShutdown, DoAppVerifierTest_ShutDownMode.StopAndStartAppPool, 1)]
public Task AppVerifierTest(IISConfigUtility.AppPoolBitness appPoolBitness, bool shutdownTimeout, DoAppVerifierTest_StartUpMode startUpMode, DoAppVerifierTest_ShutDownMode shutDownMode, int repeatCount)
{
return DoAppVerifierTest(appPoolBitness, shutdownTimeout, startUpMode, shutDownMode, repeatCount);
}
//////////////////////////////////////////////////////////
// NOTE: below test scenarios are not valid for Win7 OS
//////////////////////////////////////////////////////////
[ConditionalTheory]
[ANCMTestSkipCondition("%ANCMTestFlags%")]
[OSSkipCondition(OperatingSystems.Linux)]
[OSSkipCondition(OperatingSystems.MacOSX)]
[InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)]
[InlineData(IISConfigUtility.AppPoolBitness.noChange)]
public Task WebSocketErrorhandlingTest(IISConfigUtility.AppPoolBitness appPoolBitness)
{
return DoWebSocketErrorhandlingTest(appPoolBitness);
}
//////////////////////////////////////////////////////////
// NOTE: below test scenarios are not valid for Win7 OS
//////////////////////////////////////////////////////////

Просмотреть файл

@ -174,6 +174,8 @@ namespace AspNetCoreModule.Test
{
using (var testSite = new TestWebSite(appPoolBitness, "DoRecycleApplicationAfterW3WPProcessBeingKilled"))
{
string appDllFileName = testSite.AspNetCoreApp.GetArgumentFileName();
if (testSite.IisServerType == ServerType.IISExpress)
{
TestUtility.LogInformation("This test is not valid for IISExpress server type");
@ -204,6 +206,11 @@ namespace AspNetCoreModule.Test
workerProcess.Kill();
Thread.Sleep(500);
// Verify the application file can be removed after worker process is stopped
testSite.AspNetCoreApp.BackupFile(appDllFileName);
testSite.AspNetCoreApp.DeleteFile(appDllFileName);
testSite.AspNetCoreApp.RestoreFile(appDllFileName);
}
}
}
@ -213,6 +220,8 @@ namespace AspNetCoreModule.Test
using (var testSite = new TestWebSite(appPoolBitness, "DoRecycleApplicationAfterWebConfigUpdated"))
{
string backendProcessId_old = null;
string appDllFileName = testSite.AspNetCoreApp.GetArgumentFileName();
const int repeatCount = 3;
for (int i = 0; i < repeatCount; i++)
{
@ -231,6 +240,11 @@ namespace AspNetCoreModule.Test
testSite.AspNetCoreApp.MoveFile("web.config", "_web.config");
Thread.Sleep(500);
testSite.AspNetCoreApp.MoveFile("_web.config", "web.config");
// Verify the application file can be removed after backend process is restarted
testSite.AspNetCoreApp.BackupFile(appDllFileName);
testSite.AspNetCoreApp.DeleteFile(appDllFileName);
testSite.AspNetCoreApp.RestoreFile(appDllFileName);
}
// restore web.config
@ -320,9 +334,9 @@ namespace AspNetCoreModule.Test
Assert.True(totalNumber == (await GetResponse(testSite.AspNetCoreApp.GetUri("GetEnvironmentVariables"), HttpStatusCode.OK)));
iisConfig.SetANCMConfig(
testSite.SiteName,
testSite.AspNetCoreApp.Name,
"environmentVariable",
testSite.SiteName,
testSite.AspNetCoreApp.Name,
"environmentVariable",
new string[] { "ANCMTestFoo", "foo" }
);
@ -366,7 +380,7 @@ namespace AspNetCoreModule.Test
// Add a new environment variable
if (setEnvironmentVariableConfiguration)
{
{
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "environmentVariable", new string[] { environmentVariableName, environmentVariableValue });
// Adjust the new expected total number of environment variables
@ -377,7 +391,7 @@ namespace AspNetCoreModule.Test
}
}
Thread.Sleep(500);
// check JitDebugger before continuing
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
totalResult = (await GetResponse(testSite.AspNetCoreApp.GetUri("GetEnvironmentVariables"), HttpStatusCode.OK));
@ -387,17 +401,17 @@ namespace AspNetCoreModule.Test
// Verify other common environment variables
string temp = (await GetResponse(testSite.AspNetCoreApp.GetUri("DumpEnvironmentVariables"), HttpStatusCode.OK));
Assert.True(temp.Contains("ASPNETCORE_PORT"));
Assert.True(temp.Contains("ASPNETCORE_APPL_PATH"));
Assert.True(temp.Contains("ASPNETCORE_IIS_HTTPAUTH"));
Assert.True(temp.Contains("ASPNETCORE_TOKEN"));
Assert.True(temp.Contains("ASPNETCORE_HOSTINGSTARTUPASSEMBLIES"));
Assert.Contains("ASPNETCORE_PORT", temp);
Assert.Contains("ASPNETCORE_APPL_PATH", temp);
Assert.Contains("ASPNETCORE_IIS_HTTPAUTH", temp);
Assert.Contains("ASPNETCORE_TOKEN", temp);
Assert.Contains("ASPNETCORE_HOSTINGSTARTUPASSEMBLIES", temp);
// Verify other inherited environment variables
Assert.True(temp.Contains("PROCESSOR_ARCHITECTURE"));
Assert.True(temp.Contains("USERNAME"));
Assert.True(temp.Contains("USERDOMAIN"));
Assert.True(temp.Contains("USERPROFILE"));
Assert.Contains("PROCESSOR_ARCHITECTURE", temp);
Assert.Contains("USERNAME", temp);
Assert.Contains("USERDOMAIN", temp);
Assert.Contains("USERPROFILE", temp);
}
testSite.AspNetCoreApp.RestoreFile("web.config");
@ -410,8 +424,10 @@ namespace AspNetCoreModule.Test
{
string backendProcessId_old = null;
string fileContent = "BackEndAppOffline";
testSite.AspNetCoreApp.CreateFile(new string[] { fileContent }, "App_Offline.Htm");
string appDllFileName = testSite.AspNetCoreApp.GetArgumentFileName();
testSite.AspNetCoreApp.CreateFile(new string[] { fileContent }, "App_Offline.Htm");
for (int i = 0; i < _repeatCount; i++)
{
// check JitDebugger before continuing
@ -423,6 +439,11 @@ namespace AspNetCoreModule.Test
// verify 503
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), fileContent + "\r\n", HttpStatusCode.ServiceUnavailable);
// Verify the application file can be removed under app_offline mode
testSite.AspNetCoreApp.BackupFile(appDllFileName);
testSite.AspNetCoreApp.DeleteFile(appDllFileName);
testSite.AspNetCoreApp.RestoreFile(appDllFileName);
// rename app_offline.htm to _app_offline.htm and verify 200
testSite.AspNetCoreApp.MoveFile("App_Offline.Htm", "_App_Offline.Htm");
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
@ -444,6 +465,8 @@ namespace AspNetCoreModule.Test
{
string backendProcessId_old = null;
string fileContent = "BackEndAppOffline2";
string appDllFileName = testSite.AspNetCoreApp.GetArgumentFileName();
testSite.AspNetCoreApp.CreateFile(new string[] { fileContent }, "App_Offline.Htm");
for (int i = 0; i < _repeatCount; i++)
@ -458,6 +481,11 @@ namespace AspNetCoreModule.Test
string urlForUrlRewrite = testSite.URLRewriteApp.URL + "/Rewrite2/" + testSite.AspNetCoreApp.URL + "/GetProcessId";
await VerifyResponseBody(testSite.RootAppContext.GetUri(urlForUrlRewrite), fileContent + "\r\n", HttpStatusCode.ServiceUnavailable);
// Verify the application file can be removed under app_offline mode
testSite.AspNetCoreApp.BackupFile(appDllFileName);
testSite.AspNetCoreApp.DeleteFile(appDllFileName);
testSite.AspNetCoreApp.RestoreFile(appDllFileName);
// delete app_offline.htm and verify 200
testSite.AspNetCoreApp.DeleteFile("App_Offline.Htm");
string backendProcessId = await GetResponse(testSite.RootAppContext.GetUri(urlForUrlRewrite), HttpStatusCode.OK);
@ -528,7 +556,7 @@ namespace AspNetCoreModule.Test
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
responseBody = await GetResponse(testSite.AspNetCoreApp.GetUri(), HttpStatusCode.BadGateway);
Assert.True(responseBody.Contains("808681"));
Assert.Contains("808681", responseBody);
// verify event error log
Assert.True(TestUtility.RetryHelper((arg1, arg2, arg3) => VerifyApplicationEventLog(arg1, arg2, arg3), errorEventId, startTime, errorMessageContainThis));
@ -574,7 +602,7 @@ namespace AspNetCoreModule.Test
backendProcessId_old = backendProcessId;
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", ""));
//Verifying EventID of new backend process is not necesssary and removed in order to fix some test reliablity issues
//Thread.Sleep(3000);
//Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTimeInsideLooping, backendProcessId), "Verifying event log of new backend process id " + backendProcessId);
@ -582,7 +610,7 @@ namespace AspNetCoreModule.Test
backendProcess.Kill();
Thread.Sleep(3000);
}
Assert.True(rapidFailsTriggered, "Verify 503 error");
Assert.True(rapidFailsTriggered, "Verify 502 Bad Gateway error");
// verify event error log
int errorEventId = 1003;
@ -647,7 +675,7 @@ namespace AspNetCoreModule.Test
processIDs.Add(id);
}
}
Assert.Equal(1, processIDs.Count);
Assert.Single(processIDs);
}
testSite.AspNetCoreApp.RestoreFile("web.config");
@ -662,9 +690,9 @@ namespace AspNetCoreModule.Test
{
int startupDelay = 3; //3 seconds
iisConfig.SetANCMConfig(
testSite.SiteName,
testSite.AspNetCoreApp.Name,
"environmentVariable",
testSite.SiteName,
testSite.AspNetCoreApp.Name,
"environmentVariable",
new string[] { "ANCMTestStartUpDelay", (startupDelay * 1000).ToString() }
);
@ -676,7 +704,7 @@ namespace AspNetCoreModule.Test
{
await VerifyResponseStatus(testSite.AspNetCoreApp.GetUri("DoSleep3000"), HttpStatusCode.BadGateway);
}
else
else
{
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri("DoSleep3000"), "Running", HttpStatusCode.OK);
}
@ -691,12 +719,12 @@ namespace AspNetCoreModule.Test
{
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
{
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "requestTimeout", TimeSpan.Parse(requestTimeout));
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "requestTimeout", TimeSpan.Parse(requestTimeout));
Thread.Sleep(500);
if (requestTimeout.ToString() == "00:02:00")
{
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri("DoSleep65000"), "Running", HttpStatusCode.OK, timeout:70);
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri("DoSleep65000"), "Running", HttpStatusCode.OK, timeout: 70);
}
else if (requestTimeout.ToString() == "00:01:00")
{
@ -732,7 +760,7 @@ namespace AspNetCoreModule.Test
string response = await GetResponse(testSite.AspNetCoreApp.GetUri(""), HttpStatusCode.OK);
Assert.True(response == "Running");
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
@ -749,7 +777,7 @@ namespace AspNetCoreModule.Test
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
// if expectedClosing time is less than the shutdownDelay time, gracefulshutdown is supposed to fail and failure event is expected
if (expectedClosingTime*1000 + 1000 == shutdownDelayTime)
if (expectedClosingTime * 1000 + 1000 == shutdownDelayTime)
{
Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMGracefulShutdownEvent(arg1, arg2), startTime, backendProcessId));
Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMGracefulShutdownEvent(arg1, arg2), startTime, expectedGracefulShutdownResponseStatusCode));
@ -814,7 +842,7 @@ namespace AspNetCoreModule.Test
public static async Task DoProcessPathAndArgumentsTest(IISConfigUtility.AppPoolBitness appPoolBitness, string processPath, string argumentsPrefix)
{
using (var testSite = new TestWebSite(appPoolBitness, "DoProcessPathAndArgumentsTest", copyAllPublishedFiles:true))
using (var testSite = new TestWebSite(appPoolBitness, "DoProcessPathAndArgumentsTest", copyAllPublishedFiles: true))
{
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
{
@ -848,7 +876,7 @@ namespace AspNetCoreModule.Test
testSite.AspNetCoreApp.RestoreFile("web.config");
}
}
public static async Task DoForwardWindowsAuthTokenTest(IISConfigUtility.AppPoolBitness appPoolBitness, bool enabledForwardWindowsAuthToken)
{
using (var testSite = new TestWebSite(appPoolBitness, "DoForwardWindowsAuthTokenTest"))
@ -858,9 +886,9 @@ namespace AspNetCoreModule.Test
string result = string.Empty;
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "forwardWindowsAuthToken", enabledForwardWindowsAuthToken);
string requestHeaders = await GetResponse(testSite.AspNetCoreApp.GetUri("DumpRequestHeaders"), HttpStatusCode.OK);
Assert.False(requestHeaders.ToUpper().Contains("MS-ASPNETCORE-WINAUTHTOKEN"));
Assert.DoesNotContain("MS-ASPNETCORE-WINAUTHTOKEN", requestHeaders.ToUpper());
iisConfig.EnableIISAuthentication(testSite.SiteName, windows:true, basic:false, anonymous:false);
iisConfig.EnableIISAuthentication(testSite.SiteName, windows: true, basic: false, anonymous: false);
Thread.Sleep(500);
// check JitDebugger before continuing
@ -871,7 +899,7 @@ namespace AspNetCoreModule.Test
if (enabledForwardWindowsAuthToken)
{
string expectedHeaderName = "MS-ASPNETCORE-WINAUTHTOKEN";
Assert.True(requestHeaders.ToUpper().Contains(expectedHeaderName));
Assert.Contains(expectedHeaderName, requestHeaders.ToUpper());
result = await GetResponse(testSite.AspNetCoreApp.GetUri("ImpersonateMiddleware"), HttpStatusCode.OK);
bool compare = false;
@ -892,10 +920,10 @@ namespace AspNetCoreModule.Test
}
else
{
Assert.False(requestHeaders.ToUpper().Contains("MS-ASPNETCORE-WINAUTHTOKEN"));
Assert.DoesNotContain("MS-ASPNETCORE-WINAUTHTOKEN", requestHeaders.ToUpper());
result = await GetResponse(testSite.AspNetCoreApp.GetUri("ImpersonateMiddleware"), HttpStatusCode.OK);
Assert.True(result.Contains("ImpersonateMiddleware-UserName = NoAuthentication"));
Assert.Contains("ImpersonateMiddleware-UserName = NoAuthentication", result);
}
}
@ -915,13 +943,13 @@ namespace AspNetCoreModule.Test
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
{
// allocating 1024,000 KB
await VerifyResponseStatus(testSite.AspNetCoreApp.GetUri("MemoryLeak1024000"), HttpStatusCode.OK);
// get backend process id
string pocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
// get process id of IIS worker process (w3wp.exe)
string userName = testSite.SiteName;
int processIdOfWorkerProcess = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("w3wp.exe", "Handle", userName));
@ -948,7 +976,7 @@ namespace AspNetCoreModule.Test
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
iisConfig.SetAppPoolSetting(testSite.AspNetCoreApp.AppPoolName, "privateMemory", totalPrivateMemoryKB);
// set 100 for rapidFailProtection counter for both IIS worker process and aspnetcore backend process
iisConfig.SetAppPoolSetting(testSite.AspNetCoreApp.AppPoolName, "rapidFailProtectionMaxCrashes", 100);
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "rapidFailsPerMinute", 100);
@ -1029,12 +1057,12 @@ namespace AspNetCoreModule.Test
{
startupClass = "StartupNoCompressionCaching";
}
// set startup class
iisConfig.SetANCMConfig(
testSite.SiteName,
testSite.AspNetCoreApp.Name,
"environmentVariable",
testSite.SiteName,
testSite.AspNetCoreApp.Name,
"environmentVariable",
new string[] { "ANCMTestStartupClassName", startupClass }
);
@ -1091,7 +1119,7 @@ namespace AspNetCoreModule.Test
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
{
string startupClass = "StartupCompressionCaching";
// set startup class
iisConfig.SetANCMConfig(
testSite.SiteName,
@ -1103,7 +1131,7 @@ namespace AspNetCoreModule.Test
// enable IIS compression
// Note: IIS compression, however, will be ignored if AspnetCore compression middleware is enabled.
iisConfig.SetCompression(testSite.SiteName, true);
// prepare static contents
testSite.AspNetCoreApp.CreateDirectory("wwwroot");
testSite.AspNetCoreApp.CreateDirectory(@"wwwroot\pdir");
@ -1149,7 +1177,7 @@ namespace AspNetCoreModule.Test
public static async Task DoSendHTTPSRequestTest(IISConfigUtility.AppPoolBitness appPoolBitness)
{
using (var testSite = new TestWebSite(appPoolBitness, "DoSendHTTPSRequestTest", startIISExpress:false))
using (var testSite = new TestWebSite(appPoolBitness, "DoSendHTTPSRequestTest", startIISExpress: false))
{
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
{
@ -1161,12 +1189,12 @@ namespace AspNetCoreModule.Test
// Add https binding and get https uri information
iisConfig.AddBindingToSite(testSite.SiteName, ipAddress, sslPort, hostName, "https");
// Create a self signed certificate
string thumbPrint = iisConfig.CreateSelfSignedCertificate(subjectName);
// Export the self signed certificate to rootCA
iisConfig.ExportCertificateTo(thumbPrint, sslStoreTo:@"Cert:\LocalMachine\Root");
iisConfig.ExportCertificateTo(thumbPrint, sslStoreTo: @"Cert:\LocalMachine\Root");
// Configure http.sys ssl certificate mapping to IP:Port endpoint with the newly created self signed certificage
iisConfig.SetSSLCertificate(sslPort, hexIPAddress, thumbPrint);
@ -1228,13 +1256,13 @@ namespace AspNetCoreModule.Test
string requestHeaders = string.Empty;
requestHeaders = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("DumpRequestHeaders"), new string[] { "Accept-Encoding", "gzip", requestHeader, requestHeaderValue }, HttpStatusCode.OK);
requestHeaders = requestHeaders.Replace(" ", "");
Assert.False(requestHeaders.ToUpper().Contains(requestHeader.ToLower()+":"));
Assert.DoesNotContain(requestHeader.ToLower() + ":", requestHeaders.ToUpper());
// Verify https request
Uri targetHttpsUri = testSite.AspNetCoreApp.GetUri(null, sslPort, protocol: "https");
requestHeader = await GetResponseAndHeaders(targetHttpsUri, new string[] { "Accept-Encoding", "gzip", requestHeader, requestHeaderValue }, HttpStatusCode.OK);
requestHeaders = requestHeaders.Replace(" ", "");
Assert.False(requestHeaders.ToUpper().Contains(requestHeader.ToLower() + ":"));
Assert.DoesNotContain(requestHeader.ToLower() + ":", requestHeaders.ToUpper());
// Remove the SSL Certificate mapping
iisConfig.RemoveSSLCertificate(sslPort, hexIPAddress);
@ -1273,11 +1301,11 @@ namespace AspNetCoreModule.Test
// Create a certificate for web server setting its issuer with the root certificate subject name
string thumbPrintForWebServer = iisConfig.CreateSelfSignedCertificateWithMakeCert(webServerCN, rootCN, extendedKeyUsage: "1.3.6.1.5.5.7.3.1");
string thumbPrintForKestrel = null;
string thumbPrintForKestrel = null;
// Create a certificate for client authentication setting its issuer with the root certificate subject name
string thumbPrintForClientAuthentication = iisConfig.CreateSelfSignedCertificateWithMakeCert(clientCN, rootCN, extendedKeyUsage: "1.3.6.1.5.5.7.3.2");
// Configure http.sys ssl certificate mapping to IP:Port endpoint with the newly created self signed certificage
iisConfig.SetSSLCertificate(sslPort, hexIPAddress, thumbPrintForWebServer);
@ -1337,7 +1365,8 @@ namespace AspNetCoreModule.Test
// starting IISExpress was deffered after creating test applications and now it is ready to start it
Uri rootHttpsUri = testSite.RootAppContext.GetUri(null, sslPort, protocol: "https");
testSite.StartIISExpress("( invoke-webrequest " + rootHttpsUri.OriginalString + " -CertificateThumbprint " + thumbPrintForClientAuthentication + ").StatusCode");
testSite.StartIISExpress();
TestUtility.RunPowershellScript("( invoke-webrequest " + rootHttpsUri.OriginalString + " -CertificateThumbprint " + thumbPrintForClientAuthentication + ").StatusCode", "200");
// Verify http request with using client certificate
Uri targetHttpsUri = testSite.AspNetCoreApp.GetUri(null, sslPort, protocol: "https");
@ -1348,21 +1377,21 @@ namespace AspNetCoreModule.Test
Uri targetHttpsUriForDumpRequestHeaders = testSite.AspNetCoreApp.GetUri("DumpRequestHeaders", sslPort, protocol: "https");
string outputRawContent = TestUtility.RunPowershellScript("( invoke-webrequest " + targetHttpsUriForDumpRequestHeaders.OriginalString + " -CertificateThumbprint " + thumbPrintForClientAuthentication + ").RawContent.ToString()");
string expectedHeaderName = "MS-ASPNETCORE-CLIENTCERT";
Assert.True(outputRawContent.Contains(expectedHeaderName));
Assert.Contains(expectedHeaderName, outputRawContent);
// Get the value of MS-ASPNETCORE-CLIENTCERT request header again and verify it is matched to its configured public key
Uri targetHttpsUriForCLIENTCERTRequestHeader = testSite.AspNetCoreApp.GetUri("GetRequestHeaderValueMS-ASPNETCORE-CLIENTCERT", sslPort, protocol: "https");
outputRawContent = TestUtility.RunPowershellScript("( invoke-webrequest " + targetHttpsUriForCLIENTCERTRequestHeader.OriginalString + " -CertificateThumbprint " + thumbPrintForClientAuthentication + ").RawContent.ToString()");
Assert.True(outputRawContent.Contains(publicKey));
Assert.Contains(publicKey, outputRawContent);
// Verify non-https request returns 403.4 error
string result = string.Empty;
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri(), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.Forbidden);
Assert.True(result.Contains("403.4"));
Assert.Contains("403.4", result);
// Verify https request without using client certificate returns 403.7
result = await GetResponseAndHeaders(targetHttpsUri, new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.Forbidden);
Assert.True(result.Contains("403.7"));
Assert.Contains("403.7", result);
// Clean up user
temp = TestUtility.RunPowershellScript("net localgroup IIS_IUSRS /Delete " + userName);
@ -1376,7 +1405,7 @@ namespace AspNetCoreModule.Test
iisConfig.DeleteCertificate(thumbPrintForWebServer, @"Cert:\LocalMachine\My");
if (useHTTPSMiddleWare)
{
iisConfig.DeleteCertificate(thumbPrintForKestrel, @"Cert:\LocalMachine\My");
iisConfig.DeleteCertificate(thumbPrintForKestrel, @"Cert:\LocalMachine\My");
}
iisConfig.DeleteCertificate(thumbPrintForClientAuthentication, @"Cert:\CurrentUser\My");
}
@ -1388,25 +1417,32 @@ namespace AspNetCoreModule.Test
{
using (var testSite = new TestWebSite(appPoolBitness, "DoWebSocketTest"))
{
string appDllFileName = testSite.AspNetCoreApp.GetArgumentFileName();
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
{
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "shutdownTimeLimit", 10);
}
DateTime startTime = DateTime.Now;
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
// Get Process ID
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
string backendProcessId_old = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
// Verify WebSocket without setting subprotocol
await VerifyResponseBodyContain(testSite.WebSocketApp.GetUri("echo.aspx"), new string[] { "Socket Open" }, HttpStatusCode.OK); // echo.aspx has hard coded path for the websocket server
// Verify WebSocket subprotocol
await VerifyResponseBodyContain(testSite.WebSocketApp.GetUri("echoSubProtocol.aspx"), new string[] { "Socket Open", "mywebsocketsubprotocol" }, HttpStatusCode.OK); // echoSubProtocol.aspx has hard coded path for the websocket server
// Verify websocket
using (WebSocketClientHelper websocketClient = new WebSocketClientHelper())
{
var frameReturned = websocketClient.Connect(testSite.AspNetCoreApp.GetUri("websocket"), true, true);
Assert.True(frameReturned.Content.Contains("Connection: Upgrade"));
Assert.True(frameReturned.Content.Contains("HTTP/1.1 101 Switching Protocols"));
Assert.Contains("Connection: Upgrade", frameReturned.Content);
Assert.Contains("HTTP/1.1 101 Switching Protocols", frameReturned.Content);
Thread.Sleep(500);
VerifySendingWebSocketData(websocketClient, testData);
@ -1418,8 +1454,489 @@ namespace AspNetCoreModule.Test
Assert.True(frameReturned.FrameType == FrameType.Close, "Closing Handshake");
}
// send a simple request again and verify the response body
// send a simple request and verify the response body
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
Thread.Sleep(500);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
Assert.Equal(backendProcessId_old, backendProcessId);
// Verify server side websocket disconnection
using (WebSocketClientHelper websocketClient = new WebSocketClientHelper())
{
for (int jj = 0; jj < 3; jj++)
{
var frameReturned = websocketClient.Connect(testSite.AspNetCoreApp.GetUri("websocket"), true, true);
Assert.Contains("Connection: Upgrade", frameReturned.Content);
Assert.Contains("HTTP/1.1 101 Switching Protocols", frameReturned.Content);
Thread.Sleep(500);
Assert.True(websocketClient.IsOpened, "Check active connection before starting");
// Send a special string to initiate the server side connection closing
websocketClient.SendTextData("CloseFromServer");
bool connectionClosedFromServer = websocketClient.WaitForWebSocketState(WebSocketState.ConnectionClosed);
// Verify server side connection closing is done successfully
Assert.True(connectionClosedFromServer, "Closing Handshake initiated from Server");
// extract text data from the last frame, which is the close frame
int lastIndex = websocketClient.Connection.DataReceived.Count - 1;
// Verify text data is matched to the string sent by server
Assert.Equal("ClosingFromServer", websocketClient.Connection.DataReceived[lastIndex].TextData);
}
}
// send a simple request and verify the response body
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
Thread.Sleep(500);
backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
Assert.Equal(backendProcessId_old, backendProcessId);
// Verify websocket with app_offline.htm
using (WebSocketClientHelper websocketClient = new WebSocketClientHelper())
{
for (int jj = 0; jj < 3; jj++)
{
testSite.AspNetCoreApp.DeleteFile("App_Offline.Htm");
Thread.Sleep(1000);
var frameReturned = websocketClient.Connect(testSite.AspNetCoreApp.GetUri("websocket"), true, true);
Assert.Contains("Connection: Upgrade", frameReturned.Content);
Assert.Contains("HTTP/1.1 101 Switching Protocols", frameReturned.Content);
Thread.Sleep(500);
VerifySendingWebSocketData(websocketClient, testData);
Thread.Sleep(500);
// put app_offline
testSite.AspNetCoreApp.CreateFile(new string[] { "test" }, "App_Offline.Htm");
Thread.Sleep(1000);
// ToDo: This should be replaced when the server can handle this automaticially
// send a websocket data to invoke the server side websocket disconnection after the app_offline
websocketClient.SendTextData("test");
bool connectionClosedFromServer = websocketClient.WaitForWebSocketState(WebSocketState.ConnectionClosed);
// Verify server side connection closing is done successfully
Assert.True(connectionClosedFromServer, "Closing Handshake initiated from Server");
// extract text data from the last frame, which is the close frame
int lastIndex = websocketClient.Connection.DataReceived.Count - 1;
// Verify text data is matched to the string sent by server
Assert.Equal("ClosingFromServer", websocketClient.Connection.DataReceived[lastIndex].TextData);
// Verify the application file can be removed under app_offline mode
testSite.AspNetCoreApp.BackupFile(appDllFileName);
testSite.AspNetCoreApp.DeleteFile(appDllFileName);
testSite.AspNetCoreApp.RestoreFile(appDllFileName);
}
}
// remove app_offline.htm
testSite.AspNetCoreApp.DeleteFile("App_Offline.Htm");
Thread.Sleep(1000);
/*
BugBug!!! configuration change does not invoke the shutdown message
because IIS does not trigger the change notification event until all websocket connection is gone.
This scenario should be added back when the issue is resolved.
// Verify websocket with configuration change notification
using (WebSocketClientHelper websocketClient = new WebSocketClientHelper())
{
for (int jj = 0; jj < 10; jj++)
{
var frameReturned = websocketClient.Connect(testSite.AspNetCoreApp.GetUri("websocket"), true, true);
Assert.Contains("Connection: Upgrade", frameReturned.Content);
Assert.Contains("HTTP/1.1 101 Switching Protocols", frameReturned.Content);
Thread.Sleep(500);
VerifySendingWebSocketData(websocketClient, testData);
Thread.Sleep(500);
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
{
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "startupTimeLimit", 11 + jj);
Thread.Sleep(1000);
}
// ToDo: This should be replaced when the server can handle this automaticially
// send a websocket data to invoke the server side websocket disconnection after the app_offline
websocketClient.SendTextData("test");
bool connectionClosedFromServer = websocketClient.WaitForWebSocketState(WebSocketState.ConnectionClosed);
// Verify server side connection closing is done successfully
Assert.True(connectionClosedFromServer, "Closing Handshake initiated from Server");
// extract text data from the last frame, which is the close frame
int lastIndex = websocketClient.Connection.DataReceived.Count - 1;
// Verify text data is matched to the string sent by server
Assert.Equal("ClosingFromServer", websocketClient.Connection.DataReceived[lastIndex].TextData);
}
}
*/
// send a simple request and verify the response body
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
}
}
public static async Task DoWebSocketErrorhandlingTest(IISConfigUtility.AppPoolBitness appPoolBitness)
{
try
{
using (var testSite = new TestWebSite(appPoolBitness, "DoWebSocketErrorhandlingTest"))
{
// Verify websocket returns 404 when websocket module is not registered
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
{
// Remove websocketModule
IISConfigUtility.BackupAppHostConfig("DoWebSocketErrorhandlingTest", true);
iisConfig.RemoveModule("WebSocketModule");
using (WebSocketClientHelper websocketClient = new WebSocketClientHelper())
{
var frameReturned = websocketClient.Connect(testSite.AspNetCoreApp.GetUri("websocket"), true, true);
Assert.DoesNotContain("Connection: Upgrade", frameReturned.Content);
//BugBug: Currently we returns 101 here.
//Assert.DoesNotContain("HTTP/1.1 101 Switching Protocols", frameReturned.Content);
}
}
// send a simple request again and verify the response body
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
// roback configuration
IISConfigUtility.RestoreAppHostConfig("DoWebSocketErrorhandlingTest", true);
}
}
catch
{
// roback configuration
IISConfigUtility.RestoreAppHostConfig("DoWebSocketErrorhandlingTest", true);
throw;
}
}
public enum DoAppVerifierTest_ShutDownMode
{
RecycleAppPool,
CreateAppOfflineHtm,
StopAndStartAppPool,
RestartW3SVC,
ConfigurationChangeNotification
}
public enum DoAppVerifierTest_StartUpMode
{
UseGracefulShutdown,
DontUseGracefulShutdown
}
public static async Task DoAppVerifierTest(IISConfigUtility.AppPoolBitness appPoolBitness, bool verifyTimeout, DoAppVerifierTest_StartUpMode startUpMode, DoAppVerifierTest_ShutDownMode shutDownMode, int repeatCount = 2)
{
TestWebSite testSite = null;
bool testResult = false;
testSite = new TestWebSite(appPoolBitness, "DoAppVerifierTest", startIISExpress: false);
if (testSite.IisServerType == ServerType.IISExpress)
{
TestUtility.LogInformation("This test is not valid for IISExpress server type because of IISExpress bug; Once it is resolved, we should activate this test for IISExpress as well");
return;
}
// enable AppVerifier
testSite.AttachAppverifier();
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
{
// Prepare https binding
string hostName = "";
string subjectName = "localhost";
string ipAddress = "*";
string hexIPAddress = "0x00";
int sslPort = InitializeTestMachine.SiteId + 6300;
// Add https binding and get https uri information
iisConfig.AddBindingToSite(testSite.SiteName, ipAddress, sslPort, hostName, "https");
// Create a self signed certificate
string thumbPrint = iisConfig.CreateSelfSignedCertificate(subjectName);
// Export the self signed certificate to rootCA
iisConfig.ExportCertificateTo(thumbPrint, sslStoreTo: @"Cert:\LocalMachine\Root");
// Configure http.sys ssl certificate mapping to IP:Port endpoint with the newly created self signed certificage
iisConfig.SetSSLCertificate(sslPort, hexIPAddress, thumbPrint);
// Set shutdownTimeLimit with 3 seconds and use 5 seconds for delay time to make the shutdownTimeout happen
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "shutdownTimeLimit", 3);
int timeoutValue = 3;
if (verifyTimeout)
{
// set requestTimeout
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "requestTimeout", TimeSpan.Parse("00:01:00")); // 1 minute
// set startupTimeout
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "startupTimeLimit", timeoutValue);
// Set shutdownTimeLimit with 3 seconds and use 5 seconds for delay time to make the shutdownTimeout happen
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "shutdownTimeLimit", timeoutValue);
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "environmentVariable", new string[] { "ANCMTestShutdownDelay", "10" });
}
// starting IISExpress was deffered after creating test applications and now it is ready to start it
testSite.StartIISExpress();
if (verifyTimeout)
{
Thread.Sleep(500);
// initial request which requires more than startup timeout should fails
await VerifyResponseStatus(testSite.AspNetCoreApp.GetUri("DoSleep5000"), HttpStatusCode.BadGateway, timeout: 10);
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri("DoSleep5000"), "Running", HttpStatusCode.OK, timeout: 10);
// request which requires more than request timeout should fails
await VerifyResponseStatus(testSite.AspNetCoreApp.GetUri("DoSleep65000"), HttpStatusCode.BadGateway, timeout: 70);
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri("DoSleep50000"), "Running", HttpStatusCode.OK, timeout: 70);
}
///////////////////////////////////
// Start test sceanrio
///////////////////////////////////
if (startUpMode == DoAppVerifierTest_StartUpMode.DontUseGracefulShutdown)
{
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "environmentVariable", new string[] { "GracefulShutdown", "disabled" });
}
// reset existing worker process process
TestUtility.ResetHelper(ResetHelperMode.KillWorkerProcess);
for (int i = 0; i < repeatCount; i++)
{
// reset worker process id to refresh
testSite.WorkerProcessID = 0;
// send a startup request to start a new worker process
TestUtility.RunPowershellScript("( invoke-webrequest http://localhost:" + testSite.TcpPort + " ).StatusCode", "200", retryCount: 5);
// attach debugger to the worker process
testSite.AttachWinDbg(testSite.WorkerProcessID);
TestUtility.RunPowershellScript("( invoke-webrequest http://localhost:" + testSite.TcpPort + " ).StatusCode", "200", retryCount: 30);
// verify windbg process is started
TestUtility.RunPowershellScript("(get-process -name windbg 2> $null).count", "1", retryCount: 5);
DateTime startTime = DateTime.Now;
// Verify http request
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
// Get Process ID
string backendProcessId_old = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
// Verify WebSocket without setting subprotocol
await VerifyResponseBodyContain(testSite.WebSocketApp.GetUri("echo.aspx"), new string[] { "Socket Open" }, HttpStatusCode.OK); // echo.aspx has hard coded path for the websocket server
// Verify WebSocket subprotocol
await VerifyResponseBodyContain(testSite.WebSocketApp.GetUri("echoSubProtocol.aspx"), new string[] { "Socket Open", "mywebsocketsubprotocol" }, HttpStatusCode.OK); // echoSubProtocol.aspx has hard coded path for the websocket server
string testData = "test";
// Verify websocket
using (WebSocketClientHelper websocketClient = new WebSocketClientHelper())
{
var frameReturned = websocketClient.Connect(testSite.AspNetCoreApp.GetUri("websocket"), true, true);
Assert.Contains("Connection: Upgrade", frameReturned.Content);
Assert.Contains("HTTP/1.1 101 Switching Protocols", frameReturned.Content);
Thread.Sleep(500);
VerifySendingWebSocketData(websocketClient, testData);
Thread.Sleep(500);
frameReturned = websocketClient.Close();
Thread.Sleep(500);
Assert.True(frameReturned.FrameType == FrameType.Close, "Closing Handshake");
}
// send a simple request and verify the response body
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
Thread.Sleep(500);
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
Assert.Equal(backendProcessId_old, backendProcessId);
// Verify server side websocket disconnection
using (WebSocketClientHelper websocketClient = new WebSocketClientHelper())
{
var frameReturned = websocketClient.Connect(testSite.AspNetCoreApp.GetUri("websocket"), true, true);
Assert.Contains("Connection: Upgrade", frameReturned.Content);
Assert.Contains("HTTP/1.1 101 Switching Protocols", frameReturned.Content);
Thread.Sleep(500);
Assert.True(websocketClient.IsOpened, "Check active connection before starting");
// Send a special string to initiate the server side connection closing
websocketClient.SendTextData("CloseFromServer");
bool connectionClosedFromServer = websocketClient.WaitForWebSocketState(WebSocketState.ConnectionClosed);
// Verify server side connection closing is done successfully
Assert.True(connectionClosedFromServer, "Closing Handshake initiated from Server");
// extract text data from the last frame, which is the close frame
int lastIndex = websocketClient.Connection.DataReceived.Count - 1;
// Verify text data is matched to the string sent by server
Assert.Equal("ClosingFromServer", websocketClient.Connection.DataReceived[lastIndex].TextData);
}
// send a simple request and verify the response body
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
Thread.Sleep(500);
backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
Assert.Equal(backendProcessId_old, backendProcessId);
if (startUpMode != DoAppVerifierTest_StartUpMode.DontUseGracefulShutdown)
{
// Verify websocket with app_offline.htm
using (WebSocketClientHelper websocketClient = new WebSocketClientHelper())
{
for (int jj = 0; jj < 10; jj++)
{
testSite.AspNetCoreApp.DeleteFile("App_Offline.Htm");
Thread.Sleep(1000);
var frameReturned = websocketClient.Connect(testSite.AspNetCoreApp.GetUri("websocket"), true, true);
Assert.Contains("Connection: Upgrade", frameReturned.Content);
Assert.Contains("HTTP/1.1 101 Switching Protocols", frameReturned.Content);
Thread.Sleep(500);
VerifySendingWebSocketData(websocketClient, testData);
Thread.Sleep(500);
// put app_offline
testSite.AspNetCoreApp.CreateFile(new string[] { "test" }, "App_Offline.Htm");
Thread.Sleep(500);
// ToDo: remove this when server can handle this automatically
// send a websocket data to invoke the server side websocket disconnection after the app_offline
websocketClient.SendTextData("test");
bool connectionClosedFromServer = websocketClient.WaitForWebSocketState(WebSocketState.ConnectionClosed);
// Verify server side connection closing is done successfully
Assert.True(connectionClosedFromServer, "Closing Handshake initiated from Server");
}
}
// remove app_offline.htm
testSite.AspNetCoreApp.DeleteFile("App_Offline.Htm");
Thread.Sleep(500);
}
// Verify websocket again
using (WebSocketClientHelper websocketClient = new WebSocketClientHelper())
{
var frameReturned = websocketClient.Connect(testSite.AspNetCoreApp.GetUri("websocket"), true, true);
Assert.Contains("Connection: Upgrade", frameReturned.Content);
Assert.Contains("HTTP/1.1 101 Switching Protocols", frameReturned.Content);
Thread.Sleep(500);
VerifySendingWebSocketData(websocketClient, testData);
Thread.Sleep(500);
frameReturned = websocketClient.Close();
Thread.Sleep(500);
Assert.True(frameReturned.FrameType == FrameType.Close, "Closing Handshake");
}
// send a simple request and verify the response body
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
// Verify https request
Uri targetHttpsUri = testSite.AspNetCoreApp.GetUri(null, sslPort, protocol: "https");
var result = await GetResponseAndHeaders(targetHttpsUri, new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
Assert.True(result.Contains("Running"), "verify response body");
switch (shutDownMode)
{
case DoAppVerifierTest_ShutDownMode.StopAndStartAppPool:
iisConfig.StopAppPool(testSite.AspNetCoreApp.AppPoolName);
Thread.Sleep(5000);
iisConfig.StartAppPool(testSite.AspNetCoreApp.AppPoolName);
break;
case DoAppVerifierTest_ShutDownMode.RestartW3SVC:
TestUtility.ResetHelper(ResetHelperMode.StopWasStartW3svc);
break;
case DoAppVerifierTest_ShutDownMode.CreateAppOfflineHtm:
testSite.AspNetCoreApp.DeleteFile("App_Offline.Htm");
testSite.AspNetCoreApp.CreateFile(new string[] { "test" }, "App_Offline.Htm");
break;
case DoAppVerifierTest_ShutDownMode.ConfigurationChangeNotification:
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "startupTimeLimit", timeoutValue + 1);
iisConfig.RecycleAppPool(testSite.AspNetCoreApp.AppPoolName);
break;
case DoAppVerifierTest_ShutDownMode.RecycleAppPool:
iisConfig.RecycleAppPool(testSite.AspNetCoreApp.AppPoolName);
break;
}
Thread.Sleep(2000);
if (verifyTimeout)
{
// Wait for shutdown delay additionally
Thread.Sleep(timeoutValue * 1000);
}
switch (shutDownMode)
{
case DoAppVerifierTest_ShutDownMode.CreateAppOfflineHtm:
// verify app_offline.htm file works
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "test" + "\r\n", HttpStatusCode.ServiceUnavailable);
// remove app_offline.htm file and then recycle apppool
testSite.AspNetCoreApp.MoveFile("App_Offline.Htm", "_App_Offline.Htm");
iisConfig.RecycleAppPool(testSite.AspNetCoreApp.AppPoolName);
Thread.Sleep(2000);
break;
}
// verify windbg process is gone, which means there was no unexpected error
TestUtility.RunPowershellScript("(get-process -name windbg 2> $null).count", "0", retryCount: 5);
}
// clean up https test environment
// Remove the SSL Certificate mapping
iisConfig.RemoveSSLCertificate(sslPort, hexIPAddress);
// Remove the newly created self signed certificate
iisConfig.DeleteCertificate(thumbPrint);
// Remove the exported self signed certificate on rootCA
iisConfig.DeleteCertificate(thumbPrint, @"Cert:\LocalMachine\Root");
}
// cleanup
if (testSite != null)
{
testSite.DetachAppverifier();
}
TestUtility.ResetHelper(ResetHelperMode.KillWorkerProcess);
// cleanup windbg process incase it is still running
if (testResult == false)
{
TestUtility.RunPowershellScript("stop-process -Name windbg -Force -Confirm:$false 2> $null");
}
}
@ -1561,11 +2078,11 @@ namespace AspNetCoreModule.Test
Assert.Null(response.Headers.ConnectionClose);
Assert.Null(GetContentLength(response));
}
catch (XunitException)
catch (XunitException ex)
{
TestUtility.LogInformation(response.ToString());
TestUtility.LogInformation(responseText);
throw;
throw ex;
}
}
@ -1786,7 +2303,7 @@ namespace AspNetCoreModule.Test
}
foreach (string item in expectedStringsInResponseBody)
{
Assert.True(responseText.Contains(item));
Assert.Contains(item, responseText);
}
}
Assert.Equal(expectedResponseStatus, response.StatusCode);

Просмотреть файл

@ -1,12 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System.Text;
namespace AspNetCoreModule.Test.WebSocketClient
{
public class Frame
{
private int startingIndex; // This will be initialized as output parameter of GetFrameString()
public int DataLength; // This will be initialized as output parameter of GetFrameString()
public int DataLength = 0; // This will be initialized as output parameter of GetFrameString()
public Frame(byte[] data)
{
@ -18,6 +20,18 @@ namespace AspNetCoreModule.Test.WebSocketClient
public FrameType FrameType { get; set; }
public byte[] Data { get; private set; }
public string TextData {
get
{
if (DataLength == 0)
{
throw new System.Exception("DataLength is zero");
}
return Encoding.ASCII.GetString(Data, startingIndex, DataLength);
}
}
public string Content { get; private set; }
public bool IsMasked { get; private set; }

Просмотреть файл

@ -7,7 +7,6 @@ using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Threading;
using Xunit;
namespace AspNetCoreModule.Test.WebSocketClient
{
@ -33,6 +32,34 @@ namespace AspNetCoreModule.Test.WebSocketClient
}
}
public bool WaitForWebSocketState(WebSocketState expectedState, int timeout = 3000)
{
bool result = false;
int RETRYMAX = 300;
int INTERVAL = 100; // ms
if (timeout > RETRYMAX * INTERVAL)
{
throw new Exception("timeout should be less than " + 100 * 300);
}
for (int i=0; i<RETRYMAX; i++)
{
if (i*100 > timeout)
{
break;
}
if (this.WebSocketState == expectedState)
{
result = true;
break;
}
else
{
Thread.Sleep(INTERVAL);
}
}
return result;
}
public Frame Connect(Uri address, bool storeData, bool isAlwaysReading)
{
Address = address;
@ -44,7 +71,11 @@ namespace AspNetCoreModule.Test.WebSocketClient
InitiateWithAlwaysReading();
}
SendWebSocketRequest(WebSocketClientUtility.WebSocketVersion);
Thread.Sleep(3000);
if (!WaitForWebSocketState(WebSocketState.ConnectionOpen))
{
throw new Exception("Failed to open a connection");
}
Frame openingFrame = null;
@ -53,23 +84,21 @@ namespace AspNetCoreModule.Test.WebSocketClient
else
openingFrame = Connection.DataReceived[0];
Thread.Sleep(1000);
IsOpened = true;
return openingFrame;
}
public Frame Close()
{
CloseConnection();
Thread.Sleep(1000);
Frame closeFrame = null;
if (!IsAlwaysReading)
closeFrame = ReadData();
else
closeFrame = Connection.DataReceived[Connection.DataReceived.Count - 1];
IsOpened = false;
return closeFrame;
}
@ -137,7 +166,7 @@ namespace AspNetCoreModule.Test.WebSocketClient
Encoding.UTF8.GetString(outputData, 0, outputData.Length));
}
}
public void ReadDataCallback(IAsyncResult result)
{
WebSocketConnect client = (WebSocketConnect) result.AsyncState;
@ -190,28 +219,17 @@ namespace AspNetCoreModule.Test.WebSocketClient
// Create frame with the tempBuffer
Frame frame = new Frame(tempBuffer);
int nextFrameIndex = 0;
ProcessReceivedData(frame);
int nextFrameIndex = frame.IndexOfNextFrame;
while (nextFrameIndex != -1)
{
TestUtility.LogInformation("ReadDataCallback: Client {0:D3}: Read Type {1} : {2} ", Connection.Id, frame.FrameType, bytesReadIntotal);
tempBuffer = tempBuffer.SubArray(frame.IndexOfNextFrame, tempBuffer.Length - frame.IndexOfNextFrame);
frame = new Frame(tempBuffer);
ProcessReceivedData(frame);
// Send Pong if the frame was Ping
if (frame.FrameType == FrameType.Ping)
SendPong(frame);
nextFrameIndex = frame.IndexOfNextFrame;
if (nextFrameIndex != -1)
{
tempBuffer = tempBuffer.SubArray(frame.IndexOfNextFrame, tempBuffer.Length - frame.IndexOfNextFrame);
frame = new Frame(tempBuffer);
}
}
// Send Pong if the frame was Ping
if (frame.FrameType == FrameType.Ping)
SendPong(frame);
if (client.IsDisposed)
return;
@ -271,6 +289,10 @@ namespace AspNetCoreModule.Test.WebSocketClient
{
Send(Frames.PONG);
}
public void SendClose()
{
Send(Frames.CLOSE_FRAME);
}
public void SendPong(Frame receivedPing)
{
@ -291,8 +313,13 @@ namespace AspNetCoreModule.Test.WebSocketClient
public void CloseConnection()
{
Connection.Done = true;
this.WebSocketState = WebSocketState.ClosingFromClientStarted;
Send(Frames.CLOSE_FRAME);
if (!WaitForWebSocketState(WebSocketState.ConnectionClosed))
{
throw new Exception("Failed to close a connection");
}
}
public static void WriteCallback(IAsyncResult result)
@ -336,12 +363,45 @@ namespace AspNetCoreModule.Test.WebSocketClient
private void ProcessReceivedData(Frame frame)
{
if (frame.Content.Contains("Connection: Upgrade")
&& frame.Content.Contains("Upgrade: Websocket")
&& frame.Content.Contains("HTTP/1.1 101 Switching Protocols"))
WebSocketState = WebSocketState.ConnectionOpen;
if (frame.FrameType == FrameType.Close)
WebSocketState = WebSocketState.ConnectionClosed;
TestUtility.LogInformation("ReadDataCallback: Client {0:D3}: Read Type {1} : {2} ", Connection.Id, frame.FrameType, frame.DataLength);
if (frame.FrameType == FrameType.NonControlFrame)
{
string content = frame.Content.ToLower();
if (content.Contains("connection: upgrade")
&& content.Contains("upgrade: websocket")
&& content.Contains("http/1.1 101 switching protocols"))
{
TestUtility.LogInformation("Connection opened...");
TestUtility.LogInformation(frame.Content);
WebSocketState = WebSocketState.ConnectionOpen;
}
}
else
{
// Send Pong if the frame was Ping
if (frame.FrameType == FrameType.Ping)
SendPong(frame);
// Send Close if the frame was Close
if (frame.FrameType == FrameType.Close)
{
if (WebSocketState == WebSocketState.ConnectionClosed)
{
throw new Exception("Connection was already closed");
}
else
{
if (WebSocketState != WebSocketState.ClosingFromClientStarted)
{
TestUtility.LogInformation("Send back Close frame to responsd server closing...");
SendClose();
}
TestUtility.LogInformation(frame.Content);
WebSocketState = WebSocketState.ConnectionClosed;
IsOpened = false;
}
}
}
ProcessData(frame, false);
}

Просмотреть файл

@ -35,9 +35,7 @@ namespace AspNetCoreModule.Test.WebSocketClient
public byte[] InputData { get; set; }
public bool IsDisposed { get; set; }
public bool Done { get; set; }
public int Id { get; set; }
public MyTcpClient TcpClient { get; set; }

Просмотреть файл

@ -7,6 +7,7 @@ namespace AspNetCoreModule.Test.WebSocketClient
{
NonWebSocket,
ConnectionOpen,
ClosingFromClientStarted,
ConnectionClosed
}
}

Просмотреть файл

@ -15,6 +15,7 @@ namespace AspnetCoreModule.TestSites.Standard
public static class Program
{
public static IApplicationLifetime AappLifetime;
public static bool AappLifetimeStopping = false;
public static int GracefulShutdownDelayTime = 0;
private static X509Certificate2 _x509Certificate2;
@ -153,16 +154,21 @@ namespace AspnetCoreModule.TestSites.Standard
}
AappLifetime.ApplicationStarted.Register(
() => Thread.Sleep(1000)
() => {
Thread.Sleep(1000);
}
);
AappLifetime.ApplicationStopping.Register(
() => Thread.Sleep(Startup.SleeptimeWhileClosing / 2)
() => {
AappLifetimeStopping = true;
Thread.Sleep(Startup.SleeptimeWhileClosing / 2);
}
);
AappLifetime.ApplicationStopped.Register(
() => {
Thread.Sleep(Startup.SleeptimeWhileClosing / 2);
Startup.SleeptimeWhileClosing = 0; // All of SleeptimeWhileClosing is used now
}
}
);
try
{

Просмотреть файл

@ -32,17 +32,36 @@ namespace AspnetCoreModule.TestSites.Standard
// the below line is not required at present, however keeping in case the default value is changed later.
options.ForwardWindowsAuthentication = true;
});
}
}
private async Task Echo(WebSocket webSocket)
{
var buffer = new byte[1024 * 4];
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
bool closeFromServer = false;
while (!result.CloseStatus.HasValue)
{
await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
if ((result.Count == "CloseFromServer".Length && System.Text.Encoding.ASCII.GetString(buffer).Substring(0, result.Count) == "CloseFromServer")
|| Program.AappLifetimeStopping == true)
{
// start closing handshake from backend process
await webSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "ClosingFromServer", CancellationToken.None);
closeFromServer = true;
}
else
{
await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
}
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
}
if (closeFromServer)
{
return;
}
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}

Просмотреть файл

@ -0,0 +1,366 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style>
table {
border: 0
}
.commslog-data {
font-family: Consolas, Courier New, Courier, monospace;
}
.commslog-server {
background-color: red;
color: white
}
.commslog-client {
background-color: green;
color: white
}
</style>
<style type="text/css">
body {
background-repeat: no-repeat;
padding-top: 85px;
font-family: Trebuchet MS, Lucida Sans Unicode, Arial, sans-serif;
font-size: 0.9em;
line-height: 130%;
}
.selectBoxArrow {
margin-top: 1px;
float: left;
position: absolute;
right: 1px;
}
.selectBoxInput {
border: 0px;
padding-left: 1px;
height: 16px;
position: absolute;
top: 0px;
left: 0px;
width: 600px;
}
.selectBox {
border: 1px solid #7f9db9;
height: 20px;
}
.selectBoxOptionContainer {
position: absolute;
border: 1px solid #7f9db9;
height: 100px;
background-color: #FFF;
left: -1px;
top: 20px;
visibility: hidden;
overflow: auto;
}
.selectBoxAnOption {
font-family: arial;
font-size: 12px;
cursor: default;
margin: 1px;
overflow: hidden;
white-space: nowrap;
}
.selectBoxIframe {
position: absolute;
background-color: #FFF;
border: 0px;
z-index: 999;
}
</style>
<script type="text/javascript">
// Path to arrow images
var arrowImage = './images/select_arrow.gif'; // Regular arrow
var arrowImageOver = './images/select_arrow_over.gif'; // Mouse over
var arrowImageDown = './images/select_arrow_down.gif'; // Mouse down
var selectBoxIds = 0;
var currentlyOpenedOptionBox = false;
var editableSelect_activeArrow = false;
function selectBox_switchImageUrl() {
if (this.src.indexOf(arrowImage) >= 0) {
this.src = this.src.replace(arrowImage, arrowImageOver);
} else {
this.src = this.src.replace(arrowImageOver, arrowImage);
}
}
function selectBox_showOptions() {
if (editableSelect_activeArrow && editableSelect_activeArrow != this) {
editableSelect_activeArrow.src = arrowImage;
}
editableSelect_activeArrow = this;
var numId = this.id.replace(/[^\d]/g, '');
var optionDiv = document.getElementById('selectBoxOptions' + numId);
if (optionDiv.style.display == 'block') {
optionDiv.style.display = 'none';
if (navigator.userAgent.indexOf('MSIE') >= 0) document.getElementById('selectBoxIframe' + numId).style.display = 'none';
this.src = arrowImageOver;
} else {
optionDiv.style.display = 'block';
if (navigator.userAgent.indexOf('MSIE') >= 0) document.getElementById('selectBoxIframe' + numId).style.display = 'block';
this.src = arrowImageDown;
if (currentlyOpenedOptionBox && currentlyOpenedOptionBox != optionDiv) currentlyOpenedOptionBox.style.display = 'none';
currentlyOpenedOptionBox = optionDiv;
}
}
function selectOptionValue() {
var parentNode = this.parentNode.parentNode;
var textInput = parentNode.getElementsByTagName('INPUT')[0];
textInput.value = this.innerHTML;
this.parentNode.style.display = 'none';
document.getElementById('arrowSelectBox' + parentNode.id.replace(/[^\d]/g, '')).src = arrowImageOver;
if (navigator.userAgent.indexOf('MSIE') >= 0) document.getElementById('selectBoxIframe' + parentNode.id.replace(/[^\d]/g, '')).style.display = 'none';
}
var activeOption;
function highlightSelectBoxOption() {
if (this.style.backgroundColor == '#316AC5') {
this.style.backgroundColor = '';
this.style.color = '';
} else {
this.style.backgroundColor = '#316AC5';
this.style.color = '#FFF';
}
if (activeOption) {
activeOption.style.backgroundColor = '';
activeOption.style.color = '';
}
activeOption = this;
}
function createEditableSelect(dest) {
dest.className = 'selectBoxInput';
var div = document.createElement('DIV');
div.style.styleFloat = 'left';
div.style.width = dest.offsetWidth + 16 + 'px';
div.style.position = 'relative';
div.id = 'selectBox' + selectBoxIds;
var parent = dest.parentNode;
parent.insertBefore(div, dest);
div.appendChild(dest);
div.className = 'selectBox';
div.style.zIndex = 10000 - selectBoxIds;
var img = document.createElement('IMG');
img.src = arrowImage;
img.className = 'selectBoxArrow';
img.onmouseover = selectBox_switchImageUrl;
img.onmouseout = selectBox_switchImageUrl;
img.onclick = selectBox_showOptions;
img.id = 'arrowSelectBox' + selectBoxIds;
div.appendChild(img);
var optionDiv = document.createElement('DIV');
optionDiv.id = 'selectBoxOptions' + selectBoxIds;
optionDiv.className = 'selectBoxOptionContainer';
optionDiv.style.width = div.offsetWidth - 2 + 'px';
div.appendChild(optionDiv);
if (navigator.userAgent.indexOf('MSIE') >= 0) {
var iframe = document.createElement('<IFRAME src="about:blank" frameborder=0>');
iframe.style.width = optionDiv.style.width;
iframe.style.height = optionDiv.offsetHeight + 'px';
iframe.style.display = 'none';
iframe.id = 'selectBoxIframe' + selectBoxIds;
div.appendChild(iframe);
}
if (dest.getAttribute('selectBoxOptions')) {
var options = dest.getAttribute('selectBoxOptions').split(';');
var optionsTotalHeight = 0;
var optionArray = new Array();
for (var no = 0; no < options.length; no++) {
var anOption = document.createElement('DIV');
anOption.innerHTML = options[no];
anOption.className = 'selectBoxAnOption';
anOption.onclick = selectOptionValue;
anOption.style.width = optionDiv.style.width.replace('px', '') - 2 + 'px';
anOption.onmouseover = highlightSelectBoxOption;
optionDiv.appendChild(anOption);
optionsTotalHeight = optionsTotalHeight + anOption.offsetHeight;
optionArray.push(anOption);
}
if (optionsTotalHeight > optionDiv.offsetHeight) {
for (var no = 0; no < optionArray.length; no++) {
optionArray[no].style.width = optionDiv.style.width.replace('px', '') - 22 + 'px';
}
}
optionDiv.style.display = 'none';
optionDiv.style.visibility = 'visible';
}
selectBoxIds = selectBoxIds + 1;
}
</script>
</head>
<body>
<h1>WebSocket Sample Application</h1>
<p id="stateLabel">Ready to connect...</p>
<div>
<label for="connectionUrl">WebSocket Server URL:</label>
<input id="connectionUrl" />
<button id="connectButton" type="submit">Connect</button>
</div>
<p></p>
<div>
<label for="sendMessage">Message to send:</label>
<input id="sendMessage" disabled selectBoxOptions="CloseFromServerBugRepro;CloseFromServerWithIgnoringExceptionError" />
<button id="sendButton" type="submit" disabled>Send</button>
<button id="closeButton" disabled>Close Socket</button>
<button id="clearButton">Clear Log</button>
</div>
<h2>Communication Log</h2>
<table style="width: 800px">
<thead>
<tr>
<td style="width: 100px">From</td>
<td style="width: 100px">To</td>
<td>Data</td>
</tr>
</thead>
<tbody id="commsLog"></tbody>
</table>
<script type="text/javascript">
createEditableSelect(document.getElementById("sendMessage"));
</script>
<script>
var connectionForm = document.getElementById("connectionForm");
var connectionUrl = document.getElementById("connectionUrl");
var connectButton = document.getElementById("connectButton");
var stateLabel = document.getElementById("stateLabel");
var sendMessage = document.getElementById("sendMessage");
var sendButton = document.getElementById("sendButton");
var sendForm = document.getElementById("sendForm");
var commsLog = document.getElementById("commsLog");
var socket;
var scheme = document.location.protocol == "https:" ? "wss" : "ws";
var port = document.location.port ? (":" + document.location.port) : "";
connectionUrl.value = scheme + "://" + document.location.hostname + port + "/ws";
function updateState() {
function disable() {
sendMessage.disabled = true;
sendButton.disabled = true;
closeButton.disabled = true;
}
function enable() {
sendMessage.disabled = false;
sendButton.disabled = false;
closeButton.disabled = false;
}
connectionUrl.disabled = true;
connectButton.disabled = true;
if (!socket) {
disable();
} else {
switch (socket.readyState) {
case WebSocket.CLOSED:
stateLabel.innerHTML = "Closed";
disable();
connectionUrl.disabled = false;
connectButton.disabled = false;
break;
case WebSocket.CLOSING:
stateLabel.innerHTML = "Closing...";
disable();
break;
case WebSocket.CONNECTING:
stateLabel.innerHTML = "Connecting...";
disable();
break;
case WebSocket.OPEN:
stateLabel.innerHTML = "Open";
enable();
break;
default:
stateLabel.innerHTML = "Unknown WebSocket State: " + socket.readyState;
disable();
break;
}
}
}
closeButton.onclick = function () {
if (!socket || socket.readyState != WebSocket.OPEN) {
alert("socket not connected");
}
socket.close(1000, "Closing from client");
}
clearButton.onclick = function () {
var table = document.getElementById("commsLog");
table.innerHTML = "";
}
sendButton.onclick = function () {
if (!socket || socket.readyState != WebSocket.OPEN) {
alert("socket not connected");
}
var data = sendMessage.value;
socket.send(data);
//document.getElementById("sendMessage").value = "";
commsLog.innerHTML += '<tr>' +
'<td class="commslog-client">Client</td>' +
'<td class="commslog-server">Server</td>' +
'<td class="commslog-data">' + data + '</td>'
'</tr>';
}
connectButton.onclick = function () {
stateLabel.innerHTML = "Connecting";
socket = new WebSocket(connectionUrl.value);
socket.onopen = function (event) {
updateState();
commsLog.innerHTML += '<tr>' +
'<td colspan="3" class="commslog-data">Connection opened</td>' +
'</tr>';
};
socket.onclose = function (event) {
updateState();
commsLog.innerHTML += '<tr>' +
'<td colspan="3" class="commslog-data">Connection closed. Code: ' + event.code + '. Reason: ' + event.reason + '</td>' +
'</tr>';
};
socket.onerror = updateState;
socket.onmessage = function (event) {
commsLog.innerHTML += '<tr>' +
'<td class="commslog-server">Server</td>' +
'<td class="commslog-client">Client</td>' +
'<td class="commslog-data">' + event.data + '</td>'
'</tr>';
};
};
</script>
</body>
</html>

Двоичные данные
test/WebRoot/WebSocket/images/select_arrow.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 636 B

Двоичные данные
test/WebRoot/WebSocket/images/select_arrow_down.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 650 B

Двоичные данные
test/WebRoot/WebSocket/images/select_arrow_over.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 639 B

Просмотреть файл

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
</configuration>

Просмотреть файл

@ -0,0 +1,53 @@
using AspNetCoreModule.Test.Framework;
using AspNetCoreModule.Test.WebSocketClient;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WebSocketClientEXE
{
class Program
{
static void Main(string[] args)
{
using (WebSocketClientHelper websocketClient = new WebSocketClientHelper())
{
if (args.Length == 0)
{
TestUtility.LogInformation("Usage: WebSocketClientEXE http://localhost:40000/aspnetcoreapp/websocket");
return;
}
string url = "http://localhost:40000/aspnetcoreapp/websocket";
if (args[0].Contains("http:"))
{
url = args[0];
}
var frameReturned = websocketClient.Connect(new Uri(url), true, true);
TestUtility.LogInformation(frameReturned.Content);
TestUtility.LogInformation("Type any data and Enter key ('Q' to quit): ");
while (true)
{
Thread.Sleep(500);
if (!websocketClient.IsOpened)
{
TestUtility.LogInformation("Connection closed...");
break;
}
string data = Console.ReadLine();
if (data.Trim().ToLower() == "q")
{
frameReturned = websocketClient.Close();
TestUtility.LogInformation(frameReturned.Content);
break;
}
websocketClient.SendTextData(data);
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("WebSocketClientEXE")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WebSocketClientEXE")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("4062ea94-75f5-4691-86dc-c8594ba896de")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Просмотреть файл

@ -0,0 +1,15 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
namespace AspNetCoreModule.Test.Framework
{
public class TestUtility
{
public static void LogInformation(string format, params object[] parameters)
{
Console.WriteLine(format, parameters);
}
}
}

Просмотреть файл

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{4062EA94-75F5-4691-86DC-C8594BA896DE}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>WebSocketClientEXE</RootNamespace>
<AssemblyName>WebSocketClientEXE</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\AspNetCoreModule.Test\WebSocketClientHelper\Frame.cs">
<Link>Frame.cs</Link>
</Compile>
<Compile Include="..\AspNetCoreModule.Test\WebSocketClientHelper\Frames.cs">
<Link>Frames.cs</Link>
</Compile>
<Compile Include="..\AspNetCoreModule.Test\WebSocketClientHelper\FrameType.cs">
<Link>FrameType.cs</Link>
</Compile>
<Compile Include="..\AspNetCoreModule.Test\WebSocketClientHelper\WebSocketClientHelper.cs">
<Link>WebSocketClientHelper.cs</Link>
</Compile>
<Compile Include="..\AspNetCoreModule.Test\WebSocketClientHelper\WebSocketClientUtility.cs">
<Link>WebSocketClientUtility.cs</Link>
</Compile>
<Compile Include="..\AspNetCoreModule.Test\WebSocketClientHelper\WebSocketConnect.cs">
<Link>WebSocketConnect.cs</Link>
</Compile>
<Compile Include="..\AspNetCoreModule.Test\WebSocketClientHelper\WebSocketConstants.cs">
<Link>WebSocketConstants.cs</Link>
</Compile>
<Compile Include="..\AspNetCoreModule.Test\WebSocketClientHelper\WebSocketState.cs">
<Link>WebSocketState.cs</Link>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TestUtility.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>