Added new test scenarios for websocket (#153)
This commit is contained in:
Родитель
a732b106f5
Коммит
c5f59e06c3
|
@ -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>
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 636 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 650 B |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 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>
|
Загрузка…
Ссылка в новой задаче