diff --git a/AspNetCoreModule.sln b/AspNetCoreModule.sln index a8ba10859c..e2ab3a60d5 100644 --- a/AspNetCoreModule.sln +++ b/AspNetCoreModule.sln @@ -12,6 +12,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IISLib", "src\IISLib\IISLib EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{02F461DC-5166-4E88-AAD5-CF110016A647}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AspNetCoreModule.Test", "test\AspNetCoreModule.Test\AspNetCoreModule.Test.xproj", "{4DDA7560-AA29-4161-A5EA-A7E8F3997321}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2097C03C-E2F7-4396-B3BC-4335F1B87B5E}" ProjectSection(SolutionItems) = preProject global.json = global.json @@ -19,6 +21,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{FDD2EDF8-1B62-4978-9815-9D95260B8B91}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AspnetCoreModule.TestSites.Standard", "test\AspNetCoreModule.TestSites.Standard\AspnetCoreModule.TestSites.Standard.xproj", "{030225D8-4EE8-47E5-B692-2A96B3B51A38}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -49,6 +53,30 @@ Global {4787A64F-9A3E-4867-A55A-70CB4B2B2FFE}.Release|Win32.Build.0 = Release|Win32 {4787A64F-9A3E-4867-A55A-70CB4B2B2FFE}.Release|x64.ActiveCfg = Release|Win32 {4787A64F-9A3E-4867-A55A-70CB4B2B2FFE}.Release|x64.Build.0 = Release|Win32 + {4DDA7560-AA29-4161-A5EA-A7E8F3997321}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4DDA7560-AA29-4161-A5EA-A7E8F3997321}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4DDA7560-AA29-4161-A5EA-A7E8F3997321}.Debug|Win32.ActiveCfg = Debug|Any CPU + {4DDA7560-AA29-4161-A5EA-A7E8F3997321}.Debug|Win32.Build.0 = Debug|Any CPU + {4DDA7560-AA29-4161-A5EA-A7E8F3997321}.Debug|x64.ActiveCfg = Debug|Any CPU + {4DDA7560-AA29-4161-A5EA-A7E8F3997321}.Debug|x64.Build.0 = Debug|Any CPU + {4DDA7560-AA29-4161-A5EA-A7E8F3997321}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4DDA7560-AA29-4161-A5EA-A7E8F3997321}.Release|Any CPU.Build.0 = Release|Any CPU + {4DDA7560-AA29-4161-A5EA-A7E8F3997321}.Release|Win32.ActiveCfg = Release|Any CPU + {4DDA7560-AA29-4161-A5EA-A7E8F3997321}.Release|Win32.Build.0 = Release|Any CPU + {4DDA7560-AA29-4161-A5EA-A7E8F3997321}.Release|x64.ActiveCfg = Release|Any CPU + {4DDA7560-AA29-4161-A5EA-A7E8F3997321}.Release|x64.Build.0 = Release|Any CPU + {030225D8-4EE8-47E5-B692-2A96B3B51A38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {030225D8-4EE8-47E5-B692-2A96B3B51A38}.Debug|Any CPU.Build.0 = Debug|Any CPU + {030225D8-4EE8-47E5-B692-2A96B3B51A38}.Debug|Win32.ActiveCfg = Debug|Any CPU + {030225D8-4EE8-47E5-B692-2A96B3B51A38}.Debug|Win32.Build.0 = Debug|Any CPU + {030225D8-4EE8-47E5-B692-2A96B3B51A38}.Debug|x64.ActiveCfg = Debug|Any CPU + {030225D8-4EE8-47E5-B692-2A96B3B51A38}.Debug|x64.Build.0 = Debug|Any CPU + {030225D8-4EE8-47E5-B692-2A96B3B51A38}.Release|Any CPU.ActiveCfg = Release|Any CPU + {030225D8-4EE8-47E5-B692-2A96B3B51A38}.Release|Any CPU.Build.0 = Release|Any CPU + {030225D8-4EE8-47E5-B692-2A96B3B51A38}.Release|Win32.ActiveCfg = Release|Any CPU + {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -56,5 +84,7 @@ Global GlobalSection(NestedProjects) = preSolution {439824F9-1455-4CC4-BD79-B44FA0A16552} = {FDD2EDF8-1B62-4978-9815-9D95260B8B91} {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} EndGlobalSection EndGlobal diff --git a/NuGet.config b/NuGet.config new file mode 100644 index 0000000000..4e531e985d --- /dev/null +++ b/NuGet.config @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/test/AspNetCoreModule.Test/AspNetCoreModule.Test.ForVS.csproj b/test/AspNetCoreModule.Test/AspNetCoreModule.Test.ForVS.csproj new file mode 100644 index 0000000000..dd35d6f469 --- /dev/null +++ b/test/AspNetCoreModule.Test/AspNetCoreModule.Test.ForVS.csproj @@ -0,0 +1,155 @@ + + + + + Debug + AnyCPU + {FB383E4C-A762-4FEA-BEFF-B3E6F4FA40C5} + Library + Properties + AspNetCoreModule.FunctionalTest + AspNetCoreModule.FunctionalTest + v4.5.1 + 512 + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\packages\dotnet-test-xunit.2.2.0-preview2-build1029\lib\net451\dotnet-test-xunit.exe + True + + + ..\..\..\..\Users\jhkim\.nuget\packages\Microsoft.AspNetCore.Server.IntegrationTesting\0.3.0-preview1-22821\lib\net451\Microsoft.AspNetCore.Server.IntegrationTesting.dll + + + ..\..\packages\Microsoft.AspNetCore.Server.Testing.0.2.0-alpha1-21873\lib\net451\Microsoft.AspNetCore.Server.Testing.dll + True + + + ..\..\packages\Microsoft.AspNetCore.Testing.1.2.0-preview1-22815\lib\net451\Microsoft.AspNetCore.Testing.dll + True + + + ..\..\packages\Microsoft.Extensions.Configuration.Abstractions.1.2.0-preview1-22821\lib\netstandard1.0\Microsoft.Extensions.Configuration.Abstractions.dll + True + + + ..\..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.1.2.0-preview1-22821\lib\netstandard1.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + True + + + ..\..\packages\Microsoft.Extensions.FileProviders.Abstractions.1.1.0-preview1-final\lib\netstandard1.0\Microsoft.Extensions.FileProviders.Abstractions.dll + True + + + ..\..\packages\Microsoft.Extensions.FileProviders.Embedded.1.1.0-preview1-final\lib\net451\Microsoft.Extensions.FileProviders.Embedded.dll + True + + + ..\..\packages\Microsoft.Extensions.Logging.1.2.0-preview1-22821\lib\netstandard1.1\Microsoft.Extensions.Logging.dll + True + + + ..\..\packages\Microsoft.Extensions.Logging.Abstractions.1.2.0-preview1-22821\lib\netstandard1.1\Microsoft.Extensions.Logging.Abstractions.dll + True + + + ..\..\packages\Microsoft.Extensions.Logging.Console.1.2.0-preview1-22821\lib\net451\Microsoft.Extensions.Logging.Console.dll + True + + + ..\..\packages\Microsoft.Extensions.PlatformAbstractions.1.2.0-preview1-22821\lib\net451\Microsoft.Extensions.PlatformAbstractions.dll + True + + + ..\..\packages\Microsoft.Extensions.Primitives.1.2.0-preview1-22821\lib\netstandard1.0\Microsoft.Extensions.Primitives.dll + True + + + ..\..\packages\Microsoft.Net.Http.Headers.1.2.0-preview1-22821\lib\netstandard1.1\Microsoft.Net.Http.Headers.dll + True + + + ..\..\packages\Microsoft.Web.Administration.7.0.0.0\lib\net20\Microsoft.Web.Administration.dll + True + + + ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + True + + + + ..\..\packages\System.Buffers.4.3.0\lib\netstandard1.1\System.Buffers.dll + True + + + + + + + + + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.4.3.0\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll + True + + + ..\..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll + True + + + + + + + + + + ..\..\packages\xunit.abstractions.2.0.1\lib\net35\xunit.abstractions.dll + True + + + ..\..\packages\xunit.assert.2.2.0-beta4-build3444\lib\netstandard1.0\xunit.assert.dll + True + + + ..\..\packages\xunit.extensibility.core.2.2.0-beta4-build3444\lib\net45\xunit.core.dll + True + + + ..\..\packages\xunit.extensibility.execution.2.2.0-beta4-build3444\lib\net45\xunit.execution.desktop.dll + True + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/AspNetCoreModule.Test/AspNetCoreModule.Test.xproj b/test/AspNetCoreModule.Test/AspNetCoreModule.Test.xproj new file mode 100644 index 0000000000..b1ed5dc42f --- /dev/null +++ b/test/AspNetCoreModule.Test/AspNetCoreModule.Test.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 4dda7560-aa29-4161-a5ea-a7e8f3997321 + AspNetCoreModule.Test + .\obj + + + 2.0 + + + + + + \ No newline at end of file diff --git a/test/AspNetCoreModule.Test/Framework/EnvironmentVariableTestConditionAttribute.cs b/test/AspNetCoreModule.Test/Framework/EnvironmentVariableTestConditionAttribute.cs new file mode 100644 index 0000000000..c8691bc91a --- /dev/null +++ b/test/AspNetCoreModule.Test/Framework/EnvironmentVariableTestConditionAttribute.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Testing.xunit; + +namespace AspNetCoreModule.Test.Framework +{ + /// + /// Skip test if a given environment variable is not enabled. To enable the test, set environment variable + /// to "true" for the test process. + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public class EnvironmentVariableTestConditionAttribute : Attribute, ITestCondition + { + private readonly string _environmentVariableName; + + public EnvironmentVariableTestConditionAttribute(string environmentVariableName) + { + _environmentVariableName = environmentVariableName; + } + + public bool IsMet + { + get + { + return string.Compare(Environment.GetEnvironmentVariable(_environmentVariableName), "true", ignoreCase: true) == 0; + } + } + + public string SkipReason + { + get + { + return $"To run this test, set the environment variable {_environmentVariableName}=\"true\". {AdditionalInfo}"; + } + } + + public string AdditionalInfo { get; set; } + } +} \ No newline at end of file diff --git a/test/AspNetCoreModule.Test/Framework/IISConfigUtility.cs b/test/AspNetCoreModule.Test/Framework/IISConfigUtility.cs new file mode 100644 index 0000000000..aba16ea281 --- /dev/null +++ b/test/AspNetCoreModule.Test/Framework/IISConfigUtility.cs @@ -0,0 +1,1074 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using AspNetCoreModule.Test.HttpClientHelper; +using Microsoft.Web.Administration; +using System; +using System.IO; +using System.Management; +using System.ServiceProcess; +using System.Threading; + +namespace AspNetCoreModule.Test.Framework +{ + public class IISConfigUtility : IDisposable + { + public class Strings + { + public static string AppHostConfigPath = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "system32", "inetsrv", "config", "applicationHost.config"); + public static string IIS64BitPath = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "system32", "inetsrv"); + public static string IIS32BitPath = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "syswow64", "inetsrv"); + public static string IISExpress64BitPath = Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "IIS Express"); + public static string IISExpress32BitPath = Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"), "IIS Express"); + public static string DefaultAppPool = "DefaultAppPool"; + } + + private ServerType _serverType = ServerType.IIS; + private string _iisExpressConfigPath = null; + + public enum AppPoolBitness + { + enable32Bit, + noChange + } + + public void Dispose() + { + } + + public ServerManager GetServerManager() + { + if (_serverType == ServerType.IISExpress) + { + return new ServerManager( + false, // readOnly + _iisExpressConfigPath // applicationhost.config path for IISExpress + ); + } + else + { + return new ServerManager( + false, // readOnly + Strings.AppHostConfigPath // applicationhost.config path for IIS + ); + } + } + + public IISConfigUtility(ServerType type, string iisExpressConfigPath = null) + { + _serverType = type; + _iisExpressConfigPath = iisExpressConfigPath; + } + + public static void BackupAppHostConfig() + { + string fromfile = Strings.AppHostConfigPath; + string tofile = Strings.AppHostConfigPath + ".ancmtest.bak"; + if (File.Exists(fromfile)) + { + TestUtility.FileCopy(fromfile, tofile, overWrite: false); + } + } + + public static void RestoreAppHostConfig() + { + string fromfile = Strings.AppHostConfigPath + ".ancmtest.bak"; + string tofile = Strings.AppHostConfigPath; + + if (!File.Exists(fromfile) && !File.Exists(tofile)) + { + // IIS is not installed, don't do anything here + return; + } + + // backup first if the backup file is not available + if (!File.Exists(fromfile)) + { + BackupAppHostConfig(); + } + + // try again after the ininial clean up + if (File.Exists(fromfile)) + { + try + { + TestUtility.FileCopy(fromfile, tofile, true, true); + } + catch + { + // ignore + } + + // try again + if (!File.Exists(tofile) || File.ReadAllBytes(fromfile).Length != File.ReadAllBytes(tofile).Length) + { + // try again + TestUtility.ResetHelper(ResetHelperMode.KillWorkerProcess); + TestUtility.FileCopy(fromfile, tofile, true, true); + } + + if (File.ReadAllBytes(fromfile).Length != File.ReadAllBytes(tofile).Length) + { + throw new System.ApplicationException("Failed to restore applicationhost.config"); + } + } + } + + public void SetAppPoolSetting(string appPoolName, string attribute, object value) + { + TestUtility.LogInformation("Setting Apppool : " + appPoolName + "::" + attribute.ToString() + " <== " + value.ToString()); + using (ServerManager serverManager = GetServerManager()) + { + Configuration config = serverManager.GetApplicationHostConfiguration(); + ConfigurationSection applicationPoolsSection = config.GetSection("system.applicationHost/applicationPools"); + ConfigurationElementCollection applicationPoolsCollection = applicationPoolsSection.GetCollection(); + ConfigurationElement addElement = FindElement(applicationPoolsCollection, "add", "name", appPoolName); + if (addElement == null) throw new InvalidOperationException("Element not found!"); + + switch (attribute) + { + case "privateMemory": + case "memory": + ConfigurationElement recyclingElement = addElement.GetChildElement("recycling"); + ConfigurationElement periodicRestartElement = recyclingElement.GetChildElement("periodicRestart"); + periodicRestartElement[attribute] = value; + break; + case "rapidFailProtectionMaxCrashes": + ConfigurationElement failureElement = addElement.GetChildElement("failure"); + failureElement["rapidFailProtectionMaxCrashes"] = value; + break; + default: + addElement[attribute] = value; + break; + } + serverManager.CommitChanges(); + } + } + + public void RecycleAppPool(string appPoolName) + { + using (ServerManager serverManager = GetServerManager()) + { + serverManager.ApplicationPools[appPoolName].Recycle(); + } + } + + public void StopAppPool(string appPoolName) + { + using (ServerManager serverManager = GetServerManager()) + { + serverManager.ApplicationPools[appPoolName].Stop(); + } + } + + public void StartAppPool(string appPoolName) + { + using (ServerManager serverManager = GetServerManager()) + { + serverManager.ApplicationPools[appPoolName].Start(); + } + } + + public void CreateSite(string siteName, string physicalPath, int siteId, int tcpPort, string appPoolName = "DefaultAppPool") + { + TestUtility.LogInformation("Creating web site : " + siteName); + + using (ServerManager serverManager = GetServerManager()) + { + Configuration config = serverManager.GetApplicationHostConfiguration(); + ConfigurationSection sitesSection = config.GetSection("system.applicationHost/sites"); + ConfigurationElementCollection sitesCollection = sitesSection.GetCollection(); + ConfigurationElement siteElement = FindElement(sitesCollection, "site", "name", siteName); + if (siteElement != null) + { + sitesCollection.Remove(siteElement); + } + siteElement = sitesCollection.CreateElement("site"); + siteElement["id"] = siteId; + siteElement["name"] = siteName; + ConfigurationElementCollection bindingsCollection = siteElement.GetCollection("bindings"); + + ConfigurationElement bindingElement = bindingsCollection.CreateElement("binding"); + bindingElement["protocol"] = @"http"; + bindingElement["bindingInformation"] = "*:" + tcpPort + ":"; + bindingsCollection.Add(bindingElement); + + ConfigurationElementCollection siteCollection = siteElement.GetCollection(); + ConfigurationElement applicationElement = siteCollection.CreateElement("application"); + applicationElement["path"] = @"/"; + applicationElement["applicationPool"] = appPoolName; + + ConfigurationElementCollection applicationCollection = applicationElement.GetCollection(); + ConfigurationElement virtualDirectoryElement = applicationCollection.CreateElement("virtualDirectory"); + virtualDirectoryElement["path"] = @"/"; + virtualDirectoryElement["physicalPath"] = physicalPath; + applicationCollection.Add(virtualDirectoryElement); + siteCollection.Add(applicationElement); + sitesCollection.Add(siteElement); + + serverManager.CommitChanges(); + } + } + + public void CreateApp(string siteName, string appName, string physicalPath, string appPoolName = "DefaultAppPool") + { + TestUtility.LogInformation("Creating web app : " + siteName + "/" + appName); + using (ServerManager serverManager = GetServerManager()) + { + Configuration config = serverManager.GetApplicationHostConfiguration(); + + ConfigurationSection sitesSection = config.GetSection("system.applicationHost/sites"); + + ConfigurationElementCollection sitesCollection = sitesSection.GetCollection(); + + ConfigurationElement siteElement = FindElement(sitesCollection, "site", "name", siteName); + if (siteElement == null) throw new InvalidOperationException("Element not found!"); + + ConfigurationElementCollection siteCollection = siteElement.GetCollection(); + + ConfigurationElement applicationElement = siteCollection.CreateElement("application"); + string appPath = @"/" + appName; + appPath = appPath.Replace("//", "/"); + applicationElement["path"] = appPath; + applicationElement["applicationPool"] = appPoolName; + + ConfigurationElementCollection applicationCollection = applicationElement.GetCollection(); + + ConfigurationElement virtualDirectoryElement = applicationCollection.CreateElement("virtualDirectory"); + virtualDirectoryElement["path"] = @"/"; + virtualDirectoryElement["physicalPath"] = physicalPath; + applicationCollection.Add(virtualDirectoryElement); + siteCollection.Add(applicationElement); + + serverManager.CommitChanges(); + } + } + + public void EnableWindowsAuthentication(string siteName) + { + TestUtility.LogInformation("Enable Windows authentication : " + siteName); + using (ServerManager serverManager = GetServerManager()) + { + Configuration config = serverManager.GetApplicationHostConfiguration(); + + ConfigurationSection anonymousAuthenticationSection = config.GetSection("system.webServer/security/authentication/anonymousAuthentication", siteName); + anonymousAuthenticationSection["enabled"] = false; + ConfigurationSection windowsAuthenticationSection = config.GetSection("system.webServer/security/authentication/windowsAuthentication", siteName); + windowsAuthenticationSection["enabled"] = true; + + serverManager.CommitChanges(); + } + } + + public void SetCompression(string siteName, bool enabled) + { + TestUtility.LogInformation("Enable Compression : " + siteName); + using (ServerManager serverManager = GetServerManager()) + { + Configuration config = serverManager.GetApplicationHostConfiguration(); + + ConfigurationSection urlCompressionSection = config.GetSection("system.webServer/urlCompression", siteName); + urlCompressionSection["doStaticCompression"] = enabled; + urlCompressionSection["doDynamicCompression"] = enabled; + + serverManager.CommitChanges(); + } + } + + public void DisableWindowsAuthentication(string siteName) + { + TestUtility.LogInformation("Enable Windows authentication : " + siteName); + using (ServerManager serverManager = GetServerManager()) + { + Configuration config = serverManager.GetApplicationHostConfiguration(); + + ConfigurationSection anonymousAuthenticationSection = config.GetSection("system.webServer/security/authentication/anonymousAuthentication", siteName); + anonymousAuthenticationSection["enabled"] = true; + ConfigurationSection windowsAuthenticationSection = config.GetSection("system.webServer/security/authentication/windowsAuthentication", siteName); + windowsAuthenticationSection["enabled"] = false; + + serverManager.CommitChanges(); + } + } + + public void SetANCMConfig(string siteName, string appName, string attributeName, object attributeValue) + { + using (ServerManager serverManager = GetServerManager()) + { + Configuration config = serverManager.GetWebConfiguration(siteName, appName); + ConfigurationSection aspNetCoreSection = config.GetSection("system.webServer/aspNetCore"); + if (attributeName == "environmentVariable") + { + string name = ((string[])attributeValue)[0]; + string value = ((string[])attributeValue)[1]; + ConfigurationElementCollection environmentVariablesCollection = aspNetCoreSection.GetCollection("environmentVariables"); + ConfigurationElement environmentVariableElement = environmentVariablesCollection.CreateElement("environmentVariable"); + environmentVariableElement["name"] = name; + environmentVariableElement["value"] = value; + var element = FindElement(environmentVariablesCollection, "add", "name", value); + if (element != null) + { + throw new System.ApplicationException("duplicated collection item"); + } + environmentVariablesCollection.Add(environmentVariableElement); + } + else + { + aspNetCoreSection[attributeName] = attributeValue; + } + + serverManager.CommitChanges(); + } + } + + public void ConfigureCustomLogging(string siteName, string appName, int statusCode, int subStatusCode, string path) + { + using (ServerManager serverManager = GetServerManager()) + { + Configuration config = serverManager.GetWebConfiguration(siteName, appName); + ConfigurationSection httpErrorsSection = config.GetSection("system.webServer/httpErrors"); + httpErrorsSection["errorMode"] = @"Custom"; + + ConfigurationElementCollection httpErrorsCollection = httpErrorsSection.GetCollection(); + ConfigurationElement errorElement = FindElement(httpErrorsCollection, "error", "statusCode", statusCode.ToString(), "subStatusCode", subStatusCode.ToString()); + if (errorElement != null) + { + httpErrorsCollection.Remove(errorElement); + } + + ConfigurationElement errorElement2 = httpErrorsCollection.CreateElement("error"); + errorElement2["statusCode"] = statusCode; + errorElement2["subStatusCode"] = subStatusCode; + errorElement2["path"] = path; + httpErrorsCollection.Add(errorElement2); + + serverManager.CommitChanges(); + } + Thread.Sleep(500); + } + + private static bool? _isIISInstalled = null; + public static bool? IsIISInstalled + { + get + { + if (_isIISInstalled == null) + { + bool result = true; + if (!File.Exists(Path.Combine(Strings.IIS64BitPath, "iiscore.dll"))) + { + result = false; + } + if (!File.Exists(Path.Combine(Strings.IIS64BitPath, "config", "applicationhost.config"))) + { + result = false; + } + _isIISInstalled = result; + } + return _isIISInstalled; + } + } + + public bool IsAncmInstalled(ServerType servertype) + { + bool result = true; + if (servertype == ServerType.IIS) + { + if (!File.Exists(InitializeTestMachine.IISAspnetcoreSchema_path)) + { + result = false; + } + } + else + { + if (!File.Exists(InitializeTestMachine.IISExpressAspnetcoreSchema_path)) + { + result = false; + } + } + return result; + } + + public string GetServiceStatus(string serviceName) + { + ServiceController sc = new ServiceController(serviceName); + + switch (sc.Status) + { + case ServiceControllerStatus.Running: + return "Running"; + case ServiceControllerStatus.Stopped: + return "Stopped"; + case ServiceControllerStatus.Paused: + return "Paused"; + case ServiceControllerStatus.StopPending: + return "Stopping"; + case ServiceControllerStatus.StartPending: + return "Starting"; + default: + return "Status Changing"; + } + } + + public bool IsUrlRewriteInstalledForIIS() + { + bool result = true; + var toRewrite64 = Path.Combine(Strings.IIS64BitPath, "rewrite.dll"); + var toRewrite32 = Path.Combine(Strings.IIS32BitPath, "rewrite.dll"); + + if (TestUtility.IsOSAmd64) + { + if (!File.Exists(toRewrite64)) + { + result = false; + } + } + + if (!File.Exists(toRewrite32)) + { + result = false; + } + + using (ServerManager serverManager = GetServerManager()) + { + Configuration config = serverManager.GetApplicationHostConfiguration(); + ConfigurationSection globalModulesSection = config.GetSection("system.webServer/globalModules"); + ConfigurationElementCollection globalModulesCollection = globalModulesSection.GetCollection(); + if (FindElement(globalModulesCollection, "add", "name", "RewriteModule") == null) + { + result = false; + } + + ConfigurationSection modulesSection = config.GetSection("system.webServer/modules"); + ConfigurationElementCollection modulesCollection = modulesSection.GetCollection(); + if (FindElement(modulesCollection, "add", "name", "RewriteModule") == null) + { + result = false; + } + } + return result; + } + + public bool RemoveModule(string moduleName) + { + bool result = true; + using (ServerManager serverManager = GetServerManager()) + { + Configuration config = serverManager.GetApplicationHostConfiguration(); + ConfigurationSection globalModulesSection = config.GetSection("system.webServer/globalModules"); + ConfigurationElementCollection globalModulesCollection = globalModulesSection.GetCollection(); + var globalModule = FindElement(globalModulesCollection, "add", "name", moduleName); + if (globalModule != null) + { + globalModulesCollection.Remove(globalModule); + + } + ConfigurationSection modulesSection = config.GetSection("system.webServer/modules"); + ConfigurationElementCollection modulesCollection = modulesSection.GetCollection(); + var module = FindElement(modulesCollection, "add", "name", moduleName); + if (module != null) + { + modulesCollection.Remove(module); + } + + serverManager.CommitChanges(); + } + return result; + } + + public bool AddModule(string moduleName, string image, string preCondition) + { + RemoveModule(moduleName); + + bool result = true; + using (ServerManager serverManager = GetServerManager()) + { + Configuration config = serverManager.GetApplicationHostConfiguration(); + ConfigurationSection globalModulesSection = config.GetSection("system.webServer/globalModules"); + ConfigurationElementCollection globalModulesCollection = globalModulesSection.GetCollection(); + + ConfigurationElement globalModule = globalModulesCollection.CreateElement("add"); + globalModule["name"] = moduleName; + globalModule["image"] = image; + if (preCondition != null) + { + globalModule["preCondition"] = preCondition; + } + globalModulesCollection.Add(globalModule); + + ConfigurationSection modulesSection = config.GetSection("system.webServer/modules"); + ConfigurationElementCollection modulesCollection = modulesSection.GetCollection(); + ConfigurationElement module = modulesCollection.CreateElement("add"); + module["name"] = moduleName; + modulesCollection.Add(module); + + serverManager.CommitChanges(); + } + return result; + } + + private static ConfigurationElement FindElement(ConfigurationElementCollection collection, string elementTagName, params string[] keyValues) + { + foreach (ConfigurationElement element in collection) + { + if (String.Equals(element.ElementTagName, elementTagName, StringComparison.OrdinalIgnoreCase)) + { + bool matches = true; + + for (int i = 0; i < keyValues.Length; i += 2) + { + object o = element.GetAttributeValue(keyValues[i]); + string value = null; + if (o != null) + { + value = o.ToString(); + } + + if (!String.Equals(value, keyValues[i + 1], StringComparison.OrdinalIgnoreCase)) + { + matches = false; + break; + } + } + if (matches) + { + return element; + } + } + } + return null; + } + + public void CreateAppPool(string poolName, bool alwaysRunning = false) + { + try + { + TestUtility.LogTrace(String.Format("#################### Adding App Pool {0} with startMode = {1} ####################", poolName, alwaysRunning ? "AlwaysRunning" : "OnDemand")); + using (ServerManager serverManager = GetServerManager()) + { + serverManager.ApplicationPools.Add(poolName); + ApplicationPool apppool = serverManager.ApplicationPools[poolName]; + apppool.ManagedPipelineMode = ManagedPipelineMode.Integrated; + if (alwaysRunning) + { + apppool.SetAttributeValue("startMode", "AlwaysRunning"); + } + + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### Create app pool {0} failed. Reason: {1} ####################", poolName, ex.Message)); + } + } + + public void SetIdleTimeoutForAppPool(string appPoolName, int idleTimeoutMinutes) + { + TestUtility.LogTrace(String.Format("#################### Setting idleTimeout to {0} minutes for AppPool {1} ####################", idleTimeoutMinutes, appPoolName)); + try + { + using (ServerManager serverManager = GetServerManager()) + { + ApplicationPoolCollection appPools = serverManager.ApplicationPools; + appPools[appPoolName].ProcessModel.IdleTimeout = TimeSpan.FromMinutes(idleTimeoutMinutes); + + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### Setting idleTimeout to {0} minutes for AppPool {1} failed. Reason: {2} ####################", idleTimeoutMinutes, appPoolName, ex.Message)); + } + } + + public void SetMaxProcessesForAppPool(string appPoolName, int maxProcesses) + { + TestUtility.LogTrace(String.Format("#################### Setting maxProcesses to {0} for AppPool {1} ####################", maxProcesses, appPoolName)); + try + { + using (ServerManager serverManager = GetServerManager()) + { + ApplicationPoolCollection appPools = serverManager.ApplicationPools; + appPools[appPoolName].ProcessModel.MaxProcesses = maxProcesses; + + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### Setting maxProcesses to {0} for AppPool {1} failed. Reason: {2} ####################", maxProcesses, appPoolName, ex.Message)); + } + } + + public void SetIdentityForAppPool(string appPoolName, string userName, string password) + { + TestUtility.LogTrace(String.Format("#################### Setting userName {0} and password {1} for AppPool {2} ####################", userName, password, appPoolName)); + try + { + using (ServerManager serverManager = GetServerManager()) + { + ApplicationPoolCollection appPools = serverManager.ApplicationPools; + appPools[appPoolName].ProcessModel.IdentityType = ProcessModelIdentityType.SpecificUser; + appPools[appPoolName].ProcessModel.UserName = userName; + appPools[appPoolName].ProcessModel.Password = password; + + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### Setting userName {0} and password {1} for AppPool {2} failed. Reason: {2} ####################", userName, password, appPoolName, ex.Message)); + } + } + + public void SetStartModeAlwaysRunningForAppPool(string appPoolName, bool alwaysRunning) + { + string startMode = alwaysRunning ? "AlwaysRunning" : "OnDemand"; + + TestUtility.LogTrace(String.Format("#################### Setting startMode to {0} for AppPool {1} ####################", startMode, appPoolName)); + + try + { + using (ServerManager serverManager = GetServerManager()) + { + ApplicationPoolCollection appPools = serverManager.ApplicationPools; + appPools[appPoolName]["startMode"] = startMode; + + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### Setting startMode to {0} for AppPool {1} failed. Reason: {2} ####################", startMode, appPoolName, ex.Message)); + } + } + + public void StartAppPoolEx(string appPoolName) + { + StartOrStopAppPool(appPoolName, true); + } + + public void StopAppPoolEx(string appPoolName) + { + StartOrStopAppPool(appPoolName, false); + } + + private void StartOrStopAppPool(string appPoolName, bool start) + { + string action = start ? "Starting" : "Stopping"; + TestUtility.LogTrace(String.Format("#################### {0} app pool {1} ####################", action, appPoolName)); + + try + { + using (ServerManager serverManager = GetServerManager()) + { + ApplicationPoolCollection appPools = serverManager.ApplicationPools; + if (start) + appPools[appPoolName].Start(); + else + appPools[appPoolName].Stop(); + + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + string message = ex.Message; + TestUtility.LogInformation(String.Format("#################### {0} app pool {1} failed. Reason: {2} ####################", action, appPoolName, ex.Message)); + } + } + + public void VerifyAppPoolState(string appPoolName, Microsoft.Web.Administration.ObjectState state) + { + try + { + using (ServerManager serverManager = GetServerManager()) + { + ApplicationPoolCollection appPools = serverManager.ApplicationPools; + if (appPools[appPoolName].State == state) + TestUtility.LogInformation(String.Format("Verified state for app pool {0} is {1}.", appPoolName, state.ToString())); + else + TestUtility.LogInformation(String.Format("Unexpected state {0} for app pool {1}.", state, appPoolName.ToString())); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### Failed to verify state for app pool {0}. Reason: {1} ####################", appPoolName, ex.Message)); + } + } + + public void DeleteAppPool(string poolName) + { + try + { + using (ServerManager serverManager = GetServerManager()) + { + TestUtility.LogTrace(String.Format("#################### Deleting App Pool {0} ####################", poolName)); + + ApplicationPoolCollection appPools = serverManager.ApplicationPools; + appPools.Remove(appPools[poolName]); + + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### Delete app pool {0} failed. Reason: {1} ####################", poolName, ex.Message)); + } + } + + public void DeleteAllAppPools(bool commitDelay = false) + { + TestUtility.LogTrace(String.Format("#################### Deleting all app pools ####################")); + + using (ServerManager serverManager = GetServerManager()) + { + ApplicationPoolCollection appPools = serverManager.ApplicationPools; + while (appPools.Count > 0) + appPools.RemoveAt(0); + + serverManager.CommitChanges(); + } + } + + public void CreateSiteEx(int siteId, string siteName, string poolName, string dirRoot, string Ip, int Port, string host) + { + try + { + using (ServerManager serverManager = GetServerManager()) + { + string bindingInfo = ""; + if (Ip == null) + Ip = "*"; + bindingInfo += Ip; + bindingInfo += ":"; + bindingInfo += Port; + bindingInfo += ":"; + if (host != null) + bindingInfo += host; + + TestUtility.LogTrace(String.Format("#################### Adding Site {0} with App Pool {1} with BindingInfo {2} ####################", siteName, poolName, bindingInfo)); + + SiteCollection sites = serverManager.Sites; + Site site = sites.CreateElement(); + site.Id = siteId; + site.SetAttributeValue("name", siteName); + sites.Add(site); + + Application app = site.Applications.CreateElement(); + app.SetAttributeValue("path", "/"); + app.SetAttributeValue("applicationPool", poolName); + site.Applications.Add(app); + + VirtualDirectory vdir = app.VirtualDirectories.CreateElement(); + vdir.SetAttributeValue("path", "/"); + vdir.SetAttributeValue("physicalPath", dirRoot); + + app.VirtualDirectories.Add(vdir); + + Binding b = site.Bindings.CreateElement(); + b.SetAttributeValue("protocol", "http"); + b.SetAttributeValue("bindingInformation", bindingInfo); + + site.Bindings.Add(b); + + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### Create site {0} failed. Reason: {1} ####################", siteName, ex.Message)); + } + } + + public void StartSite(string siteName) + { + StartOrStopSite(siteName, true); + } + + public void StopSite(string siteName) + { + StartOrStopSite(siteName, false); + } + + private void StartOrStopSite(string siteName, bool start) + { + string action = start ? "Starting" : "Stopping"; + TestUtility.LogTrace(String.Format("#################### {0} site {1} ####################", action, siteName)); + + try + { + using (ServerManager serverManager = GetServerManager()) + { + SiteCollection sites = serverManager.Sites; + if (start) + { + sites[siteName].Start(); + sites[siteName].SetAttributeValue("serverAutoStart", true); + } + else + { + sites[siteName].Stop(); + sites[siteName].SetAttributeValue("serverAutoStart", false); + } + + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### {0} site {1} failed. Reason: {2} ####################", action, siteName, ex.Message)); + } + } + + public ObjectState GetSiteState(string siteName) + { + using (ServerManager serverManager = GetServerManager()) + { + SiteCollection sites = serverManager.Sites; + if (sites[siteName] != null) + { + return sites[siteName].State; + } + else + { + return ObjectState.Unknown; + } + } + } + + public void AddApplicationToSite(string siteName, string appPath, string physicalPath, string poolName) + { + try + { + using (ServerManager serverManager = GetServerManager()) + { + TestUtility.LogTrace(String.Format("#################### Adding Application {0} with App Pool {1} to Site {2} ####################", appPath, poolName, siteName)); + + SiteCollection sites = serverManager.Sites; + Application app = sites[siteName].Applications.CreateElement(); + app.SetAttributeValue("path", appPath); + app.SetAttributeValue("applicationPool", poolName); + sites[siteName].Applications.Add(app); + + VirtualDirectory vdir = app.VirtualDirectories.CreateElement(); + vdir.SetAttributeValue("path", "/"); + vdir.SetAttributeValue("physicalPath", physicalPath); + + app.VirtualDirectories.Add(vdir); + + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### Add Application {0} with App Pool {1} to Site {2} failed. Reason: {3} ####################", appPath, poolName, siteName, ex.Message)); + } + } + + public void ChangeApplicationPool(string siteName, int appIndex, string poolName) + { + try + { + using (ServerManager serverManager = GetServerManager()) + { + TestUtility.LogTrace(String.Format("#################### Changing Application Pool for App {0} of Site {1} to {2} ####################", appIndex, siteName, poolName)); + + serverManager.Sites[siteName].Applications[appIndex].SetAttributeValue("applicationPool", poolName); + + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### Changing Application Pool for App {0} of Site {1} to {2} failed. Reason: {3} ####################", appIndex, siteName, poolName, ex.Message)); + } + } + + public void ChangeApplicationPath(string siteName, int appIndex, string path) + { + try + { + using (ServerManager serverManager = GetServerManager()) + { + TestUtility.LogTrace(String.Format("#################### Changing Path for App {0} of Site {1} to {2} ####################", appIndex, siteName, path)); + + serverManager.Sites[siteName].Applications[appIndex].SetAttributeValue("path", path); + + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### Changing Path for App {0} of Site {1} to {2} failed. Reason: {3} ####################", appIndex, siteName, path, ex.Message)); + } + } + + public void RemoveApplication(string siteName, int appIndex) + { + try + { + using (ServerManager serverManager = GetServerManager()) + { + TestUtility.LogTrace(String.Format("#################### Deleting App {0} from Site {1} ####################", appIndex, siteName)); + + serverManager.Sites[siteName].Applications.RemoveAt(appIndex); + + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### Deleting App {0} from Site {1} failed. Reason: {2} ####################", appIndex, siteName, ex.Message)); + } + } + + public void AddBindingToSite(string siteName, string Ip, int Port, string host) + { + string bindingInfo = ""; + if (Ip == null) + Ip = "*"; + bindingInfo += Ip; + bindingInfo += ":"; + bindingInfo += Port; + bindingInfo += ":"; + if (host != null) + bindingInfo += host; + + TestUtility.LogInformation(String.Format("#################### Adding Binding {0} to Site {1} ####################", bindingInfo, siteName)); + + try + { + using (ServerManager serverManager = GetServerManager()) + { + SiteCollection sites = serverManager.Sites; + Binding b = sites[siteName].Bindings.CreateElement(); + b.SetAttributeValue("protocol", "http"); + b.SetAttributeValue("bindingInformation", bindingInfo); + + sites[siteName].Bindings.Add(b); + + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### Adding Binding {0} to Site {1} failed. Reason: {2} ####################", bindingInfo, siteName, ex.Message)); + } + } + + public void RemoveBindingFromSite(string siteName, BindingInfo bindingInfo) + { + try + { + using (ServerManager serverManager = GetServerManager()) + { + TestUtility.LogTrace(String.Format("#################### Removing Binding {0} from Site {1} ####################", bindingInfo.ToBindingString(), siteName)); + + for (int i = 0; i < serverManager.Sites[siteName].Bindings.Count; i++) + { + if (serverManager.Sites[siteName].Bindings[i].BindingInformation.ToString() == bindingInfo.ToBindingString()) + { + serverManager.Sites[siteName].Bindings.RemoveAt(i); + + serverManager.CommitChanges(); + return; + } + } + + TestUtility.LogInformation(String.Format("#################### Remove binding failed because binding was not found ####################")); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### Remove binding failed. Reason: {0} ####################", ex.Message)); + } + } + + public void ModifyBindingForSite(string siteName, BindingInfo bindingInfoOld, BindingInfo bindingInfoNew) + { + try + { + using (ServerManager serverManager = GetServerManager()) + { + TestUtility.LogTrace(String.Format("#################### Changing Binding {0} for Site {1} to {2} ####################", bindingInfoOld.ToBindingString(), siteName, bindingInfoNew.ToBindingString())); + + for (int i = 0; i < serverManager.Sites[siteName].Bindings.Count; i++) + { + if (serverManager.Sites[siteName].Bindings[i].BindingInformation.ToString() == bindingInfoOld.ToBindingString()) + { + serverManager.Sites[siteName].Bindings[i].SetAttributeValue("bindingInformation", bindingInfoNew.ToBindingString()); + + serverManager.CommitChanges(); + return; + } + } + + TestUtility.LogInformation(String.Format("#################### Modify binding failed because binding was not found ####################")); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### Changing binding failed. Reason: {0} ####################", ex.Message)); + } + } + + public void DeleteSite(string siteName) + { + try + { + using (ServerManager serverManager = GetServerManager()) + { + TestUtility.LogTrace(String.Format("#################### Deleting Site {0} ####################", siteName)); + + SiteCollection sites = serverManager.Sites; + sites.Remove(sites[siteName]); + + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + TestUtility.LogInformation(String.Format("#################### Delete site {0} failed. Reason: {1} ####################", siteName, ex.Message)); + } + } + + public void DeleteAllSites(bool commitDelay = false) + { + using (ServerManager serverManager = GetServerManager()) + { + TestUtility.LogTrace(String.Format("#################### Deleting all sites ####################")); + + SiteCollection sites = serverManager.Sites; + while (sites.Count > 0) + sites.RemoveAt(0); + + serverManager.CommitChanges(); + } + } + + public void SetDynamicSiteRegistrationThreshold(int threshold) + { + try + { + TestUtility.LogTrace(String.Format("#################### Changing dynamicRegistrationThreshold to {0} ####################", threshold)); + + using (ServerManager serverManager = new ServerManager()) + { + Configuration config = serverManager.GetApplicationHostConfiguration(); + + ConfigurationSection webLimitsSection = config.GetSection("system.applicationHost/webLimits"); + webLimitsSection["dynamicRegistrationThreshold"] = threshold; + + + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + TestUtility.LogTrace(String.Format("#################### Changing dynamicRegistrationThreshold failed. Reason: {0} ####################", ex.Message)); + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCoreModule.Test/Framework/InitializeTestMachine.cs b/test/AspNetCoreModule.Test/Framework/InitializeTestMachine.cs new file mode 100644 index 0000000000..f72d57c4b3 --- /dev/null +++ b/test/AspNetCoreModule.Test/Framework/InitializeTestMachine.cs @@ -0,0 +1,244 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Threading; +using Microsoft.Extensions.PlatformAbstractions; + +namespace AspNetCoreModule.Test.Framework +{ + public class InitializeTestMachine : IDisposable + { + // + // By default, we don't use the private AspNetCoreFile + // + public static bool UsePrivateAspNetCoreFile = false; + + public static int SiteId = 40000; + public static string Aspnetcore_path = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "system32", "inetsrv", "aspnetcore_private.dll"); + public static string Aspnetcore_path_original = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "system32", "inetsrv", "aspnetcore.dll"); + public static string Aspnetcore_X86_path = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "syswow64", "inetsrv", "aspnetcore_private.dll"); + public static string IISExpressAspnetcoreSchema_path = Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "IIS Express", "config", "schema", "aspnetcore_schema.xml"); + public static string IISAspnetcoreSchema_path = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "system32", "inetsrv", "config", "schema", "aspnetcore_schema.xml"); + public static int _referenceCount = 0; + private static bool _InitializeTestMachineCompleted = false; + private string _setupScriptPath = null; + + public InitializeTestMachine() + { + _referenceCount++; + + if (_referenceCount == 1) + { + TestUtility.LogInformation("InitializeTestMachine::InitializeTestMachine() Start"); + + _InitializeTestMachineCompleted = false; + + TestUtility.LogInformation("InitializeTestMachine::Start"); + if (Environment.ExpandEnvironmentVariables("%ANCMDebug%").ToLower() == "true") + { + System.Diagnostics.Debugger.Launch(); + } + + TestUtility.ResetHelper(ResetHelperMode.KillIISExpress); + TestUtility.ResetHelper(ResetHelperMode.KillWorkerProcess); + // cleanup before starting + string siteRootPath = Path.Combine(Environment.ExpandEnvironmentVariables("%SystemDrive%") + @"\", "inetpub", "ANCMTest"); + try + { + if (IISConfigUtility.IsIISInstalled == true) + { + IISConfigUtility.RestoreAppHostConfig(); + } + } + catch + { + TestUtility.LogInformation("Failed to restore applicationhost.config"); + } + + if (!Directory.Exists(siteRootPath)) + { + Directory.CreateDirectory(siteRootPath); + } + + foreach (string directory in Directory.GetDirectories(siteRootPath)) + { + bool successDeleteChildDirectory = true; + try + { + TestUtility.DeleteDirectory(directory); + } + catch + { + successDeleteChildDirectory = false; + TestUtility.LogInformation("Failed to delete " + directory); + } + if (successDeleteChildDirectory) + { + try + { + TestUtility.DeleteDirectory(siteRootPath); + } + catch + { + TestUtility.LogInformation("Failed to delete " + siteRootPath); + } + } + } + + if (InitializeTestMachine.UsePrivateAspNetCoreFile) + { + PreparePrivateANCMFiles(); + + // update applicationhost.config for IIS server + if (IISConfigUtility.IsIISInstalled == true) + { + + using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + { + iisConfig.AddModule("AspNetCoreModule", Aspnetcore_path, null); + } + } + } + + _InitializeTestMachineCompleted = true; + + TestUtility.LogInformation("InitializeTestMachine::InitializeTestMachine() End"); + } + + for (int i=0; i<120; i++) + { + if (_InitializeTestMachineCompleted) + { + break; + } + else + { + TestUtility.LogInformation("InitializeTestMachine::InitializeTestMachine() Waiting..."); + Thread.Sleep(500); + } + } + if (!_InitializeTestMachineCompleted) + { + throw new System.ApplicationException("InitializeTestMachine failed"); + } + } + + public void Dispose() + { + _referenceCount--; + + if (_referenceCount == 0) + { + TestUtility.LogInformation("InitializeTestMachine::Dispose() Start"); + TestUtility.ResetHelper(ResetHelperMode.KillIISExpress); + + if (InitializeTestMachine.UsePrivateAspNetCoreFile) + { + if (IISConfigUtility.IsIISInstalled == true) + { + using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + { + try + { + iisConfig.AddModule("AspNetCoreModule", Aspnetcore_path_original, null); + } + catch + { + TestUtility.LogInformation("Failed to restore aspnetcore.dll path!!!"); + } + } + } + } + TestUtility.LogInformation("InitializeTestMachine::Dispose() End"); + } + } + + private void PreparePrivateANCMFiles() + { + var solutionRoot = GetSolutionDirectory(); + string outputPath = string.Empty; + _setupScriptPath = Path.Combine(solutionRoot, "tools"); + + // First try with debug build + outputPath = Path.Combine(solutionRoot, "artifacts", "build", "AspNetCore", "bin", "Debug"); + + // If debug build does is not available, try with release build + if (!File.Exists(Path.Combine(outputPath, "Win32", "aspnetcore.dll")) + || !File.Exists(Path.Combine(outputPath, "x64", "aspnetcore.dll")) + || !File.Exists(Path.Combine(outputPath, "x64", "aspnetcore_schema.xml"))) + { + outputPath = Path.Combine(solutionRoot, "artifacts", "build", "AspNetCore", "bin", "Release"); + } + + if (!File.Exists(Path.Combine(outputPath, "Win32", "aspnetcore.dll")) + || !File.Exists(Path.Combine(outputPath, "x64", "aspnetcore.dll")) + || !File.Exists(Path.Combine(outputPath, "x64", "aspnetcore_schema.xml"))) + { + outputPath = Path.Combine(solutionRoot, "src", "AspNetCore", "bin", "Debug"); + } + + if (!File.Exists(Path.Combine(outputPath, "Win32", "aspnetcore.dll")) + || !File.Exists(Path.Combine(outputPath, "x64", "aspnetcore.dll")) + || !File.Exists(Path.Combine(outputPath, "x64", "aspnetcore_schema.xml"))) + { + throw new ApplicationException("aspnetcore.dll is not available; build aspnetcore.dll for both x86 and x64 and then try again!!!"); + } + + // create an extra private copy of the private file on IISExpress directory + if (InitializeTestMachine.UsePrivateAspNetCoreFile) + { + bool updateSuccess = false; + + for (int i = 0; i < 3; i++) + { + updateSuccess = false; + try + { + TestUtility.ResetHelper(ResetHelperMode.KillWorkerProcess); + TestUtility.ResetHelper(ResetHelperMode.StopW3svcStartW3svc); + Thread.Sleep(1000); + TestUtility.FileCopy(Path.Combine(outputPath, "x64", "aspnetcore.dll"), Aspnetcore_path); + if (TestUtility.IsOSAmd64) + { + TestUtility.FileCopy(Path.Combine(outputPath, "Win32", "aspnetcore.dll"), Aspnetcore_X86_path); + } + updateSuccess = true; + } + catch + { + updateSuccess = false; + } + if (updateSuccess) + { + break; + } + } + if (!updateSuccess) + { + throw new System.ApplicationException("Failed to update aspnetcore.dll"); + } + } + } + + public static string GetSolutionDirectory() + { + var applicationBasePath = PlatformServices.Default.Application.ApplicationBasePath; + var directoryInfo = new DirectoryInfo(applicationBasePath); + do + { + var solutionFile = new FileInfo(Path.Combine(directoryInfo.FullName, "AspNetCoreModule.sln")); + if (solutionFile.Exists) + { + return directoryInfo.FullName; + } + + directoryInfo = directoryInfo.Parent; + } + while (directoryInfo.Parent != null); + + throw new Exception($"Solution root could not be located using application root {applicationBasePath}."); + } + } +} diff --git a/test/AspNetCoreModule.Test/Framework/TestUtility.cs b/test/AspNetCoreModule.Test/Framework/TestUtility.cs new file mode 100644 index 0000000000..f0422d2aeb --- /dev/null +++ b/test/AspNetCoreModule.Test/Framework/TestUtility.cs @@ -0,0 +1,830 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Text; +using System.IO; +using System.Xml; +using System.Management; +using System.Threading; +using System.Diagnostics; +using Microsoft.Extensions.PlatformAbstractions; +using Microsoft.Extensions.Logging; +using System.Collections.Generic; +using System.Security.Principal; +using System.Security.AccessControl; +using System.Net.Http; +using System.Threading.Tasks; +using System.Net; + +namespace AspNetCoreModule.Test.Framework +{ + public enum ResetHelperMode + { + CallIISReset, + StopHttpStartW3svc, + StopWasStartW3svc, + StopW3svcStartW3svc, + KillWorkerProcess, + KillVSJitDebugger, + KillIISExpress + } + + public enum ServerType + { + IISExpress = 0, + IIS = 1, + } + + public class TestUtility + { + public static ILogger _logger = null; + + public static ILogger Logger + { + get + { + if (_logger == null) + { + _logger = new LoggerFactory() + .AddConsole() + .CreateLogger("TestUtility"); + } + return _logger; + } + } + + public TestUtility(ILogger logger) + { + _logger = logger; + } + + /// + /// Retries every 1 sec for 60 times by default. + /// + /// + /// + /// + /// + public static async Task RetryRequest( + Func> retryBlock, + ILogger logger, + CancellationToken cancellationToken = default(CancellationToken), + int retryCount = 60) + { + for (var retry = 0; retry < retryCount; retry++) + { + if (cancellationToken.IsCancellationRequested) + { + logger.LogInformation("Failed to connect, retry canceled."); + throw new OperationCanceledException("Failed to connect, retry canceled.", cancellationToken); + } + + try + { + logger.LogWarning("Retry count {retryCount}..", retry + 1); + var response = await retryBlock().ConfigureAwait(false); + + if (response.StatusCode == HttpStatusCode.ServiceUnavailable) + { + // Automatically retry on 503. May be application is still booting. + logger.LogWarning("Retrying a service unavailable error."); + continue; + } + + return response; // Went through successfully + } + catch (Exception exception) + { + if (retry == retryCount - 1) + { + logger.LogError(0, exception, "Failed to connect, retry limit exceeded."); + throw; + } + else + { + if (exception is HttpRequestException +#if NET451 + || exception is System.Net.WebException +#endif + ) + { + logger.LogWarning("Failed to complete the request : {0}.", exception.Message); + await Task.Delay(1 * 1000); //Wait for a while before retry. + } + } + } + } + + logger.LogInformation("Failed to connect, retry limit exceeded."); + throw new OperationCanceledException("Failed to connect, retry limit exceeded."); + } + + public static void RetryOperation( + Action retryBlock, + Action exceptionBlock, + int retryCount = 3, + int retryDelayMilliseconds = 0) + { + for (var retry = 0; retry < retryCount; ++retry) + { + try + { + retryBlock(); + break; + } + catch (Exception exception) + { + exceptionBlock(exception); + } + + Thread.Sleep(retryDelayMilliseconds); + } + } + + public static bool RetryHelper ( + Func verifier, + T arg, + Action exceptionBlock = null, + int retryCount = 3, + int retryDelayMilliseconds = 1000 + ) + { + for (var retry = 0; retry < retryCount; ++retry) + { + try + { + if (verifier(arg)) + return true; + } + catch (Exception exception) + { + exceptionBlock?.Invoke(exception); + } + Thread.Sleep(retryDelayMilliseconds); + } + return false; + } + + public static bool RetryHelper( + Func verifier, + T1 arg1, + T2 arg2, + Action exceptionBlock = null, + int retryCount = 3, + int retryDelayMilliseconds = 1000 + ) + { + for (var retry = 0; retry < retryCount; ++retry) + { + try + { + if (verifier(arg1, arg2)) + return true; + } + catch (Exception exception) + { + exceptionBlock?.Invoke(exception); + } + Thread.Sleep(retryDelayMilliseconds); + } + return false; + } + + public static bool RetryHelper( + Func verifier, + T1 arg1, + T2 arg2, + T3 arg3, + Action exceptionBlock = null, + int retryCount = 3, + int retryDelayMilliseconds = 1000 + ) + { + for (var retry = 0; retry < retryCount; ++retry) + { + try + { + if (verifier(arg1, arg2, arg3)) + return true; + } + catch (Exception exception) + { + exceptionBlock?.Invoke(exception); + } + Thread.Sleep(retryDelayMilliseconds); + } + return false; + } + + public static void GiveWritePermissionTo(string folder, SecurityIdentifier sid) + { + DirectorySecurity fsecurity = Directory.GetAccessControl(folder); + FileSystemAccessRule writerule = new FileSystemAccessRule(sid, FileSystemRights.Write, AccessControlType.Allow); + fsecurity.AddAccessRule(writerule); + Directory.SetAccessControl(folder, fsecurity); + Thread.Sleep(500); + } + + public static bool IsOSAmd64 + { + get + { + if (Environment.ExpandEnvironmentVariables("%PROCESSOR_ARCHITECTURE%") == "AMD64") + { + return true; + } + else + { + return false; + } + } + } + + public static void LogTrace(string format, params object[] parameters) + { + if (format != null) + { + Logger.LogTrace(format); + } + } + public static void LogError(string format, params object[] parameters) + { + if (format != null) + { + Logger.LogError(format); + } + } + public static void LogInformation(string format, params object[] parameters) + { + if (format != null) + { + Logger.LogInformation(format); + } + } + + public static void DeleteFile(string filePath) + { + if (File.Exists(filePath)) + { + RunCommand("cmd.exe", "/c del \"" + filePath + "\""); + } + if (File.Exists(filePath)) + { + throw new ApplicationException("Failed to delete file: " + filePath); + } + } + + public static void FileMove(string from, string to, bool overWrite = true) + { + if (overWrite) + { + DeleteFile(to); + } + if (File.Exists(from)) + { + if (File.Exists(to) && overWrite == false) + { + return; + } + File.Move(from, to); + if (!File.Exists(to)) + { + throw new ApplicationException("Failed to rename from : " + from + ", to : " + to); + } + if (File.Exists(from)) + { + throw new ApplicationException("Failed to rename from : " + from + ", to : " + to); + } + } + else + { + throw new ApplicationException("File not found " + from); + } + } + + public static void FileCopy(string from, string to, bool overWrite = true, bool ignoreExceptionWhileDeletingExistingFile = false) + { + if (overWrite) + { + try + { + DeleteFile(to); + } + catch + { + if (!ignoreExceptionWhileDeletingExistingFile) + { + throw; + } + } + } + + if (File.Exists(from)) + { + if (File.Exists(to) && overWrite == false) + { + return; + } + RunCommand("cmd.exe", "/c copy /y \"" + from + "\" \"" + to + "\""); + + if (!File.Exists(to)) + { + throw new ApplicationException("Failed to move from : " + from + ", to : " + to); + } + } + else + { + LogError("File not found " + from); + } + } + + public static void DeleteDirectory(string directoryPath) + { + if (Directory.Exists(directoryPath)) + { + RunCommand("cmd.exe", "/c rd \"" + directoryPath + "\" /s /q"); + } + if (Directory.Exists(directoryPath)) + { + throw new ApplicationException("Failed to delete directory: " + directoryPath); + } + } + + public static void CreateDirectory(string directoryPath) + { + if (!Directory.Exists(directoryPath)) + { + RunCommand("cmd.exe", "/c md \"" + directoryPath + "\""); + } + if (!Directory.Exists(directoryPath)) + { + throw new ApplicationException("Failed to create directory: " + directoryPath); + } + } + + public static void DirectoryCopy(string from, string to) + { + if (Directory.Exists(to)) + { + DeleteDirectory(to); + } + + if (!Directory.Exists(to)) + { + CreateDirectory(to); + } + + if (Directory.Exists(from)) + { + RunCommand("cmd.exe", "/c xcopy \"" + from + "\" \"" + to + "\" /s"); + } + else + { + TestUtility.LogInformation("Directory not found " + from); + } + } + + public static string FileReadAllText(string file) + { + string result = null; + if (File.Exists(file)) + { + result = File.ReadAllText(file); + } + return result; + } + + public static void CreateFile(string file, string[] stringArray) + { + DeleteFile(file); + using (StreamWriter sw = new StreamWriter(file)) + { + foreach (string line in stringArray) + { + sw.WriteLine(line); + } + } + + if (!File.Exists(file)) + { + throw new ApplicationException("Failed to create " + file); + } + } + + public static void KillProcess(string processFileName) + { + string query = "Select * from Win32_Process Where Name = \"" + processFileName + "\""; + ManagementObjectSearcher searcher = new ManagementObjectSearcher(query); + ManagementObjectCollection processList = searcher.Get(); + foreach (ManagementObject obj in processList) + { + obj.InvokeMethod("Terminate", null); + } + Thread.Sleep(1000); + + processList = searcher.Get(); + if (processList.Count > 0) + { + TestUtility.LogInformation("Failed to kill process " + processFileName); + } + } + + public static int GetNumberOfProcess(string processFileName, int expectedNumber = 1, int retry = 0) + { + int result = 0; + string query = "Select * from Win32_Process Where Name = \"" + processFileName + "\""; + ManagementObjectSearcher searcher = new ManagementObjectSearcher(query); + ManagementObjectCollection processList = searcher.Get(); + result = processList.Count; + for (int i = 0; i < retry; i++) + { + if (result == expectedNumber) + { + break; + } + Thread.Sleep(1000); + processList = searcher.Get(); + result = processList.Count; + } + return result; + } + + public static object GetProcessWMIAttributeValue(string processFileName, string attributeName, string owner = null) + { + string query = "Select * from Win32_Process Where Name = \"" + processFileName + "\""; + ManagementObjectSearcher searcher = new ManagementObjectSearcher(query); + ManagementObjectCollection processList = searcher.Get(); + object result = null; + foreach (ManagementObject obj in processList) + { + string[] argList = new string[] { string.Empty, string.Empty }; + bool found = true; + + if (owner != null) + { + found = false; + int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList)); + if (returnVal == 0) + { + if (argList[0].ToUpper() == owner.ToUpper()) + { + found = true; + } + } + } + if (found) + { + result = obj.GetPropertyValue(attributeName); + break; + } + } + return result; + } + + public static string GetHttpUri(string Url, TestWebSite siteContext) + { + string tempUrl = Url.TrimStart(new char[] { '/' }); + return "http://" + siteContext.HostName + ":" + siteContext.TcpPort + "/" + tempUrl; + } + + public static string XmlParser(string xmlFileContent, string elementName, string attributeName, string childkeyValue) + { + string result = string.Empty; + + XmlDocument serviceStateXml = new XmlDocument(); + serviceStateXml.LoadXml(xmlFileContent); + + XmlNodeList elements = serviceStateXml.GetElementsByTagName(elementName); + foreach (XmlNode item in elements) + { + if (childkeyValue == null) + { + if (item.Attributes[attributeName].Value != null) + { + string newValueFound = item.Attributes[attributeName].Value; + if (result != string.Empty) + { + newValueFound += "," + newValueFound; // make the result value in comma seperated format if there are multiple nodes + } + result += newValueFound; + } + } + else + { + //int groupIndex = 0; + foreach (XmlNode groupNode in item.ChildNodes) + { + /*UrlGroup urlGroup = new UrlGroup(); + urlGroup._requestQueue = requestQueue._requestQueueName; + urlGroup._urlGroupId = groupIndex.ToString(); + + foreach (XmlNode urlNode in groupNode) + urlGroup._urls.Add(urlNode.InnerText.ToUpper()); + + requestQueue._urlGroupIds.Add(groupIndex); + requestQueue._urlGroups.Add(urlGroup); + groupIndex++; */ + } + } + } + return result; + } + + public static string RandomString(long size) + { + var random = new Random((int)DateTime.Now.Ticks); + + var builder = new StringBuilder(); + char ch; + for (int i = 0; i < size; i++) + { + ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))); + builder.Append(ch); + } + + return builder.ToString(); + } + + public static bool ResetHelper(ResetHelperMode mode) + { + bool result = false; + switch (mode) + { + case ResetHelperMode.CallIISReset: + result = CallIISReset(); + break; + case ResetHelperMode.StopHttpStartW3svc: + StopHttp(); + result = StartW3svc(); + break; + case ResetHelperMode.StopWasStartW3svc: + StopWas(); + result = StartW3svc(); + break; + case ResetHelperMode.StopW3svcStartW3svc: + StopW3svc(); + result = StartW3svc(); + break; + case ResetHelperMode.KillWorkerProcess: + result = KillWorkerProcess(); + break; + case ResetHelperMode.KillVSJitDebugger: + result = KillVSJitDebugger(); + break; + case ResetHelperMode.KillIISExpress: + result = KillIISExpress(); + break; + }; + return result; + } + + public static bool KillIISExpress() + { + bool result = false; + string query = "Select * from Win32_Process Where Name = \"iisexpress.exe\""; + ManagementObjectSearcher searcher = new ManagementObjectSearcher(query); + ManagementObjectCollection processList = searcher.Get(); + + foreach (ManagementObject obj in processList) + { + string[] argList = new string[] { string.Empty, string.Empty }; + bool foundProcess = true; + if (foundProcess) + { + obj.InvokeMethod("Terminate", null); + result = true; + } + } + return result; + } + + public static bool KillVSJitDebugger() + { + bool result = false; + string query = "Select * from Win32_Process Where Name = \"vsjitdebugger.exe\""; + ManagementObjectSearcher searcher = new ManagementObjectSearcher(query); + ManagementObjectCollection processList = searcher.Get(); + + foreach (ManagementObject obj in processList) + { + string[] argList = new string[] { string.Empty, string.Empty }; + bool foundProcess = true; + if (foundProcess) + { + LogError("Jit Debugger found"); + obj.InvokeMethod("Terminate", null); + result = true; + } + } + return result; + } + + public static bool KillWorkerProcess(string owner = null) + { + bool result = false; + + string query = "Select * from Win32_Process Where Name = \"w3wp.exe\""; + ManagementObjectSearcher searcher = new ManagementObjectSearcher(query); + ManagementObjectCollection processList = searcher.Get(); + + foreach (ManagementObject obj in processList) + { + if (owner != null) + { + string[] argList = new string[] { string.Empty, string.Empty }; + int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList)); + if (returnVal == 0) + { + bool foundProcess = true; + + if (String.Compare(argList[0], owner, true) != 0) + { + foundProcess = false; + } + if (foundProcess) + { + obj.InvokeMethod("Terminate", null); + result = true; + } + } + } + else + { + obj.InvokeMethod("Terminate", null); + result = true; + } + } + return result; + } + + public static bool KillIISExpressProcess(string owner = null) + { + bool result = false; + + string query = "Select * from Win32_Process Where Name = \"iisexpress.exe\""; + ManagementObjectSearcher searcher = new ManagementObjectSearcher(query); + ManagementObjectCollection processList = searcher.Get(); + + foreach (ManagementObject obj in processList) + { + if (owner != null) + { + string[] argList = new string[] { string.Empty, string.Empty }; + int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList)); + if (returnVal == 0) + { + bool foundProcess = true; + + if (String.Compare(argList[0], owner, true) != 0) + { + foundProcess = false; + } + if (foundProcess) + { + obj.InvokeMethod("Terminate", null); + result = true; + } + } + } + else + { + obj.InvokeMethod("Terminate", null); + result = true; + } + } + return result; + } + + public static int RunCommand(string fileName, string arguments = null, bool checkStandardError = true, bool waitForExit=true) + { + int pid = -1; + Process p = new Process(); + p.StartInfo.FileName = fileName; + if (arguments != null) + { + p.StartInfo.Arguments = arguments; + } + + if (waitForExit) + { + p.StartInfo.RedirectStandardOutput = true; + p.StartInfo.RedirectStandardError = true; + } + + p.StartInfo.UseShellExecute = false; + p.StartInfo.CreateNoWindow = true; + p.Start(); + pid = p.Id; + string standardOutput = string.Empty; + string standardError = string.Empty; + if (waitForExit) + { + standardOutput = p.StandardOutput.ReadToEnd(); + standardError = p.StandardError.ReadToEnd(); + p.WaitForExit(); + } + if (checkStandardError && standardError != string.Empty) + { + throw new Exception("Failed to run " + fileName + " " + arguments + ", Error: " + standardError + ", StandardOutput: " + standardOutput); + } + return pid; + } + + public static bool CallIISReset() + { + int result = RunCommand("iisreset", null, false); + return (result != -1); + } + + public static bool StopHttp() + { + int result = RunCommand("net", "stop http /y", false); + return (result != -1); + } + + public static bool StopWas() + { + int result = RunCommand("net", "stop was /y", false); + return (result != -1); + } + + public static bool StartWas() + { + int result = RunCommand("net", "start was", false); + return (result != -1); + } + + public static bool StopW3svc() + { + int result = RunCommand("net", "stop w3svc /y", false); + return (result != -1); + } + + public static bool StartW3svc() + { + int result = RunCommand("net", "start w3svc", false); + return (result != -1); + } + + public static string GetApplicationPath() + { + var applicationBasePath = PlatformServices.Default.Application.ApplicationBasePath; + string solutionPath = InitializeTestMachine.GetSolutionDirectory(); + string applicationPath = string.Empty; + applicationPath = Path.Combine(solutionPath, "test", "AspNetCoreModule.TestSites.Standard"); + return applicationPath; + } + + public static string GetConfigContent(ServerType serverType, string iisConfig) + { + string content = null; + if (serverType == ServerType.IISExpress) + { + content = File.ReadAllText(iisConfig); + } + return content; + } + + public static void ClearApplicationEventLog() + { + using (EventLog eventLog = new EventLog("Application")) + { + eventLog.Clear(); + } + for (int i = 0; i < 5; i++) + { + TestUtility.LogInformation("Waiting 1 seconds for eventlog to clear..."); + Thread.Sleep(1000); + EventLog systemLog = new EventLog("Application"); + if (systemLog.Entries.Count == 0) + { + break; + } + } + } + + public static List GetApplicationEvent(int id, DateTime startFrom) + { + var result = new List(); + TestUtility.LogInformation("Waiting 1 seconds for eventlog to update..."); + Thread.Sleep(1000); + EventLog systemLog = new EventLog("Application"); + foreach (EventLogEntry entry in systemLog.Entries) + { + if (entry.InstanceId == id && entry.TimeWritten >= startFrom) + { + result.Add(entry.ReplacementStrings[0]); + } + } + + return result; + } + + public static string ConvertToPunycode(string domain) + { + Uri uri = new Uri("http://" + domain); + return uri.DnsSafeHost; + } + } +} \ No newline at end of file diff --git a/test/AspNetCoreModule.Test/Framework/TestWebApplication.cs b/test/AspNetCoreModule.Test/Framework/TestWebApplication.cs new file mode 100644 index 0000000000..d3d041effa --- /dev/null +++ b/test/AspNetCoreModule.Test/Framework/TestWebApplication.cs @@ -0,0 +1,234 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; + +namespace AspNetCoreModule.Test.Framework +{ + public class TestWebApplication : IDisposable + { + private TestWebSite _testSite; + public TestWebSite TestSite + { + get + { + return _testSite; + } + set + { + _testSite = value; + } + } + + public TestWebApplication(string name, string physicalPath, string url = null) + : this(name, physicalPath, null, url) + { + } + + public TestWebApplication(string name, string physicalPath, TestWebSite siteContext, string url = null) + { + _testSite = siteContext; + _name = name; + string temp = physicalPath; + if (physicalPath.Contains("%")) + { + temp = System.Environment.ExpandEnvironmentVariables(physicalPath); + } + _physicalPath = temp; + + if (url != null) + { + _url = url; + } + else + { + string tempUrl = name.Trim(); + if (tempUrl[0] != '/') + { + _url = "/" + tempUrl; + } + else + { + _url = tempUrl; + } + } + BackupFile("web.config"); + } + + public void Dispose() + { + DeleteFile("app_offline.htm"); + RestoreFile("web.config"); + } + + private string _name = null; + public string Name + { + get + { + return _name; + } + set + { + _name = value; + } + } + + private string _physicalPath = null; + public string PhysicalPath + { + get + { + return _physicalPath; + } + set + { + _physicalPath = value; + } + } + + private string _url = null; + public string URL + { + get + { + return _url; + } + set + { + _url = value; + } + } + + public Uri GetHttpUri() + { + return new Uri("http://" + _testSite.HostName + ":" + _testSite.TcpPort.ToString() + URL); + } + + public Uri GetHttpUri(string subPath) + { + string tempSubPath = subPath; + if (!tempSubPath.StartsWith("/")) + { + tempSubPath = "/" + tempSubPath; + } + return new Uri("http://" + _testSite.HostName + ":" + _testSite.TcpPort.ToString() + URL + tempSubPath); + } + + public string _appPoolName = null; + public string AppPoolName + { + get + { + if (_appPoolName == null) + { + _appPoolName = "DefaultAppPool"; + } + return _appPoolName; + } + set + { + _appPoolName = value; + } + } + + public string GetProcessFileName() + { + string filePath = Path.Combine(_physicalPath, "web.config"); + string result = null; + + // read web.config + string fileContent = TestUtility.FileReadAllText(filePath); + + // get the value of processPath attribute of aspNetCore element + if (fileContent != null) + { + result = TestUtility.XmlParser(fileContent, "aspNetCore", "processPath", null); + } + + // split fileName from full path + result = Path.GetFileName(result); + + // append .exe if it wasn't used + if (!result.Contains(".exe")) + { + result = result + ".exe"; + } + return result; + } + + public string GetArgumentFileName() + { + string filePath = Path.Combine(_physicalPath, "web.config"); + string result = null; + + // read web.config + string fileContent = TestUtility.FileReadAllText(filePath); + + // get the value of processPath attribute of aspNetCore element + if (fileContent != null) + { + result = TestUtility.XmlParser(fileContent, "aspNetCore", "arguments", null); + } + + // split fileName from full path + result = Path.GetFileName(result); + return result; + } + + public void BackupFile(string from) + { + string fromfile = Path.Combine(_physicalPath, from); + string tofile = Path.Combine(_physicalPath, fromfile + ".bak"); + TestUtility.FileCopy(fromfile, tofile, overWrite: false); + } + + public void RestoreFile(string from) + { + string fromfile = Path.Combine(_physicalPath, from + ".bak"); + string tofile = Path.Combine(_physicalPath, from); + if (!File.Exists(tofile)) + { + BackupFile(from); + } + TestUtility.FileCopy(fromfile, tofile); + } + + public string GetDirectoryPathWith(string subPath) + { + return Path.Combine(_physicalPath, subPath); + } + + public void DeleteFile(string file = "app_offline.htm") + { + string filePath = Path.Combine(_physicalPath, file); + TestUtility.DeleteFile(filePath); + } + + public void CreateFile(string[] content, string file = "app_offline.htm") + { + string filePath = Path.Combine(_physicalPath, file); + TestUtility.CreateFile(filePath, content); + } + + public void MoveFile(string from, string to) + { + string fromfile = Path.Combine(_physicalPath, from); + string tofile = Path.Combine(_physicalPath, to); + TestUtility.FileMove(fromfile, tofile); + } + + public void DeleteDirectory(string directory) + { + string directoryPath = Path.Combine(_physicalPath, directory); + TestUtility.DeleteDirectory(directoryPath); + } + + public void CreateDirectory(string directory) + { + string directoryPath = Path.Combine(_physicalPath, directory); + TestUtility.CreateDirectory(directoryPath); + } + } +} \ No newline at end of file diff --git a/test/AspNetCoreModule.Test/Framework/TestWebSite.cs b/test/AspNetCoreModule.Test/Framework/TestWebSite.cs new file mode 100644 index 0000000000..504962b5ec --- /dev/null +++ b/test/AspNetCoreModule.Test/Framework/TestWebSite.cs @@ -0,0 +1,244 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using Microsoft.Extensions.Logging; +using System.Diagnostics; + +namespace AspNetCoreModule.Test.Framework +{ + public class TestWebSite : IDisposable + { + static private bool _publishedAspnetCoreApp = false; + + public TestWebApplication RootAppContext; + public TestWebApplication AspNetCoreApp; + public TestWebApplication WebSocketApp; + public TestWebApplication URLRewriteApp; + public TestUtility testHelper; + private ILogger _logger; + private int _iisExpressPidBackup = -1; + + private string postfix = string.Empty; + + public void Dispose() + { + TestUtility.LogInformation("TestWebSite::Dispose() Start"); + + if (_iisExpressPidBackup != -1) + { + var iisExpressProcess = Process.GetProcessById(Convert.ToInt32(_iisExpressPidBackup)); + iisExpressProcess.Kill(); + iisExpressProcess.WaitForExit(); + } + + TestUtility.LogInformation("TestWebSite::Dispose() End"); + } + + public string _hostName = null; + public string HostName + { + get + { + if (_hostName == null) + { + _hostName = "localhost"; + } + return _hostName; + } + set + { + _hostName = value; + } + } + + public string _siteName = null; + public string SiteName + { + get + { + return _siteName; + } + set + { + _siteName = value; + } + } + + public int _tcpPort = 8080; + public int TcpPort + { + get + { + return _tcpPort; + } + set + { + _tcpPort = value; + } + } + + public TestWebSite(IISConfigUtility.AppPoolBitness appPoolBitness, string loggerPrefix = "ANCMTest", ServerType serverType = ServerType.IIS) + { + TestUtility.LogInformation("TestWebSite::TestWebSite() Start"); + + string solutionPath = InitializeTestMachine.GetSolutionDirectory(); + + if (serverType == ServerType.IIS) + { + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + } + + // initialize logger for TestUtility + _logger = new LoggerFactory() + .AddConsole() + .CreateLogger(string.Format(loggerPrefix)); + + testHelper = new TestUtility(_logger); + + // + // Initialize context variables + // + string siteRootPath = string.Empty; + string siteName = string.Empty; + + // repeat three times until getting the valid temporary directory path + for (int i = 0; i < 3; i++) + { + string postfix = Path.GetRandomFileName(); + siteName = loggerPrefix.Replace(" ", "") + "_" + postfix; + siteRootPath = Path.Combine(Environment.ExpandEnvironmentVariables("%SystemDrive%") + @"\", "inetpub", "ANCMTest", siteName); + if (!Directory.Exists(siteRootPath)) + { + break; + } + } + + TestUtility.DirectoryCopy(Path.Combine(solutionPath, "test", "WebRoot"), siteRootPath); + string aspnetCoreAppRootPath = Path.Combine(siteRootPath, "AspNetCoreApp"); + string srcPath = TestUtility.GetApplicationPath(); + + // + // Currently we use only DotnetCore v1.1 + // + string publishPath = Path.Combine(srcPath, "bin", "Debug", "netcoreapp1.1", "publish"); + + // + // Publish aspnetcore app + // + if (_publishedAspnetCoreApp != true) + { + string argumentForDotNet = "publish " + srcPath; + TestUtility.LogInformation("TestWebSite::TestWebSite() StandardTestApp is not published, trying to publish on the fly: dotnet.exe " + argumentForDotNet); + TestUtility.RunCommand("dotnet", argumentForDotNet); + _publishedAspnetCoreApp = true; + } + + // check published files + bool checkPublishedFiles = false; + string[] publishedFiles = Directory.GetFiles(publishPath); + foreach (var item in publishedFiles) + { + if (Path.GetFileName(item) == "web.config") + { + checkPublishedFiles = true; + } + } + + if (!checkPublishedFiles) + { + throw new System.ApplicationException("web.config is not available in " + publishPath); + } + + // Copy the pubishpath to standardAppRootPath + TestUtility.DirectoryCopy(publishPath, aspnetCoreAppRootPath); + + int tcpPort = InitializeTestMachine.SiteId++; + int siteId = tcpPort; + + // + // initialize class member variables + // + string appPoolName = null; + if (serverType == ServerType.IIS) + { + appPoolName = siteName; + } + else if (serverType == ServerType.IISExpress) + { + appPoolName = "Clr4IntegratedAppPool"; + } + + // Initialize member variables + _hostName = "localhost"; + _siteName = siteName; + _tcpPort = tcpPort; + + RootAppContext = new TestWebApplication("/", Path.Combine(siteRootPath, "WebSite1"), this); + RootAppContext.RestoreFile("web.config"); + RootAppContext.DeleteFile("app_offline.htm"); + RootAppContext.AppPoolName = appPoolName; + + AspNetCoreApp = new TestWebApplication("/AspNetCoreApp", aspnetCoreAppRootPath, this); + AspNetCoreApp.AppPoolName = appPoolName; + AspNetCoreApp.RestoreFile("web.config"); + AspNetCoreApp.DeleteFile("app_offline.htm"); + + WebSocketApp = new TestWebApplication("/WebSocketApp", Path.Combine(siteRootPath, "WebSocket"), this); + WebSocketApp.AppPoolName = appPoolName; + WebSocketApp.RestoreFile("web.config"); + WebSocketApp.DeleteFile("app_offline.htm"); + + URLRewriteApp = new TestWebApplication("/URLRewriteApp", Path.Combine(siteRootPath, "URLRewrite"), this); + URLRewriteApp.AppPoolName = appPoolName; + URLRewriteApp.RestoreFile("web.config"); + URLRewriteApp.DeleteFile("app_offline.htm"); + + // copy http.config to the test site root directory and initialize iisExpressConfigPath with the path + string iisExpressConfigPath = null; + if (serverType == ServerType.IISExpress) + { + iisExpressConfigPath = Path.Combine(siteRootPath, "http.config"); + TestUtility.FileCopy(Path.Combine(solutionPath, "test", "AspNetCoreModule.Test", "http.config"), iisExpressConfigPath); + } + + // + // Create site and apps + // + using (var iisConfig = new IISConfigUtility(serverType, iisExpressConfigPath)) + { + if (serverType == ServerType.IIS) + { + iisConfig.CreateAppPool(appPoolName); + bool is32bit = (appPoolBitness == IISConfigUtility.AppPoolBitness.enable32Bit); + iisConfig.SetAppPoolSetting(appPoolName, "enable32BitAppOnWin64", is32bit); + } + iisConfig.CreateSite(siteName, RootAppContext.PhysicalPath, siteId, this.TcpPort, appPoolName); + iisConfig.CreateApp(siteName, AspNetCoreApp.Name, AspNetCoreApp.PhysicalPath, appPoolName); + iisConfig.CreateApp(siteName, WebSocketApp.Name, WebSocketApp.PhysicalPath, appPoolName); + iisConfig.CreateApp(siteName, URLRewriteApp.Name, URLRewriteApp.PhysicalPath, appPoolName); + } + + if (serverType == ServerType.IISExpress) + { + string cmdline; + string argument = "/siteid:" + siteId + " /config:" + iisExpressConfigPath; + + if (Directory.Exists(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%")) && appPoolBitness == IISConfigUtility.AppPoolBitness.enable32Bit) + { + cmdline = Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"), "IIS Express", "iisexpress.exe"); + } + else + { + cmdline = Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "IIS Express", "iisexpress.exe"); + } + TestUtility.LogInformation("TestWebSite::TestWebSite() Start IISExpress: " + cmdline + " " + argument); + _iisExpressPidBackup = TestUtility.RunCommand(cmdline, argument, false, false); + } + + TestUtility.LogInformation("TestWebSite::TestWebSite() End"); + } + } +} diff --git a/test/AspNetCoreModule.Test/FunctionalTest.cs b/test/AspNetCoreModule.Test/FunctionalTest.cs new file mode 100644 index 0000000000..9a8b2ee960 --- /dev/null +++ b/test/AspNetCoreModule.Test/FunctionalTest.cs @@ -0,0 +1,293 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using AspNetCoreModule.Test.Framework; +using Microsoft.AspNetCore.Testing.xunit; +using System.Threading.Tasks; +using Xunit; + +namespace AspNetCoreModule.Test +{ + public class FunctionalTest : FunctionalTestHelper, IClassFixture + { + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(ServerType.IISExpress, IISConfigUtility.AppPoolBitness.noChange)] + [InlineData(ServerType.IISExpress, IISConfigUtility.AppPoolBitness.enable32Bit)] + public Task BasicTestOnIISExpress(ServerType serverType, IISConfigUtility.AppPoolBitness appPoolBitness) + { + return DoBasicTest(serverType, appPoolBitness); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(ServerType.IIS, IISConfigUtility.AppPoolBitness.noChange)] + [InlineData(ServerType.IIS, IISConfigUtility.AppPoolBitness.enable32Bit)] + public Task BasicTestOnIIS(ServerType serverType, IISConfigUtility.AppPoolBitness appPoolBitness) + { + return DoBasicTest(serverType, appPoolBitness); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, 5)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, 5)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, 1)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, 0)] + public Task RapidFailsPerMinuteTest(IISConfigUtility.AppPoolBitness appPoolBitness, int valueOfRapidFailsPerMinute) + { + return DoRapidFailsPerMinuteTest(appPoolBitness, valueOfRapidFailsPerMinute); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, 25, 19)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, 25, 19)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, 5, 4)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, 5, 4)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, 0, 0)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, 0, 0)] + public Task ShutdownTimeLimitTest(IISConfigUtility.AppPoolBitness appPoolBitness, int valueOfshutdownTimeLimit, int expectedClosingTime) + { + return DoShutdownTimeLimitTest(appPoolBitness, valueOfshutdownTimeLimit, expectedClosingTime); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, 10)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, 10)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, 1)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, 1)] + public Task StartupTimeLimitTest(IISConfigUtility.AppPoolBitness appPoolBitness, int starupTimeLimit) + { + return DoStartupTimeLimitTest(appPoolBitness, starupTimeLimit); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789")] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, "a")] + public Task WebSocketTest(IISConfigUtility.AppPoolBitness appPoolBitness, string testData) + { + return DoWebSocketTest(appPoolBitness, testData); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange)] + public Task RecycleApplicationAfterBackendProcessBeingKilled(IISConfigUtility.AppPoolBitness appPoolBitness) + { + return DoRecycleApplicationAfterBackendProcessBeingKilled(appPoolBitness); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange)] + public Task RecycleApplicationAfterW3WPProcessBeingKilled(IISConfigUtility.AppPoolBitness appPoolBitness) + { + return DoRecycleApplicationAfterW3WPProcessBeingKilled(appPoolBitness); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange)] + public Task RecycleApplicationAfterWebConfigUpdated(IISConfigUtility.AppPoolBitness appPoolBitness) + { + return DoRecycleApplicationAfterWebConfigUpdated(appPoolBitness); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange)] + public Task RecycleApplicationWithURLRewrite(IISConfigUtility.AppPoolBitness appPoolBitness) + { + return DoRecycleApplicationWithURLRewrite(appPoolBitness); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange)] + public Task RecycleParentApplicationWithURLRewrite(IISConfigUtility.AppPoolBitness appPoolBitness) + { + return DoRecycleParentApplicationWithURLRewrite(appPoolBitness); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange)] + public Task EnvironmentVariablesTest(IISConfigUtility.AppPoolBitness appPoolBitness) + { + return DoEnvironmentVariablesTest(appPoolBitness); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange)] + public Task AppOfflineTestWithRenaming(IISConfigUtility.AppPoolBitness appPoolBitness) + { + return DoAppOfflineTestWithRenaming(appPoolBitness); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange)] + public Task AppOfflineTestWithUrlRewriteAndDeleting(IISConfigUtility.AppPoolBitness appPoolBitness) + { + return DoAppOfflineTestWithUrlRewriteAndDeleting(appPoolBitness); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789")] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, "a")] + public Task PostMethodTest(IISConfigUtility.AppPoolBitness appPoolBitness, string testData) + { + return DoPostMethodTest(appPoolBitness, testData); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange)] + public Task DisableStartUpErrorPageTest(IISConfigUtility.AppPoolBitness appPoolBitness) + { + return DoDisableStartUpErrorPageTest(appPoolBitness); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, 10)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, 2)] + public Task ProcessesPerApplicationTest(IISConfigUtility.AppPoolBitness appPoolBitness, int valueOfProcessesPerApplication) + { + return DoProcessesPerApplicationTest(appPoolBitness, valueOfProcessesPerApplication); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, "00:02:00")] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, "00:02:00")] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, "00:01:00")] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, "00:01:00")] + public Task RequestTimeoutTest(IISConfigUtility.AppPoolBitness appPoolBitness, string requestTimeout) + { + return DoRequestTimeoutTest(appPoolBitness, requestTimeout); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange)] + public Task StdoutLogEnabledTest(IISConfigUtility.AppPoolBitness appPoolBitness) + { + return DoStdoutLogEnabledTest(appPoolBitness); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, "dotnet.exe", "./")] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, "dotnet", @".\")] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, "$env", "")] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, "$env", "")] + public Task ProcessPathAndArgumentsTest(IISConfigUtility.AppPoolBitness appPoolBitness, string processPath, string argumentsPrefix) + { + return DoProcessPathAndArgumentsTest(appPoolBitness, processPath, argumentsPrefix); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, true)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, false)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, true)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, false)] + public Task ForwardWindowsAuthTokenTest(IISConfigUtility.AppPoolBitness appPoolBitness, bool enabledForwardWindowsAuthToken) + { + return DoForwardWindowsAuthTokenTest(appPoolBitness, enabledForwardWindowsAuthToken); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange)] + public Task RecylingAppPoolTest(IISConfigUtility.AppPoolBitness appPoolBitness) + { + return DoRecylingAppPoolTest(appPoolBitness); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, true, true)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, false, false)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, true, false)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, false, true)] + public Task CompressionTest(IISConfigUtility.AppPoolBitness appPoolBitness, bool useCompressionMiddleWare, bool enableIISCompression) + { + return DoCompressionTest(appPoolBitness, useCompressionMiddleWare, enableIISCompression); + } + + [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange)] + public Task CachingTest(IISConfigUtility.AppPoolBitness appPoolBitness) + { + return DoCachingTest(appPoolBitness); + } + } +} diff --git a/test/AspNetCoreModule.Test/FunctionalTestHelper.cs b/test/AspNetCoreModule.Test/FunctionalTestHelper.cs new file mode 100644 index 0000000000..d1cc5e0f24 --- /dev/null +++ b/test/AspNetCoreModule.Test/FunctionalTestHelper.cs @@ -0,0 +1,1375 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using AspNetCoreModule.Test.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.Net.Http.Headers; +using Xunit; +using Xunit.Sdk; +using System.Diagnostics; +using System.Net; +using System.Threading; +using AspNetCoreModule.Test.WebSocketClient; +using System.Text; +using System.IO; +using System.Security.Principal; +using System.IO.Compression; + +namespace AspNetCoreModule.Test +{ + public class FunctionalTestHelper + { + private const int _repeatCount = 3; + + public enum ReturnValueType + { + ResponseBody, + ResponseBodyAndHeaders, + ResponseStatus, + None + } + + public static async Task DoBasicTest(ServerType serverType, IISConfigUtility.AppPoolBitness appPoolBitness) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoBasicTest", serverType)) + { + string backendProcessId_old = null; + + DateTime startTime = DateTime.Now; + + string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK); + Assert.NotEqual(backendProcessId_old, backendProcessId); + var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId)); + Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", "")); + Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId)); + + var httpClientHandler = new HttpClientHandler(); + var httpClient = new HttpClient(httpClientHandler) + { + BaseAddress = testSite.AspNetCoreApp.GetHttpUri(), + Timeout = TimeSpan.FromSeconds(5), + }; + + // Invoke given test scenario function + await CheckChunkedAsync(httpClient, testSite.AspNetCoreApp); + } + } + + public static async Task DoRecycleApplicationAfterBackendProcessBeingKilled(IISConfigUtility.AppPoolBitness appPoolBitness) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoRecycleApplicationAfterBackendProcessBeingKilled")) + { + string backendProcessId_old = null; + const int repeatCount = 3; + for (int i = 0; i < repeatCount; i++) + { + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + + DateTime startTime = DateTime.Now; + Thread.Sleep(1000); + + string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK); + Assert.NotEqual(backendProcessId_old, backendProcessId); + backendProcessId_old = backendProcessId; + var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId)); + Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", "")); + Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId)); + backendProcess.Kill(); + Thread.Sleep(500); + } + } + } + + public static async Task DoRecycleApplicationAfterW3WPProcessBeingKilled(IISConfigUtility.AppPoolBitness appPoolBitness) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoRecycleApplicationAfterW3WPProcessBeingKilled")) + { + string backendProcessId_old = null; + const int repeatCount = 3; + for (int i = 0; i < repeatCount; i++) + { + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + + DateTime startTime = DateTime.Now; + Thread.Sleep(1000); + + string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK); + Assert.NotEqual(backendProcessId_old, backendProcessId); + backendProcessId_old = backendProcessId; + var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId)); + Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", "")); + Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId)); + + // get process id of IIS worker process (w3wp.exe) + string userName = testSite.SiteName; + int processIdOfWorkerProcess = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("w3wp.exe", "Handle", userName)); + var workerProcess = Process.GetProcessById(Convert.ToInt32(processIdOfWorkerProcess)); + workerProcess.Kill(); + + Thread.Sleep(500); + } + } + } + + public static async Task DoRecycleApplicationAfterWebConfigUpdated(IISConfigUtility.AppPoolBitness appPoolBitness) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoRecycleApplicationAfterWebConfigUpdated")) + { + string backendProcessId_old = null; + const int repeatCount = 3; + for (int i = 0; i < repeatCount; i++) + { + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + + DateTime startTime = DateTime.Now; + Thread.Sleep(1000); + + string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK); + var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId)); + Assert.NotEqual(backendProcessId_old, backendProcessId); + backendProcessId_old = backendProcessId; + Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", "")); + Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId)); + testSite.AspNetCoreApp.MoveFile("web.config", "_web.config"); + Thread.Sleep(500); + testSite.AspNetCoreApp.MoveFile("_web.config", "web.config"); + } + + // restore web.config + testSite.AspNetCoreApp.RestoreFile("web.config"); + + } + } + + public static async Task DoRecycleApplicationWithURLRewrite(IISConfigUtility.AppPoolBitness appPoolBitness) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoRecycleApplicationWithURLRewrite")) + { + string backendProcessId_old = null; + const int repeatCount = 3; + for (int i = 0; i < repeatCount; i++) + { + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + + DateTime startTime = DateTime.Now; + Thread.Sleep(500); + + string urlForUrlRewrite = testSite.URLRewriteApp.URL + "/Rewrite2/" + testSite.AspNetCoreApp.URL + "/GetProcessId"; + string backendProcessId = await GetResponse(testSite.RootAppContext.GetHttpUri(urlForUrlRewrite), HttpStatusCode.OK); + var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId)); + Assert.NotEqual(backendProcessId_old, backendProcessId); + backendProcessId_old = backendProcessId; + Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", "")); + Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId)); + + testSite.AspNetCoreApp.MoveFile("web.config", "_web.config"); + Thread.Sleep(500); + testSite.AspNetCoreApp.MoveFile("_web.config", "web.config"); + } + + // restore web.config + testSite.AspNetCoreApp.RestoreFile("web.config"); + + } + } + + public static async Task DoRecycleParentApplicationWithURLRewrite(IISConfigUtility.AppPoolBitness appPoolBitness) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoRecycleParentApplicationWithURLRewrite")) + { + string backendProcessId_old = null; + const int repeatCount = 3; + for (int i = 0; i < repeatCount; i++) + { + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + + DateTime startTime = DateTime.Now; + Thread.Sleep(1000); + + string urlForUrlRewrite = testSite.URLRewriteApp.URL + "/Rewrite2/" + testSite.AspNetCoreApp.URL + "/GetProcessId"; + string backendProcessId = await GetResponse(testSite.RootAppContext.GetHttpUri(urlForUrlRewrite), HttpStatusCode.OK); + var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId)); + Assert.NotEqual(backendProcessId_old, backendProcessId); + backendProcessId_old = backendProcessId; + Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", "")); + Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId)); + testSite.RootAppContext.MoveFile("web.config", "_web.config"); + Thread.Sleep(500); + testSite.RootAppContext.MoveFile("_web.config", "web.config"); + } + + // restore web.config + testSite.RootAppContext.RestoreFile("web.config"); + } + } + + public static async Task DoEnvironmentVariablesTest(IISConfigUtility.AppPoolBitness appPoolBitness) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoEnvironmentVariablesTest")) + { + using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + { + DateTime startTime = DateTime.Now; + Thread.Sleep(500); + + string totalNumber = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetEnvironmentVariables"), HttpStatusCode.OK); + Assert.True(totalNumber == (await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetEnvironmentVariables"), HttpStatusCode.OK))); + + iisConfig.SetANCMConfig( + testSite.SiteName, + testSite.AspNetCoreApp.Name, + "environmentVariable", + new string[] { "ANCMTestFoo", "foo" } + ); + + Thread.Sleep(500); + + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + + int expectedValue = Convert.ToInt32(totalNumber) + 1; + Assert.True(expectedValue.ToString() == (await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetEnvironmentVariables"), HttpStatusCode.OK))); + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "environmentVariable", new string[] { "ANCMTestBar", "bar" }); + Thread.Sleep(500); + + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + + expectedValue++; + Assert.True("foo" == (await GetResponse(testSite.AspNetCoreApp.GetHttpUri("ExpandEnvironmentVariablesANCMTestFoo"), HttpStatusCode.OK))); + Assert.True("bar" == (await GetResponse(testSite.AspNetCoreApp.GetHttpUri("ExpandEnvironmentVariablesANCMTestBar"), HttpStatusCode.OK))); + } + + testSite.AspNetCoreApp.RestoreFile("web.config"); + } + } + + public static async Task DoAppOfflineTestWithRenaming(IISConfigUtility.AppPoolBitness appPoolBitness) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoAppOfflineTestWithRenaming")) + { + string backendProcessId_old = null; + string fileContent = "BackEndAppOffline"; + testSite.AspNetCoreApp.CreateFile(new string[] { fileContent }, "App_Offline.Htm"); + + for (int i = 0; i < _repeatCount; i++) + { + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + + DateTime startTime = DateTime.Now; + Thread.Sleep(1100); + + // verify 503 + await VerifyResponseBody(testSite.AspNetCoreApp.GetHttpUri(), fileContent + "\r\n", HttpStatusCode.ServiceUnavailable); + + // 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.GetHttpUri("GetProcessId"), HttpStatusCode.OK); + var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId)); + Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", "")); + Assert.NotEqual(backendProcessId_old, backendProcessId); + backendProcessId_old = backendProcessId; + Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId)); + + // rename back to app_offline.htm + testSite.AspNetCoreApp.MoveFile("_App_Offline.Htm", "App_Offline.Htm"); + } + } + } + + public static async Task DoAppOfflineTestWithUrlRewriteAndDeleting(IISConfigUtility.AppPoolBitness appPoolBitness) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoAppOfflineTestWithUrlRewriteAndDeleting")) + { + string backendProcessId_old = null; + string fileContent = "BackEndAppOffline2"; + testSite.AspNetCoreApp.CreateFile(new string[] { fileContent }, "App_Offline.Htm"); + + for (int i = 0; i < _repeatCount; i++) + { + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + + DateTime startTime = DateTime.Now; + Thread.Sleep(500); + + // verify 503 + string urlForUrlRewrite = testSite.URLRewriteApp.URL + "/Rewrite2/" + testSite.AspNetCoreApp.URL + "/GetProcessId"; + await VerifyResponseBody(testSite.RootAppContext.GetHttpUri(urlForUrlRewrite), fileContent + "\r\n", HttpStatusCode.ServiceUnavailable); + + // delete app_offline.htm and verify 200 + testSite.AspNetCoreApp.DeleteFile("App_Offline.Htm"); + string backendProcessId = await GetResponse(testSite.RootAppContext.GetHttpUri(urlForUrlRewrite), HttpStatusCode.OK); + var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId)); + Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", "")); + Assert.NotEqual(backendProcessId_old, backendProcessId); + backendProcessId_old = backendProcessId; + Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId)); + + // create app_offline.htm again + testSite.AspNetCoreApp.CreateFile(new string[] { fileContent }, "App_Offline.Htm"); + } + } + } + + public static async Task DoPostMethodTest(IISConfigUtility.AppPoolBitness appPoolBitness, string testData) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoPostMethodTest")) + { + var postFormData = new[] + { + new KeyValuePair("FirstName", "Mickey"), + new KeyValuePair("LastName", "Mouse"), + new KeyValuePair("TestData", testData), + }; + var expectedResponseBody = "FirstName=Mickey&LastName=Mouse&TestData=" + testData; + await VerifyPostResponseBody(testSite.AspNetCoreApp.GetHttpUri("EchoPostData"), postFormData, expectedResponseBody, HttpStatusCode.OK); + } + } + + public static async Task DoDisableStartUpErrorPageTest(IISConfigUtility.AppPoolBitness appPoolBitness) + { + int errorEventId = 1000; + string errorMessageContainThis = "bogus"; // bogus path value to cause 502.3 error + + using (var testSite = new TestWebSite(appPoolBitness, "DoDisableStartUpErrorPageTest")) + { + testSite.AspNetCoreApp.DeleteFile("custom502-3.htm"); + string curstomErrorMessage = "ANCMTest502-3"; + testSite.AspNetCoreApp.CreateFile(new string[] { curstomErrorMessage }, "custom502-3.htm"); + + Thread.Sleep(500); + + using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + { + DateTime startTime = DateTime.Now; + Thread.Sleep(500); + + iisConfig.ConfigureCustomLogging(testSite.SiteName, testSite.AspNetCoreApp.Name, 502, 3, "custom502-3.htm"); + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "disableStartUpErrorPage", true); + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "processPath", errorMessageContainThis); + + var responseBody = await GetResponse(testSite.AspNetCoreApp.GetHttpUri(), HttpStatusCode.BadGateway); + responseBody = responseBody.Replace("\r", "").Replace("\n", "").Trim(); + Assert.True(responseBody == curstomErrorMessage); + + // verify event error log + Assert.True(TestUtility.RetryHelper((arg1, arg2, arg3) => VerifyApplicationEventLog(arg1, arg2, arg3), errorEventId, startTime, errorMessageContainThis)); + + // try again after setting "false" value + startTime = DateTime.Now; + Thread.Sleep(500); + + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "disableStartUpErrorPage", false); + Thread.Sleep(500); + + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + + responseBody = await GetResponse(testSite.AspNetCoreApp.GetHttpUri(), HttpStatusCode.BadGateway); + Assert.True(responseBody.Contains("808681")); + + // verify event error log + Assert.True(TestUtility.RetryHelper((arg1, arg2, arg3) => VerifyApplicationEventLog(arg1, arg2, arg3), errorEventId, startTime, errorMessageContainThis)); + } + testSite.AspNetCoreApp.RestoreFile("web.config"); + } + } + + public static async Task DoRapidFailsPerMinuteTest(IISConfigUtility.AppPoolBitness appPoolBitness, int valueOfRapidFailsPerMinute) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoRapidFailsPerMinuteTest")) + { + using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + { + bool rapidFailsTriggered = false; + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "rapidFailsPerMinute", valueOfRapidFailsPerMinute); + + string backendProcessId_old = null; + const int repeatCount = 10; + + DateTime startTime = DateTime.Now; + Thread.Sleep(50); + + for (int i = 0; i < repeatCount; i++) + { + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + + DateTime startTimeInsideLooping = DateTime.Now; + Thread.Sleep(50); + + var statusCode = await GetResponseStatusCode(testSite.AspNetCoreApp.GetHttpUri("GetProcessId")); + if (statusCode != HttpStatusCode.OK.ToString()) + { + Assert.True(i >= valueOfRapidFailsPerMinute, i.ToString() + "is greater than or equals to " + valueOfRapidFailsPerMinute.ToString()); + Assert.True(i < valueOfRapidFailsPerMinute + 3, i.ToString() + "is less than " + (valueOfRapidFailsPerMinute + 3).ToString()); + rapidFailsTriggered = true; + break; + } + + string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK); + Assert.NotEqual(backendProcessId_old, backendProcessId); + 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); + + backendProcess.Kill(); + Thread.Sleep(3000); + } + Assert.True(rapidFailsTriggered, "Verify 503 error"); + + // verify event error log + int errorEventId = 1003; + string errorMessageContainThis = "'" + valueOfRapidFailsPerMinute + "'"; // part of error message + Assert.True(TestUtility.RetryHelper((arg1, arg2, arg3) => VerifyApplicationEventLog(arg1, arg2, arg3), errorEventId, startTime, errorMessageContainThis)); + } + testSite.AspNetCoreApp.RestoreFile("web.config"); + } + } + + public static async Task DoProcessesPerApplicationTest(IISConfigUtility.AppPoolBitness appPoolBitness, int valueOfProcessesPerApplication) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoProcessesPerApplicationTest")) + { + using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + { + DateTime startTime = DateTime.Now; + + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "processesPerApplication", valueOfProcessesPerApplication); + HashSet processIDs = new HashSet(); + + for (int i = 0; i < 20; i++) + { + string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK); + int id = Convert.ToInt32(backendProcessId); + if (!processIDs.Contains(id)) + { + processIDs.Add(id); + } + + if (i == (valueOfProcessesPerApplication - 1)) + { + Assert.Equal(valueOfProcessesPerApplication, processIDs.Count); + } + } + + Assert.Equal(valueOfProcessesPerApplication, processIDs.Count); + foreach (var id in processIDs) + { + var backendProcess = Process.GetProcessById(id); + Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", "")); + Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, id.ToString())); + } + + // reset the value with 1 again + processIDs = new HashSet(); + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "processesPerApplication", 1); + Thread.Sleep(3000); + + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + Thread.Sleep(500); + + for (int i = 0; i < 20; i++) + { + string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK); + int id = Convert.ToInt32(backendProcessId); + if (!processIDs.Contains(id)) + { + processIDs.Add(id); + } + } + Assert.Equal(1, processIDs.Count); + } + + testSite.AspNetCoreApp.RestoreFile("web.config"); + } + } + + public static async Task DoStartupTimeLimitTest(IISConfigUtility.AppPoolBitness appPoolBitness, int startupTimeLimit) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoStartupTimeLimitTest")) + { + using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + { + int startupDelay = 3; //3 seconds + iisConfig.SetANCMConfig( + testSite.SiteName, + testSite.AspNetCoreApp.Name, + "environmentVariable", + new string[] { "ANCMTestStartUpDelay", (startupDelay * 1000).ToString() } + ); + + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "requestTimeout", TimeSpan.Parse("00:01:00")); // 1 minute + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "startupTimeLimit", startupTimeLimit); + + Thread.Sleep(500); + if (startupTimeLimit < startupDelay) + { + await VerifyResponseStatus(testSite.AspNetCoreApp.GetHttpUri("DoSleep3000"), HttpStatusCode.BadGateway); + } + else + { + await VerifyResponseBody(testSite.AspNetCoreApp.GetHttpUri("DoSleep3000"), "Running", HttpStatusCode.OK); + } + } + testSite.AspNetCoreApp.RestoreFile("web.config"); + } + } + + public static async Task DoRequestTimeoutTest(IISConfigUtility.AppPoolBitness appPoolBitness, string requestTimeout) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoRequestTimeoutTest")) + { + using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + { + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "requestTimeout", TimeSpan.Parse(requestTimeout)); + Thread.Sleep(500); + + if (requestTimeout.ToString() == "00:02:00") + { + await VerifyResponseBody(testSite.AspNetCoreApp.GetHttpUri("DoSleep65000"), "Running", HttpStatusCode.OK, timeout:70); + } + else if (requestTimeout.ToString() == "00:01:00") + { + await VerifyResponseStatus(testSite.AspNetCoreApp.GetHttpUri("DoSleep65000"), HttpStatusCode.BadGateway, 70); + } + else + { + throw new System.ApplicationException("wrong data"); + } + } + testSite.AspNetCoreApp.RestoreFile("web.config"); + } + } + + public static async Task DoShutdownTimeLimitTest(IISConfigUtility.AppPoolBitness appPoolBitness, int valueOfshutdownTimeLimit, int expectedClosingTime) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoShutdownTimeLimitTest")) + { + using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + { + // Set new value (10 second) to make the backend process get the Ctrl-C signal and measure when the recycle happens + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "shutdownTimeLimit", valueOfshutdownTimeLimit); + iisConfig.SetANCMConfig( + testSite.SiteName, + testSite.AspNetCoreApp.Name, + "environmentVariable", + new string[] { "ANCMTestShutdownDelay", "20000" } + ); + + await VerifyResponseBody(testSite.AspNetCoreApp.GetHttpUri(), "Running", HttpStatusCode.OK); + string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK); + var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId)); + + // Set a new value such as 100 to make the backend process being recycled + DateTime startTime = DateTime.Now; + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "shutdownTimeLimit", 100); + backendProcess.WaitForExit(30000); + DateTime endTime = DateTime.Now; + var difference = endTime - startTime; + Assert.True(difference.Seconds >= expectedClosingTime); + Assert.True(difference.Seconds < expectedClosingTime + 3); + Assert.True(backendProcessId != await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK)); + await VerifyResponseBody(testSite.AspNetCoreApp.GetHttpUri(), "Running", HttpStatusCode.OK); + } + + testSite.AspNetCoreApp.RestoreFile("web.config"); + } + } + public static async Task DoStdoutLogEnabledTest(IISConfigUtility.AppPoolBitness appPoolBitness) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoStdoutLogEnabledTest")) + { + testSite.AspNetCoreApp.DeleteDirectory("logs"); + + using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + { + DateTime startTime = DateTime.Now; + Thread.Sleep(500); + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "stdoutLogEnabled", true); + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "stdoutLogFile", @".\logs\stdout"); + + string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK); + string logPath = testSite.AspNetCoreApp.GetDirectoryPathWith("logs"); + Assert.False(Directory.Exists(logPath)); + Assert.True(TestUtility.RetryHelper((arg1, arg2, arg3) => VerifyApplicationEventLog(arg1, arg2, arg3), 1004, startTime, @"logs\stdout")); + Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId)); + + testSite.AspNetCoreApp.CreateDirectory("logs"); + + // verify the log file is not created because backend process is not recycled + Assert.True(Directory.GetFiles(logPath).Length == 0); + Assert.True(backendProcessId == (await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK))); + + // reset web.config to recycle backend process and give write permission to the Users local group to which IIS workerprocess identity belongs + SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null); + TestUtility.GiveWritePermissionTo(logPath, sid); + + startTime = DateTime.Now; + Thread.Sleep(500); + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "stdoutLogEnabled", false); + + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "stdoutLogEnabled", true); + + Assert.True(backendProcessId != (await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK))); + + // Verify log file is created now after backend process is recycled + Assert.True(TestUtility.RetryHelper(p => { return Directory.GetFiles(p).Length > 0 ? true : false; }, logPath)); + } + + testSite.AspNetCoreApp.RestoreFile("web.config"); + } + } + + public static async Task DoProcessPathAndArgumentsTest(IISConfigUtility.AppPoolBitness appPoolBitness, string processPath, string argumentsPrefix) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoProcessPathAndArgumentsTest")) + { + using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + { + string arguments = argumentsPrefix + testSite.AspNetCoreApp.GetArgumentFileName(); + string tempProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK); + var tempBackendProcess = Process.GetProcessById(Convert.ToInt32(tempProcessId)); + + // replace $env with the actual test value + if (processPath == "$env") + { + string tempString = Environment.ExpandEnvironmentVariables("%systemdrive%").ToLower(); + processPath = Path.Combine(tempBackendProcess.MainModule.FileName).ToLower().Replace(tempString, "%systemdrive%"); + arguments = testSite.AspNetCoreApp.GetDirectoryPathWith(arguments).ToLower().Replace(tempString, "%systemdrive%"); + } + + DateTime startTime = DateTime.Now; + Thread.Sleep(500); + + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "processPath", processPath); + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "arguments", arguments); + Thread.Sleep(500); + + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + Thread.Sleep(500); + + string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK); + Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId)); + } + + testSite.AspNetCoreApp.RestoreFile("web.config"); + } + } + + public static async Task DoForwardWindowsAuthTokenTest(IISConfigUtility.AppPoolBitness appPoolBitness, bool enabledForwardWindowsAuthToken) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoForwardWindowsAuthTokenTest")) + { + using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + { + string result = string.Empty; + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "forwardWindowsAuthToken", enabledForwardWindowsAuthToken); + string requestHeaders = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("DumpRequestHeaders"), HttpStatusCode.OK); + Assert.False(requestHeaders.ToUpper().Contains("MS-ASPNETCORE-WINAUTHTOKEN")); + + iisConfig.EnableWindowsAuthentication(testSite.SiteName); + + Thread.Sleep(500); + + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + Thread.Sleep(500); + + requestHeaders = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("DumpRequestHeaders"), HttpStatusCode.OK); + if (enabledForwardWindowsAuthToken) + { + string expectedHeaderName = "MS-ASPNETCORE-WINAUTHTOKEN"; + Assert.True(requestHeaders.ToUpper().Contains(expectedHeaderName)); + + result = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("ImpersonateMiddleware"), HttpStatusCode.OK); + bool compare = false; + + string expectedValue1 = "ImpersonateMiddleware-UserName = " + Environment.ExpandEnvironmentVariables("%USERDOMAIN%") + "\\" + Environment.ExpandEnvironmentVariables("%USERNAME%"); + if (result.ToLower().Contains(expectedValue1.ToLower())) + { + compare = true; + } + + string expectedValue2 = "ImpersonateMiddleware-UserName = " + Environment.ExpandEnvironmentVariables("%USERNAME%"); + if (result.ToLower().Contains(expectedValue2.ToLower())) + { + compare = true; + } + + Assert.True(compare); + } + else + { + Assert.False(requestHeaders.ToUpper().Contains("MS-ASPNETCORE-WINAUTHTOKEN")); + + result = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("ImpersonateMiddleware"), HttpStatusCode.OK); + Assert.True(result.Contains("ImpersonateMiddleware-UserName = NoAuthentication")); + } + } + + testSite.AspNetCoreApp.RestoreFile("web.config"); + } + } + + public static async Task DoRecylingAppPoolTest(IISConfigUtility.AppPoolBitness appPoolBitness) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoRecylingAppPoolTest")) + { + using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + { + + // allocating 1024,000 KB + await VerifyResponseStatus(testSite.AspNetCoreApp.GetHttpUri("MemoryLeak1024000"), HttpStatusCode.OK); + + // get backend process id + string pocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("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)); + var workerProcess = Process.GetProcessById(Convert.ToInt32(processIdOfWorkerProcess)); + var backendProcess = Process.GetProcessById(Convert.ToInt32(pocessIdBackendProcess)); + + var privateMemoryKB = workerProcess.PrivateMemorySize64 / 1024; + var virtualMemoryKB = workerProcess.VirtualMemorySize64 / 1024; + var privateMemoryKBBackend = backendProcess.PrivateMemorySize64 / 1024; + var virtualMemoryKBBackend = backendProcess.VirtualMemorySize64 / 1024; + var totalPrivateMemoryKB = privateMemoryKB + privateMemoryKBBackend; + var totalVirtualMemoryKB = virtualMemoryKB + virtualMemoryKBBackend; + + // terminate backend process + backendProcess.Kill(); + backendProcess.Dispose(); + + // terminate IIS worker process + workerProcess.Kill(); + workerProcess.Dispose(); + Thread.Sleep(3000); + + // check JitDebugger before continuing + 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); + Thread.Sleep(3000); + + await VerifyResponseStatus(testSite.RootAppContext.GetHttpUri("small.htm"), HttpStatusCode.OK); + Thread.Sleep(1000); + int x = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("w3wp.exe", "Handle", userName)); + + // Verify that IIS recycling does not happen while there is no memory leak + bool foundVSJit = false; + for (int i = 0; i < 10; i++) + { + // check JitDebugger before continuing + foundVSJit = TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + + await VerifyResponseStatus(testSite.RootAppContext.GetHttpUri("small.htm"), HttpStatusCode.OK); + Thread.Sleep(3000); + } + + int y = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("w3wp.exe", "Handle", userName)); + Assert.True(x == y && foundVSJit == false, "worker process is not recycled after 30 seconds"); + + string backupPocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK); + string newPocessIdBackendProcess = backupPocessIdBackendProcess; + + // Verify IIS recycling happens while there is memory leak + for (int i = 0; i < 10; i++) + { + // check JitDebugger before continuing + foundVSJit = TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + + // allocating 2048,000 KB + await VerifyResponseStatus(testSite.AspNetCoreApp.GetHttpUri("MemoryLeak2048000"), HttpStatusCode.OK); + + newPocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK); + if (foundVSJit || backupPocessIdBackendProcess != newPocessIdBackendProcess) + { + // worker process is recycled expectedly and backend process is recycled together + break; + } + Thread.Sleep(3000); + } + // check JitDebugger before continuing + TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); + + int z = 0; + for (int i = 0; i < 10; i++) + { + z = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("w3wp.exe", "Handle", userName)); + if (x != z) + { + break; + } + else + { + Thread.Sleep(1000); + } + } + z = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("w3wp.exe", "Handle", userName)); + Assert.True(x != z, "worker process is recycled"); + + newPocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK); + Assert.True(backupPocessIdBackendProcess != newPocessIdBackendProcess, "backend process is recycled"); + } + testSite.AspNetCoreApp.RestoreFile("web.config"); + } + } + + public static async Task DoCompressionTest(IISConfigUtility.AppPoolBitness appPoolBitness, bool useCompressionMiddleWare, bool enableIISCompression) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoCompressionTest")) + { + using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + { + string startupClass = "StartupCompressionCaching"; + if (!useCompressionMiddleWare) + { + startupClass = "StartupNoCompressionCaching"; + } + + // set startup class + iisConfig.SetANCMConfig( + testSite.SiteName, + testSite.AspNetCoreApp.Name, + "environmentVariable", + new string[] { "ANCMTestStartupClassName", startupClass } + ); + + // enable or IIS compression + // Note: IIS compression, however, will be ignored if AspnetCore compression middleware is enabled. + iisConfig.SetCompression(testSite.SiteName, enableIISCompression); + + // prepare static contents + testSite.AspNetCoreApp.CreateDirectory("wwwroot"); + testSite.AspNetCoreApp.CreateDirectory(@"wwwroot\pdir"); + + testSite.AspNetCoreApp.CreateFile(new string[] { "foohtm" }, @"wwwroot\foo.htm"); + testSite.AspNetCoreApp.CreateFile(new string[] { "barhtm" }, @"wwwroot\pdir\bar.htm"); + testSite.AspNetCoreApp.CreateFile(new string[] { "defaulthtm" }, @"wwwroot\default.htm"); + + string result = string.Empty; + if (!useCompressionMiddleWare && !enableIISCompression) + { + result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK); + Assert.True(result.Contains("foohtm"), "verify response body"); + Assert.False(result.Contains("Content-Encoding"), "verify response header"); + + result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri("pdir/bar.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK); + Assert.True(result.Contains("barhtm"), "verify response body"); + Assert.False(result.Contains("Content-Encoding"), "verify response header"); + + result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri(), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK); + Assert.True(result.Contains("defaulthtm"), "verify response body"); + Assert.False(result.Contains("Content-Encoding"), "verify response header"); + } + else + { + result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK); + Assert.True(result.Contains("foohtm"), "verify response body"); + Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding")); + + result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri("pdir/bar.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK); + Assert.True(result.Contains("barhtm"), "verify response body"); + Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding")); + + result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri(), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK); + Assert.True(result.Contains("defaulthtm"), "verify response body"); + Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding")); + } + } + testSite.AspNetCoreApp.RestoreFile("web.config"); + } + } + + public static async Task DoCachingTest(IISConfigUtility.AppPoolBitness appPoolBitness) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoCachingTest")) + { + using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + { + string startupClass = "StartupCompressionCaching"; + + // set startup class + iisConfig.SetANCMConfig( + testSite.SiteName, + testSite.AspNetCoreApp.Name, + "environmentVariable", + new string[] { "ANCMTestStartupClassName", startupClass } + ); + + // 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"); + + testSite.AspNetCoreApp.CreateFile(new string[] { "foohtm" }, @"wwwroot\foo.htm"); + testSite.AspNetCoreApp.CreateFile(new string[] { "barhtm" }, @"wwwroot\pdir\bar.htm"); + testSite.AspNetCoreApp.CreateFile(new string[] { "defaulthtm" }, @"wwwroot\default.htm"); + + string result = string.Empty; + + result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK); + string headerValue = GetHeaderValue(result, "MyCustomHeader"); + Assert.True(result.Contains("foohtm"), "verify response body"); + Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding")); + Thread.Sleep(2000); + + result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK); + string headerValue2 = GetHeaderValue(result, "MyCustomHeader"); + Assert.True(result.Contains("foohtm"), "verify response body"); + Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding")); + Assert.Equal(headerValue, headerValue2); + + Thread.Sleep(12000); + result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetHttpUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK); + Assert.True(result.Contains("foohtm"), "verify response body"); + Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding")); + string headerValue3 = GetHeaderValue(result, "MyCustomHeader"); + Assert.NotEqual(headerValue2, headerValue3); + } + testSite.AspNetCoreApp.RestoreFile("web.config"); + } + } + + public static async Task DoWebSocketTest(IISConfigUtility.AppPoolBitness appPoolBitness, string testData) + { + using (var testSite = new TestWebSite(appPoolBitness, "DoWebSocketTest")) + { + DateTime startTime = DateTime.Now; + + await VerifyResponseBody(testSite.AspNetCoreApp.GetHttpUri(), "Running", HttpStatusCode.OK); + + // Get Process ID + string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetHttpUri("GetProcessId"), HttpStatusCode.OK); + + // Verify WebSocket without setting subprotocol + await VerifyResponseBodyContain(testSite.WebSocketApp.GetHttpUri("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.GetHttpUri("echoSubProtocol.aspx"), new string[] { "Socket Open", "mywebsocketsubprotocol" }, HttpStatusCode.OK); // echoSubProtocol.aspx has hard coded path for the websocket server + + // Verify process creation ANCM event log + Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId)); + + // Verify websocket + using (WebSocketClientHelper websocketClient = new WebSocketClientHelper()) + { + var frameReturned = websocketClient.Connect(testSite.AspNetCoreApp.GetHttpUri("websocket"), true, true); + Assert.True(frameReturned.Content.Contains("Connection: Upgrade")); + Assert.True(frameReturned.Content.Contains("HTTP/1.1 101 Switching Protocols")); + Thread.Sleep(500); + + VerifySendingWebSocketData(websocketClient, testData); + + Thread.Sleep(500); + frameReturned = websocketClient.Close(); + Assert.True(frameReturned.FrameType == FrameType.Close, "Closing Handshake"); + } + + // send a simple request again and verify the response body + await VerifyResponseBody(testSite.AspNetCoreApp.GetHttpUri(), "Running", HttpStatusCode.OK); + } + } + + private static string GetHeaderValue(string inputData, string headerName) + { + string result = string.Empty; + foreach (string item in inputData.Split(new char[] { ',', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)) + { + if (item.Contains(headerName)) + { + var tokens = item.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries); + if (tokens.Length == 2) + { + result = tokens[1].Trim(); + break; + } + } + } + return result; + } + + private static bool VerifySendingWebSocketData(WebSocketClientHelper websocketClient, string testData) + { + bool result = false; + + // + // send complete or partial text data and ping multiple times + // + websocketClient.SendTextData(testData); + websocketClient.SendPing(); + websocketClient.SendTextData(testData); + websocketClient.SendPing(); + websocketClient.SendPing(); + websocketClient.SendTextData(testData, 0x01); // 0x01: start of sending partial data + websocketClient.SendPing(); + websocketClient.SendTextData(testData, 0x80); // 0x80: end of sending partial data + websocketClient.SendPing(); + websocketClient.SendPing(); + websocketClient.SendTextData(testData); + websocketClient.SendTextData(testData); + websocketClient.SendTextData(testData); + websocketClient.SendPing(); + Thread.Sleep(3000); + + // Verify test result + for (int i = 0; i < 3; i++) + { + if (DoVerifyDataSentAndReceived(websocketClient) == false) + { + // retrying after 1 second sleeping + Thread.Sleep(1000); + } + else + { + result = true; + break; + } + } + return result; + } + + private static bool DoVerifyDataSentAndReceived(WebSocketClientHelper websocketClient) + { + var result = true; + var sentString = new StringBuilder(); + var recString = new StringBuilder(); + var pingString = new StringBuilder(); + var pongString = new StringBuilder(); + + foreach (Frame frame in websocketClient.Connection.DataSent.ToArray()) + { + if (frame.FrameType == FrameType.Continuation + || frame.FrameType == FrameType.SegmentedText + || frame.FrameType == FrameType.Text + || frame.FrameType == FrameType.ContinuationFrameEnd) + { + sentString.Append(frame.Content); + } + + if (frame.FrameType == FrameType.Ping) + { + pingString.Append(frame.Content); + } + } + + foreach (Frame frame in websocketClient.Connection.DataReceived.ToArray()) + { + if (frame.FrameType == FrameType.Continuation + || frame.FrameType == FrameType.SegmentedText + || frame.FrameType == FrameType.Text + || frame.FrameType == FrameType.ContinuationFrameEnd) + { + recString.Append(frame.Content); + } + + if (frame.FrameType == FrameType.Pong) + { + pongString.Append(frame.Content); + } + } + + if (sentString.Length == recString.Length && pongString.Length == pingString.Length) + { + if (sentString.Length != recString.Length) + { + result = false; + TestUtility.LogInformation("Same size of data sent(" + sentString.Length + ") and received(" + recString.Length + ")"); + } + + if (sentString.ToString() != recString.ToString()) + { + result = false; + TestUtility.LogInformation("Not matched string in sent and received"); + } + if (pongString.Length != pingString.Length) + { + result = false; + TestUtility.LogInformation("Ping received; Ping (" + pingString.Length + ") and Pong (" + pongString.Length + ")"); + } + websocketClient.Connection.DataSent.Clear(); + websocketClient.Connection.DataReceived.Clear(); + } + else + { + TestUtility.LogInformation("Retrying... so far data sent(" + sentString.Length + ") and received(" + recString.Length + ")"); + result = false; + } + return result; + } + + private static async Task CheckChunkedAsync(HttpClient client, TestWebApplication webApp) + { + var response = await client.GetAsync(webApp.GetHttpUri("chunked")); + var responseText = await response.Content.ReadAsStringAsync(); + try + { + Assert.Equal("Chunked", responseText); + Assert.True(response.Headers.TransferEncodingChunked, "/chunked, chunked?"); + Assert.Null(response.Headers.ConnectionClose); + Assert.Null(GetContentLength(response)); + } + catch (XunitException) + { + TestUtility.LogInformation(response.ToString()); + TestUtility.LogInformation(responseText); + throw; + } + } + + private static string GetContentLength(HttpResponseMessage response) + { + // Don't use response.Content.Headers.ContentLength, it will dynamically calculate the value if it can. + IEnumerable values; + return response.Content.Headers.TryGetValues(HeaderNames.ContentLength, out values) ? values.FirstOrDefault() : null; + } + + private static bool VerifyANCMStartEvent(DateTime startFrom, string includeThis) + { + return VerifyEventLog(1001, startFrom, includeThis); + } + + private static bool VerifyApplicationEventLog(int eventID, DateTime startFrom, string includeThis) + { + return VerifyEventLog(eventID, startFrom, includeThis); + } + + private static bool VerifyEventLog(int eventId, DateTime startFrom, string includeThis = null) + { + var events = TestUtility.GetApplicationEvent(eventId, startFrom); + Assert.True(events.Count > 0, "Verfiy expected event logs"); + bool findEvent = false; + foreach (string item in events) + { + if (item.Contains(includeThis)) + { + findEvent = true; + break; + } + } + return findEvent; + } + + private static async Task VerifyResponseStatus(Uri uri, HttpStatusCode expectedResponseStatus, int timeout = 5, int numberOfRetryCount = 2, bool verifyResponseFlag = true) + { + await SendReceive(uri, null, null, null, expectedResponseStatus, ReturnValueType.None, numberOfRetryCount, verifyResponseFlag, postData: null, timeout: timeout); + } + + private static async Task VerifyResponseBody(Uri uri, string expectedResponseBody, HttpStatusCode expectedResponseStatus, int timeout = 5, int numberOfRetryCount = 2, bool verifyResponseFlag = true) + { + await SendReceive(uri, null, expectedResponseBody, null, expectedResponseStatus, ReturnValueType.None, numberOfRetryCount, verifyResponseFlag, postData:null, timeout:timeout); + } + + private static async Task VerifyPostResponseBody(Uri uri, KeyValuePair[] postData, string expectedResponseBody, HttpStatusCode expectedResponseStatus, int timeout = 5, int numberOfRetryCount = 2, bool verifyResponseFlag = true) + { + await SendReceive(uri, null, expectedResponseBody, null, expectedResponseStatus, ReturnValueType.None, numberOfRetryCount, verifyResponseFlag, postData, timeout); + } + + private static async Task VerifyResponseBodyContain(Uri uri, string[] expectedStrings, HttpStatusCode expectedResponseStatus, int timeout = 5, int numberOfRetryCount = 2, bool verifyResponseFlag = true) + { + await SendReceive(uri, null, null, expectedStrings, expectedResponseStatus, ReturnValueType.None, numberOfRetryCount, verifyResponseFlag, postData: null, timeout: timeout); + } + + private static async Task GetResponse(Uri uri, HttpStatusCode expectedResponseStatus, ReturnValueType returnValueType = ReturnValueType.ResponseBody, int timeout = 5, int numberOfRetryCount = 1, bool verifyResponseFlag = true) + { + return await SendReceive(uri, null, null, null, expectedResponseStatus, returnValueType, numberOfRetryCount, verifyResponseFlag, postData:null, timeout:timeout); + } + + private static async Task GetResponseAndHeaders(Uri uri, string[] requestHeaders, HttpStatusCode expectedResponseStatus, ReturnValueType returnValueType = ReturnValueType.ResponseBodyAndHeaders, int timeout = 5, int numberOfRetryCount = 1, bool verifyResponseFlag = true) + { + return await SendReceive(uri, requestHeaders, null, null, expectedResponseStatus, returnValueType, numberOfRetryCount, verifyResponseFlag, postData: null, timeout: timeout); + } + + private static async Task GetResponseStatusCode(Uri uri) + { + return await SendReceive(uri, null, null, null, HttpStatusCode.OK, ReturnValueType.ResponseStatus, numberOfRetryCount:1, verifyResponseFlag:false, postData:null, timeout:5); + } + + private static async Task ReadContent(HttpResponseMessage response) + { + bool unZipContent = false; + string result = String.Empty; + + IEnumerable values; + if (response.Headers.TryGetValues("Vary", out values)) + { + unZipContent = true; + } + + if (unZipContent) + { + var inputStream = await response.Content.ReadAsStreamAsync(); + var outputStream = new MemoryStream(); + using (var gzip = new GZipStream(inputStream, CompressionMode.Decompress)) + { + await gzip.CopyToAsync(outputStream); + gzip.Close(); + inputStream.Close(); + outputStream.Position = 0; + using (StreamReader reader = new StreamReader(outputStream, Encoding.UTF8)) + { + result = reader.ReadToEnd(); + outputStream.Close(); + } + } + } + else + { + result = await response.Content.ReadAsStringAsync(); + } + return result; + } + + private static async Task SendReceive(Uri uri, string[] requestHeaders, string expectedResponseBody, string[] expectedStringsInResponseBody, HttpStatusCode expectedResponseStatus, ReturnValueType returnValueType, int numberOfRetryCount, bool verifyResponseFlag, KeyValuePair < string, string>[] postData, int timeout) + { + string result = null; + string responseText = "NotInitialized"; + string responseStatus = "NotInitialized"; + + var httpClientHandler = new HttpClientHandler(); + httpClientHandler.UseDefaultCredentials = true; + + var httpClient = new HttpClient(httpClientHandler) + { + BaseAddress = uri, + Timeout = TimeSpan.FromSeconds(timeout), + }; + + if (requestHeaders != null) + { + for (int i = 0; i < requestHeaders.Length; i=i+2) + { + httpClient.DefaultRequestHeaders.Add(requestHeaders[i], requestHeaders[i+1]); + } + } + + HttpResponseMessage response = null; + try + { + FormUrlEncodedContent postHttpContent = null; + if (postData != null) + { + postHttpContent = new FormUrlEncodedContent(postData); + } + + if (numberOfRetryCount > 1 && expectedResponseStatus == HttpStatusCode.OK) + { + if (postData == null) + { + response = await TestUtility.RetryRequest(() => + { + return httpClient.GetAsync(string.Empty); + }, TestUtility.Logger, retryCount: numberOfRetryCount); + } + else + { + response = await TestUtility.RetryRequest(() => + { + return httpClient.PostAsync(string.Empty, postHttpContent); + }, TestUtility.Logger, retryCount: numberOfRetryCount); + } + } + else + { + if (postData == null) + { + response = await httpClient.GetAsync(string.Empty); + } + else + { + response = await httpClient.PostAsync(string.Empty, postHttpContent); + } + } + + if (response != null) + { + responseStatus = response.StatusCode.ToString(); + if (verifyResponseFlag) + { + if (expectedResponseBody != null) + { + if (responseText == "NotInitialized") + { + responseText = await ReadContent(response); + } + Assert.Equal(expectedResponseBody, responseText); + } + + if (expectedStringsInResponseBody != null) + { + if (responseText == "NotInitialized") + { + responseText = await ReadContent(response); + } + foreach (string item in expectedStringsInResponseBody) + { + Assert.True(responseText.Contains(item)); + } + } + Assert.Equal(expectedResponseStatus, response.StatusCode); + } + + switch (returnValueType) + { + case ReturnValueType.ResponseBody: + case ReturnValueType.ResponseBodyAndHeaders: + if (responseText == "NotInitialized") + { + responseText = await ReadContent(response); + } + result = responseText; + if (returnValueType == ReturnValueType.ResponseBodyAndHeaders) + { + result += ", " + response.ToString(); + } + break; + case ReturnValueType.ResponseStatus: + result = response.StatusCode.ToString(); + break; + } + } + } + catch (XunitException) + { + if (response != null) + { + TestUtility.LogInformation(response.ToString()); + } + TestUtility.LogInformation(responseText); + TestUtility.LogInformation(responseStatus); + throw; + } + return result; + } + } + +} diff --git a/test/AspNetCoreModule.Test/Http.config b/test/AspNetCoreModule.Test/Http.config new file mode 100644 index 0000000000..1518e1800f --- /dev/null +++ b/test/AspNetCoreModule.Test/Http.config @@ -0,0 +1,1032 @@ + + + + + + + +
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/AspNetCoreModule.Test/HttpClientHelper/HttpClientHelper.cs b/test/AspNetCoreModule.Test/HttpClientHelper/HttpClientHelper.cs new file mode 100644 index 0000000000..b776fd130a --- /dev/null +++ b/test/AspNetCoreModule.Test/HttpClientHelper/HttpClientHelper.cs @@ -0,0 +1,336 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using AspNetCoreModule.Test.Framework; +using System; +using System.Net; +using System.IO; +using System.Security.Cryptography.X509Certificates; +using System.Net.Security; +using System.Threading; +using System.Net.Sockets; + +namespace AspNetCoreModule.Test.HttpClientHelper +{ + public class HttpClientHelper + { + private IPHostEntry _host = Dns.GetHostEntry(Dns.GetHostName()); + private string _ipv4Loopback = "127.0.0.1"; + private string _ipv4One = null; + private string _ipv4Two = null; + private string _ipv6Loopback = "[::1]"; + private string _ipv6One = null; + private string _ipv6Two = null; + + public HttpClientHelper() + { + ReadMachineIpAddressInfo(); + + _Ips = new string[] { _ipv4Loopback, _ipv4One, _ipv6Loopback, _ipv6One, _ipv6Two }; + + _Hosts = new string[] { "foo", "bar", "foobar", "barfoo" }; + + _unusedIp = _ipv6Two; + } + + // callback used to validate the certificate in an SSL conversation + public static bool ValidateRemoteCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors policyErrors) + { + return true; + } + + public static int sendRequest(string uri, string hostName, string expectedCN, bool useLegacy, bool displayContent, bool doLogging = true) + { + int status = -1; + + if (doLogging) + { + if (hostName == null) + TestUtility.LogInformation(String.Format("HttpClient::sendRequest() {0} with no hostname", uri)); + else + TestUtility.LogInformation(String.Format("HttpClient::sendRequest() {0} with hostname {1}", uri, hostName)); + } + + ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(ValidateRemoteCertificate); + + if (useLegacy) + { + TestUtility.LogInformation(String.Format("Using SSL3")); + ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3; + } + + HttpWebRequest myRequest; + myRequest = (HttpWebRequest)WebRequest.Create(uri); + myRequest.Proxy = null; + myRequest.KeepAlive = false; + if (hostName != null) + myRequest.Host = hostName; + + ServicePoint point = myRequest.ServicePoint; + point.ConnectionLeaseTimeout = 0; + + try + { + using (HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse()) + { + using (Stream myStream = myResponse.GetResponseStream()) + { + if (displayContent) + { + using (StreamReader myReader = new StreamReader(myStream)) + { + string text = myReader.ReadToEnd(); + TestUtility.LogInformation("\n\n"); + TestUtility.LogInformation(text); + TestUtility.LogInformation("\n\n"); + } + } + } + status = (int)myResponse.StatusCode; + } + } + catch (WebException ex) + { + if ((HttpWebResponse)ex.Response == null) + status = 0; + else + status = (int)((HttpWebResponse)ex.Response).StatusCode; + } + + return status; + } + + public string IPv4Loopback + { + get { return _ipv4Loopback; } + } + public string IPv4One + { + get { return _ipv4One; } + } + public string IPv4Two + { + get { return _ipv4Two; } + } + public string IPv6Loopback + { + get { return _ipv6Loopback; } + } + public string IPv6One + { + get { return _ipv6One; } + } + public string IPv6Two + { + get { return _ipv6Two; } + } + + private string[] _Ips; + + private string[] _Hosts = { "foo", "bar", "foobar", "barfoo" }; + + private string _unusedIp; + + + private Thread _backgroundRequestThread = null; + + public void ReadMachineIpAddressInfo() + { + foreach (IPAddress ip in _host.AddressList) + { + if (IPAddress.IsLoopback(ip)) + continue; + + if (ip.AddressFamily == AddressFamily.InterNetwork) + { + if (_ipv4One == null) + _ipv4One = ip.ToString(); + else if (_ipv4Two == null) + _ipv4Two = ip.ToString(); + } + else if (ip.AddressFamily == AddressFamily.InterNetworkV6) + { + if (!ip.ToString().Contains("%")) + { + if (_ipv6One == null) + _ipv6One = "[" + ip.ToString() + "]"; + else if (_ipv6Two == null) + _ipv6Two = "[" + ip.ToString() + "]"; + } + } + } + } + + public int SendReceiveStatus(string path = "/", string protocol = "http", string ip = "127.0.0.1", int port = 8080, string host = "localhost", int expectedStatus = 200, int retryCount = 0) + { + string uri = protocol + "://" + ip + ":" + port + path; + int status = HttpClientHelper.sendRequest(uri, host, "CN=NULL", false, false); + for (int i = 0; i < retryCount; i++) + { + if (status == expectedStatus) + { + break; + } + DoSleep(1000); + status = HttpClientHelper.sendRequest(uri, host, "CN=NULL", false, false); + } + return status; + } + + public void DoRequest(string uri, string host = null, string expectedCN = "CN=NULL", bool useLegacy = false, bool displayContent = false) + { + HttpClientHelper.sendRequest(uri, host, expectedCN, useLegacy, displayContent); + } + + private void BackgroundRequestLoop(object req) + { + String[] uriHost = (String[])req; + + while (true) + { + HttpClientHelper.sendRequest(uriHost[0], uriHost[1], "CN=NULL", false, false, false); + Thread.Sleep(5000); + } + } + + public void StartBackgroundRequests(string uri, string host = null) + { + if (_backgroundRequestThread != null && _backgroundRequestThread.ThreadState == System.Threading.ThreadState.Running) + _backgroundRequestThread.Abort(); + + if (host == null) + TestUtility.LogInformation(String.Format("########## Starting background requests to {0} with no hostname ##########", uri)); + else + TestUtility.LogInformation(String.Format("########## Starting background requests to {0} with hostname {1} ##########", uri, host)); + + + ParameterizedThreadStart threadStart = new ParameterizedThreadStart(BackgroundRequestLoop); + _backgroundRequestThread = new Thread(threadStart); + _backgroundRequestThread.IsBackground = true; + _backgroundRequestThread.Start(new string[] { uri, host }); + } + + public void StopBackgroundRequests() + { + TestUtility.LogInformation(String.Format("####################### Stopping background requests #######################")); + + if (_backgroundRequestThread != null && _backgroundRequestThread.ThreadState == System.Threading.ThreadState.Running) + _backgroundRequestThread.Abort(); + + _backgroundRequestThread = null; + } + + public void DoSleep(int sleepMs) + { + TestUtility.LogInformation(String.Format("################## Sleeping for {0} ms ##################", sleepMs)); + Thread.Sleep(sleepMs); + } + } + + public class RequestInfo + { + public string ip; + public int port; + public string host; + public int status; + + public RequestInfo(string ipIn, int portIn, string hostIn, int statusIn) + { + ip = ipIn; + port = portIn; + host = hostIn; + status = statusIn; + } + + public string ToUrlRegistration() + { + if ((ip == null || ip == "*") && (host == null || host == "*")) + return String.Format("HTTP://*:{0}/", port).ToUpper(); + + if (ip == null || ip == "*") + return String.Format("HTTP://{0}:{1}/", host, port).ToUpper(); + + if (host == null || host == "*") + return String.Format("HTTP://{0}:{1}:{0}/", ip, port).ToUpper(); + + return String.Format("HTTP://{0}:{1}:{2}/", host, port, ip).ToUpper(); + } + } + + public class BindingInfo + { + public string ip; + public int port; + public string host; + + public BindingInfo(string ip, int port, string host) + { + this.ip = ip; + this.port = port; + this.host = host; + } + + public int GetBindingType() + { + if (ip == null) + { + if (host == null) + return 5; + else + return 3; + } + else + { + if (host == null) + return 4; + else + return 2; + } + } + + public bool IsSupportedForDynamic() + { + return GetBindingType() == 2 || GetBindingType() == 5; + } + + public bool Match(RequestInfo req) + { + if (ip != null && ip != req.ip) + return false; + if (port != req.port) + return false; + if (host != null && host != req.host) + return false; + + return true; + } + + public string ToBindingString() + { + string bindingInfoString = ""; + bindingInfoString += (ip == null) ? "*" : ip; + bindingInfoString += ":"; + bindingInfoString += port; + bindingInfoString += ":"; + if (host != null) + bindingInfoString += host; + + return bindingInfoString; + } + + public string ToUrlRegistration() + { + if ((ip == null || ip == "*") && (host == null || host == "*")) + return String.Format("HTTP://*:{0}/", port).ToUpper(); + + if (ip == null || ip == "*") + return String.Format("HTTP://{0}:{1}/", host, port).ToUpper(); + + if (host == null || host == "*") + return String.Format("HTTP://{0}:{1}:{0}/", ip, port).ToUpper(); + + return String.Format("HTTP://{0}:{1}:{2}/", host, port, ip).ToUpper(); + } + } +} + diff --git a/test/AspNetCoreModule.Test/WebSocketClientHelper/Frame.cs b/test/AspNetCoreModule.Test/WebSocketClientHelper/Frame.cs new file mode 100644 index 0000000000..f4bde38b26 --- /dev/null +++ b/test/AspNetCoreModule.Test/WebSocketClientHelper/Frame.cs @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +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 Frame(byte[] data) + { + Data = data; + FrameType = WebSocketClientUtility.GetFrameType(Data); + Content = WebSocketClientUtility.GetFrameString(Data, out startingIndex, out DataLength); + IsMasked = WebSocketClientUtility.IsFrameMasked(Data); + } + + public FrameType FrameType { get; set; } + public byte[] Data { get; private set; } + public string Content { get; private set; } + public bool IsMasked { get; private set; } + + public int IndexOfNextFrame + { + get + { + if (startingIndex > 0 && Data.Length > Content.Length + startingIndex) + { + return Content.Length + startingIndex; + } + else + { + return -1; + } + } + } + + override public string ToString() + { + return FrameType + ": " + Content; + } + } +} diff --git a/test/AspNetCoreModule.Test/WebSocketClientHelper/FrameType.cs b/test/AspNetCoreModule.Test/WebSocketClientHelper/FrameType.cs new file mode 100644 index 0000000000..c3033ade4b --- /dev/null +++ b/test/AspNetCoreModule.Test/WebSocketClientHelper/FrameType.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace AspNetCoreModule.Test.WebSocketClient +{ + //* %x0 denotes a continuation frame + //* %x1 denotes a text frame + //* %x2 denotes a binary frame + //* %x3-7 are reserved for further non-control frames + //* %x8 denotes a connection close + //* %x9 denotes a ping + //* %xA denotes a pong + public enum FrameType + { + NonControlFrame, + Ping, + Pong, + Text, + SegmentedText, + Binary, + SegmentedBinary, + Continuation, + ContinuationControlled, + ContinuationFrameEnd, + Close, + } +} diff --git a/test/AspNetCoreModule.Test/WebSocketClientHelper/Frames.cs b/test/AspNetCoreModule.Test/WebSocketClientHelper/Frames.cs new file mode 100644 index 0000000000..4f9f78f75f --- /dev/null +++ b/test/AspNetCoreModule.Test/WebSocketClientHelper/Frames.cs @@ -0,0 +1,60 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Text; + +namespace AspNetCoreModule.Test.WebSocketClient +{ + public class Frames + { + public static byte[] CLOSE_FRAME = new byte[] { 0x88, 0x85, 0xBD, 0x60, 0x97, 0x72, 0xBE, 0x88, 0xA5, 0x40, 0x8F }; + public static byte[] PING = new byte[] { 0x89, 0x88, 0,0,0,0, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB }; + public static byte[] PONG = new byte[] { 0x8A, 0x08, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB }; + public static byte[] HELLO = new byte[] { 0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f }; + public static byte[] FRAME_4096 = new byte[] { 0x81, 0xFE, 0x10, 0x00, 0x88, 0x48, 0x9B, 0xE7, 0xDA, 0x0D, 0xD4, 0xB7, 0xCA, 0x0E, 0xD2, 0xA1, 0xD2, 0x0C, 0xD7, 0xB0, 0xCB, 0x1A, 0xC9, 0xB4, 0xC0, 0x1B, 0xD8, 0xA8, 0xC5, 0x05, 0xDC, 0xBF, 0xC7, 0x19, 0xC2, 0xAD, 0xD0, 0x1B, 0xC9, 0xA8, 0xDF, 0x0E, 0xDF, 0xB4, 0xD8, 0x12, 0xDF, 0xBD, 0xC6, 0x04, 0xCD, 0xB5, 0xD9, 0x05, 0xDA, 0xBD, 0xC5, 0x1B, 0xD9, 0xBF, 0xCD, 0x1F, 0xDF, 0xA2, 0xD0, 0x1A, 0xDA, 0xAC, 0xC0, 0x12, 0xD2, 0xA0, 0xC4, 0x10, 0xDA, 0xB0, 0xC7, 0x11, 0xD8, 0xAE, 0xD8, 0x0B, 0xD9, 0xA5, 0xDA, 0x18, 0xD3, 0xA4, 0xD8, 0x12, 0xCC, 0xA1, 0xDA, 0x1D, 0xD0, 0xA6, 0xCB, 0x11, 0xCE, 0xBE, 0xD2, 0x1B, 0xC9, 0xA9, 0xCB, 0x07, 0xDD, 0xB3, 0xC5, 0x0D, 0xD7, 0xA9, 0xD8, 0x1C, 0xD2, 0xA5, 0xDD, 0x0B, 0xD5, 0xAE, 0xC3, 0x11, 0xCD, 0xAE, 0xCB, 0x0C, 0xCB, 0xB5, 0xC5, 0x12, 0xCE, 0xB7, 0xCD, 0x0D, 0xD2, 0xB0, 0xC3, 0x06, 0xC2, 0xA3, 0xC3, 0x06, 0xCB, 0xAB, 0xC6, 0x02, 0xCB, 0xBE, 0xC4, 0x01, 0xDD, 0xBD, 0xC4, 0x05, 0xD5, 0xA5, 0xDF, 0x02, 0xD7, 0xBD, 0xD1, 0x07, 0xDC, 0xAA, 0xC3, 0x1E, 0xD1, 0xAB, 0xC3, 0x04, 0xCA, 0xAF, 0xCD, 0x02, 0xC2, 0xB0, 0xC1, 0x03, 0xCE, 0xB4, 0xC7, 0x1A, 0xDD, 0xA3, 0xDD, 0x1D, 0xDE, 0xB5, 0xD9, 0x00, 0xDA, 0xA5, 0xCA, 0x12, 0xDE, 0xB1, 0xC0, 0x0C, 0xDA, 0xB4, 0xC9, 0x0D, 0xD5, 0xA6, 0xDB, 0x10, 0xCD, 0xA5, 0xC7, 0x1A, 0xC8, 0xAA, 0xD8, 0x1C, 0xD0, 0xAF, 0xC1, 0x0B, 0xC8, 0xB7, 0xDA, 0x1A, 0xCE, 0xA4, 0xC4, 0x18, 0xDC, 0xA1, 0xCD, 0x0B, 0xCB, 0xA1, 0xC2, 0x0B, 0xC8, 0xAC, 0xCC, 0x0E, 0xD7, 0xB0, 0xD2, 0x0B, 0xDF, 0xBD, 0xD8, 0x07, 0xD6, 0xAF, 0xC7, 0x10, 0xD5, 0xA2, 0xC4, 0x04, 0xD9, 0xAE, 0xC2, 0x03, 0xD4, 0xA3, 0xDA, 0x19, 0xCC, 0xAA, 0xCB, 0x06, 0xD8, 0xA9, 0xCA, 0x09, 0xDF, 0xA3, 0xDA, 0x1E, 0xCA, 0xA8, 0xC7, 0x1E, 0xD5, 0xBF, 0xCC, 0x11, 0xCA, 0xAF, 0xC7, 0x03, 0xCE, 0xBE, 0xCA, 0x02, 0xDA, 0xB4, 0xD9, 0x01, 0xDD, 0xAE, 0xCE, 0x19, 0xCC, 0xA5, 0xC4, 0x12, 0xDC, 0xA9, 0xDC, 0x03, 0xD5, 0xB7, 0xDE, 0x05, 0xCE, 0xA9, 0xD0, 0x1A, 0xDA, 0xB4, 0xD2, 0x18, 0xC3, 0xB5, 0xDC, 0x0D, 0xD6, 0xB4, 0xC9, 0x04, 0xD6, 0xAE, 0xD8, 0x00, 0xD2, 0xBF, 0xD1, 0x05, 0xD7, 0xA2, 0xDA, 0x0B, 0xD4, 0xA2, 0xDF, 0x0D, 0xD8, 0xA8, 0xD8, 0x05, 0xCD, 0xBE, 0xC1, 0x05, 0xD2, 0xB6, 0xDC, 0x0E, 0xD2, 0xA0, 0xC5, 0x07, 0xD5, 0xAE, 0xD0, 0x0E, 0xDA, 0xA9, 0xCD, 0x1F, 0xD4, 0xAB, 0xCF, 0x1F, 0xD9, 0xBF, 0xC9, 0x1B, 0xCE, 0xB7, 0xCA, 0x10, 0xD8, 0xA9, 0xD8, 0x07, 0xDA, 0xA3, 0xD2, 0x1A, 0xDE, 0xB3, 0xCC, 0x0D, 0xC9, 0xA3, 0xD8, 0x0F, 0xDC, 0xB5, 0xCC, 0x18, 0xD0, 0xB3, 0xD1, 0x03, 0xC8, 0xAA, 0xC4, 0x04, 0xCB, 0xA6, 0xC3, 0x1C, 0xDD, 0xB7, 0xC4, 0x09, 0xC8, 0xAD, 0xCD, 0x10, 0xD4, 0xAA, 0xDA, 0x1E, 0xD3, 0xA5, 0xCE, 0x10, 0xD3, 0xB2, 0xC5, 0x0B, 0xD7, 0xAA, 0xC6, 0x02, 0xCB, 0xA1, 0xDE, 0x07, 0xC9, 0xA9, 0xCA, 0x0D, 0xD2, 0xAC, 0xD2, 0x0A, 0xC9, 0xA8, 0xC7, 0x10, 0xD5, 0xA0, 0xCA, 0x10, 0xD9, 0xA4, 0xCB, 0x1A, 0xD3, 0xA8, 0xCC, 0x1E, 0xD4, 0xAF, 0xC1, 0x1C, 0xD9, 0xA5, 0xC4, 0x05, 0xD5, 0xB6, 0xCE, 0x0A, 0xD0, 0xA9, 0xC5, 0x1F, 0xD9, 0xA3, 0xCE, 0x1C, 0xDC, 0xA8, 0xD8, 0x0D, 0xD7, 0xB7, 0xC1, 0x05, 0xD8, 0xA2, 0xC0, 0x0C, 0xD1, 0xA0, 0xD8, 0x09, 0xD8, 0xA0, 0xC9, 0x19, 0xDF, 0xA5, 0xC2, 0x1F, 0xD9, 0xBD, 0xC6, 0x06, 0xCB, 0xA1, 0xD8, 0x0C, 0xD2, 0xAC, 0xC7, 0x12, 0xC9, 0xA3, 0xC0, 0x04, 0xCF, 0xBE, 0xC2, 0x02, 0xD1, 0xA5, 0xDA, 0x0D, 0xC2, 0xAB, 0xDD, 0x1E, 0xDF, 0xB7, 0xD8, 0x0E, 0xDE, 0xB3, 0xCC, 0x04, 0xD8, 0xB2, 0xDF, 0x10, 0xD4, 0xA3, 0xDE, 0x11, 0xC9, 0xB7, 0xC2, 0x11, 0xC2, 0xA8, 0xDF, 0x0D, 0xC9, 0xBD, 0xC6, 0x12, 0xD2, 0xAD, 0xD8, 0x0C, 0xD7, 0xB2, 0xC9, 0x1B, 0xCE, 0xAD, 0xDE, 0x11, 0xDA, 0xB0, 0xC1, 0x11, 0xD9, 0xAA, 0xDE, 0x0E, 0xDC, 0xB3, 0xC5, 0x01, 0xD8, 0xB1, 0xD0, 0x07, 0xCF, 0xAB, 0xC6, 0x0E, 0xDD, 0xA3, 0xCB, 0x1C, 0xDE, 0xB3, 0xC4, 0x1D, 0xDF, 0xA4, 0xCC, 0x00, 0xCE, 0xAC, 0xD1, 0x0A, 0xDD, 0xBF, 0xCB, 0x0E, 0xDE, 0xAF, 0xDB, 0x18, 0xCC, 0xAF, 0xCA, 0x18, 0xCC, 0xAA, 0xD2, 0x02, 0xCC, 0xB6, 0xDB, 0x1F, 0xCF, 0xB7, 0xDD, 0x01, 0xDA, 0xA6, 0xCB, 0x0D, 0xCB, 0xA2, 0xC6, 0x1B, 0xC3, 0xB1, 0xC1, 0x1E, 0xD1, 0xAF, 0xC9, 0x10, 0xD7, 0xA9, 0xD0, 0x10, 0xC8, 0xB0, 0xD0, 0x19, 0xD8, 0xB2, 0xC4, 0x0D, 0xC9, 0xA5, 0xC0, 0x19, 0xDF, 0xB3, 0xCE, 0x0C, 0xDD, 0xA9, 0xD2, 0x1B, 0xCF, 0xAE, 0xDB, 0x0A, 0xDF, 0xA3, 0xD2, 0x07, 0xCB, 0xB1, 0xC3, 0x0F, 0xC8, 0xB0, 0xD9, 0x0D, 0xDF, 0xAB, 0xCA, 0x1E, 0xC8, 0xAD, 0xC9, 0x1E, 0xD5, 0xB5, 0xDB, 0x18, 0xD8, 0xBF, 0xDB, 0x10, 0xD4, 0xA2, 0xCC, 0x02, 0xDF, 0xB1, 0xC6, 0x11, 0xCE, 0xBF, 0xC7, 0x1F, 0xCA, 0xA5, 0xD1, 0x0F, 0xD8, 0xA6, 0xD2, 0x1C, 0xD6, 0xA2, 0xD2, 0x03, 0xD3, 0xBF, 0xC6, 0x04, 0xD7, 0xAD, 0xC4, 0x1B, 0xD8, 0xA9, 0xDA, 0x06, 0xC8, 0xAF, 0xC9, 0x00, 0xC3, 0xA3, 0xC9, 0x1E, 0xCE, 0xA2, 0xCD, 0x05, 0xCF, 0xAB, 0xC6, 0x0A, 0xC3, 0xBE, 0xC4, 0x02, 0xDD, 0xB3, 0xCA, 0x0F, 0xD2, 0xA4, 0xC6, 0x03, 0xD7, 0xB0, 0xDC, 0x0C, 0xD1, 0xAB, 0xC7, 0x1E, 0xDA, 0xB4, 0xDE, 0x01, 0xDE, 0xA2, 0xD9, 0x0C, 0xCF, 0xA5, 0xDB, 0x09, 0xCC, 0xAD, 0xDE, 0x0C, 0xD9, 0xAF, 0xC1, 0x12, 0xDD, 0xB2, 0xD2, 0x1E, 0xCA, 0xB7, 0xC2, 0x10, 0xD0, 0xA6, 0xCB, 0x00, 0xCC, 0xB5, 0xCA, 0x0D, 0xDF, 0xA4, 0xCE, 0x09, 0xDF, 0xBE, 0xC5, 0x00, 0xD1, 0xAA, 0xC7, 0x0B, 0xD9, 0xB6, 0xCB, 0x0A, 0xDF, 0xA8, 0xD8, 0x0F, 0xCF, 0xBD, 0xDB, 0x07, 0xCE, 0xB4, 0xDB, 0x1B, 0xC2, 0xAC, 0xCC, 0x0D, 0xD3, 0xB6, 0xC9, 0x11, 0xD8, 0xAF, 0xDE, 0x00, 0xD3, 0xB5, 0xC5, 0x0C, 0xDA, 0xAF, 0xDF, 0x1D, 0xC2, 0xA6, 0xCD, 0x01, 0xD8, 0xB1, 0xC4, 0x0C, 0xD1, 0xB5, 0xCF, 0x04, 0xDD, 0xB2, 0xC2, 0x11, 0xD1, 0xAD, 0xDD, 0x04, 0xCB, 0xA3, 0xD2, 0x1E, 0xCF, 0xAE, 0xD1, 0x0A, 0xD5, 0xB7, 0xC6, 0x06, 0xCD, 0xBF, 0xDD, 0x10, 0xDC, 0xB1, 0xCB, 0x05, 0xDE, 0xBF, 0xD8, 0x03, 0xD9, 0xAC, 0xCA, 0x06, 0xD2, 0xA9, 0xDD, 0x19, 0xD6, 0xAB, 0xCD, 0x1E, 0xD9, 0xAD, 0xC7, 0x1C, 0xCC, 0xAD, 0xD9, 0x1D, 0xDF, 0xB4, 0xD8, 0x00, 0xC1, 0xAB, 0xDB, 0x06, 0xD3, 0xAF, 0xCF, 0x1B, 0xD4, 0xA8, 0xDD, 0x01, 0xD3, 0xAB, 0xDC, 0x12, 0xCE, 0xB0, 0xC9, 0x03, 0xCF, 0xBD, 0xDF, 0x10, 0xDC, 0xAE, 0xD9, 0x1E, 0xDC, 0xB2, 0xC0, 0x01, 0xCD, 0xB3, 0xC6, 0x10, 0xCD, 0xA0, 0xC2, 0x0E, 0xDE, 0xAA, 0xC0, 0x05, 0xD4, 0xA0, 0xC5, 0x03, 0xCA, 0xB6, 0xD2, 0x0F, 0xC8, 0xA2, 0xC7, 0x09, 0xCB, 0xB1, 0xC0, 0x11, 0xC9, 0xAB, 0xC4, 0x1C, 0xD3, 0xAA, 0xC5, 0x06, 0xC2, 0xB1, 0xCD, 0x07, 0xD5, 0xB1, 0xCE, 0x0F, 0xC8, 0xAC, 0xC1, 0x12, 0xCC, 0xA1, 0xCD, 0x19, 0xCD, 0xA5, 0xD9, 0x19, 0xDE, 0xAA, 0xC0, 0x09, 0xC1, 0xAB, 0xC6, 0x1C, 0xDA, 0xA9, 0xCE, 0x0B, 0xCF, 0xBE, 0xC5, 0x1D, 0xD6, 0xAB, 0xDC, 0x1F, 0xC1, 0xAF, 0xC1, 0x0E, 0xD6, 0xAF, 0xCA, 0x04, 0xDC, 0xB1, 0xD1, 0x0F, 0xCD, 0xB0, 0xC1, 0x05, 0xD4, 0xA3, 0xC7, 0x0A, 0xD3, 0xAB, 0xCF, 0x0D, 0xDD, 0xA0, 0xCE, 0x10, 0xCF, 0xAD, 0xCC, 0x02, 0xD3, 0xB3, 0xDA, 0x1F, 0xDF, 0xA4, 0xC6, 0x1B, 0xD1, 0xA5, 0xC6, 0x0E, 0xCB, 0xBE, 0xCF, 0x10, 0xCA, 0xBD, 0xCF, 0x01, 0xCC, 0xB5, 0xC7, 0x06, 0xD9, 0xA2, 0xC9, 0x0F, 0xD9, 0xA2, 0xDB, 0x10, 0xC9, 0xA8, 0xD1, 0x0B, 0xDD, 0xAA, 0xC1, 0x04, 0xCA, 0xB0, 0xDB, 0x0F, 0xC2, 0xA5, 0xD8, 0x0F, 0xC1, 0xAE, 0xC0, 0x1C, 0xD8, 0xB1, 0xC6, 0x19, 0xDE, 0xA3, 0xDE, 0x12, 0xD8, 0xA0, 0xD9, 0x0D, 0xD1, 0xB7, 0xC6, 0x09, 0xDA, 0xA2, 0xDB, 0x0C, 0xC9, 0xB1, 0xDA, 0x09, 0xC2, 0xAE, 0xC7, 0x09, 0xD4, 0xB3, 0xC0, 0x1B, 0xC3, 0xBE, 0xDD, 0x1F, 0xD9, 0xAF, 0xD0, 0x0A, 0xC9, 0xAE, 0xC1, 0x02, 0xDD, 0xA9, 0xDF, 0x02, 0xD5, 0xA8, 0xCE, 0x1E, 0xCA, 0xA3, 0xCB, 0x00, 0xDF, 0xAA, 0xDA, 0x1E, 0xD4, 0xB2, 0xC3, 0x01, 0xC1, 0xBE, 0xC0, 0x03, 0xCD, 0xB6, 0xD2, 0x1C, 0xDC, 0xB6, 0xC5, 0x01, 0xD5, 0xAF, 0xDD, 0x04, 0xD6, 0xA1, 0xC5, 0x09, 0xD4, 0xAA, 0xCB, 0x1C, 0xCC, 0xA9, 0xDC, 0x07, 0xCA, 0xA4, 0xC6, 0x1F, 0xC2, 0xBD, 0xC3, 0x00, 0xDC, 0xAB, 0xC7, 0x1F, 0xD4, 0xAA, 0xC7, 0x09, 0xCA, 0xB3, 0xDD, 0x1E, 0xC8, 0xA0, 0xC2, 0x01, 0xD3, 0xAC, 0xDD, 0x06, 0xCD, 0xA9, 0xC7, 0x00, 0xC3, 0xAB, 0xCC, 0x0D, 0xDF, 0xB6, 0xC3, 0x06, 0xC3, 0xA9, 0xCD, 0x09, 0xC9, 0xB7, 0xC4, 0x0B, 0xC3, 0xA5, 0xCB, 0x0B, 0xCF, 0xBE, 0xDF, 0x02, 0xC8, 0xA2, 0xC7, 0x07, 0xDD, 0xBF, 0xC4, 0x1B, 0xC3, 0xA0, 0xD2, 0x0B, 0xD1, 0xAC, 0xD0, 0x09, 0xD1, 0xA0, 0xD0, 0x0D, 0xD9, 0xAD, 0xD9, 0x1A, 0xC2, 0xB5, 0xD8, 0x1D, 0xD7, 0xAB, 0xC6, 0x11, 0xCB, 0xB3, 0xC4, 0x11, 0xD9, 0xB0, 0xC0, 0x12, 0xD8, 0xAA, 0xCC, 0x04, 0xCA, 0xAD, 0xDC, 0x05, 0xDE, 0xA4, 0xDC, 0x05, 0xDA, 0xB5, 0xC0, 0x02, 0xD5, 0xB0, 0xD8, 0x06, 0xD3, 0xB5, 0xCF, 0x04, 0xC8, 0xA5, 0xC5, 0x18, 0xC1, 0xBE, 0xD1, 0x06, 0xC2, 0xBE, 0xCB, 0x18, 0xDD, 0xA1, 0xCA, 0x07, 0xC2, 0xA3, 0xD8, 0x02, 0xC8, 0xA6, 0xD0, 0x11, 0xD6, 0xA4, 0xC4, 0x0D, 0xDC, 0xB2, 0xDA, 0x04, 0xDD, 0xB4, 0xDB, 0x18, 0xCC, 0xA3, 0xC6, 0x0F }; + public static byte[] FRAME_5000 = new byte[] { 0x81, 0xFE, 0x13, 0x88, 0x17, 0x84, 0x25, 0xB2, 0x58, 0xC6, 0x71, 0xE0, 0x4E, 0xD5, 0x77, 0xE0, 0x50, 0xD0, 0x69, 0xFA, 0x43, 0xCF, 0x75, 0xFF, 0x5C, 0xCB, 0x60, 0xE1, 0x52, 0xD0, 0x70, 0xFE, 0x5D, 0xCE, 0x71, 0xF3, 0x44, 0xC7, 0x60, 0xF0, 0x58, 0xC8, 0x6D, 0xE8, 0x5E, 0xDE, 0x60, 0xFF, 0x51, 0xCA, 0x6A, 0xF0, 0x44, 0xDE, 0x62, 0xE6, 0x59, 0xC5, 0x67, 0xEB, 0x4E, 0xC1, 0x77, 0xE4, 0x50, 0xCC, 0x6D, 0xE2, 0x40, 0xD5, 0x7C, 0xF6, 0x58, 0xCF, 0x76, 0xFA, 0x54, 0xD4, 0x60, 0xFE, 0x5D, 0xD6, 0x68, 0xE0, 0x52, 0xD0, 0x71, 0xF9, 0x54, 0xDE, 0x6B, 0xE0, 0x55, 0xC3, 0x66, 0xF8, 0x42, 0xC8, 0x71, 0xF3, 0x45, 0xD4, 0x75, 0xFD, 0x58, 0xC8, 0x68, 0xFA, 0x50, 0xDE, 0x77, 0xEA, 0x40, 0xD4, 0x6A, 0xF5, 0x44, 0xC5, 0x74, 0xFC, 0x58, 0xDC, 0x68, 0xEA, 0x53, 0xC3, 0x67, 0xFB, 0x5F, 0xCE, 0x6B, 0xE3, 0x40, 0xC0, 0x71, 0xE6, 0x55, 0xDC, 0x66, 0xE6, 0x50, 0xC8, 0x61, 0xF1, 0x5E, 0xD4, 0x73, 0xFE, 0x45, 0xD2, 0x77, 0xE6, 0x41, 0xC2, 0x68, 0xE7, 0x54, 0xD7, 0x69, 0xFA, 0x5D, 0xC1, 0x7F, 0xEA, 0x5A, 0xC5, 0x67, 0xE6, 0x40, 0xD2, 0x63, 0xE7, 0x4F, 0xDC, 0x62, 0xF6, 0x43, 0xCE, 0x6A, 0xFC, 0x5B, 0xD4, 0x74, 0xFF, 0x45, 0xD0, 0x73, 0xE3, 0x46, 0xDD, 0x74, 0xFB, 0x5A, 0xD2, 0x6F, 0xF1, 0x5B, 0xC3, 0x74, 0xFB, 0x58, 0xC7, 0x75, 0xE5, 0x46, 0xDC, 0x72, 0xEA, 0x4E, 0xCE, 0x67, 0xE6, 0x52, 0xDC, 0x72, 0xE7, 0x59, 0xCA, 0x63, 0xE1, 0x52, 0xCF, 0x66, 0xEA, 0x52, 0xD3, 0x6D, 0xF1, 0x58, 0xC0, 0x77, 0xFC, 0x43, 0xC3, 0x7F, 0xF8, 0x56, 0xD0, 0x73, 0xE7, 0x4F, 0xDD, 0x76, 0xFA, 0x4F, 0xDC, 0x60, 0xFD, 0x4D, 0xCB, 0x6A, 0xEA, 0x56, 0xDC, 0x61, 0xF7, 0x4D, 0xD6, 0x76, 0xE6, 0x47, 0xD0, 0x6C, 0xE7, 0x45, 0xCB, 0x7F, 0xEB, 0x4E, 0xC9, 0x71, 0xE7, 0x44, 0xCF, 0x73, 0xF5, 0x44, 0xD1, 0x64, 0xF5, 0x44, 0xD7, 0x61, 0xE8, 0x58, 0xD1, 0x68, 0xE4, 0x54, 0xD1, 0x6F, 0xFB, 0x56, 0xC7, 0x63, 0xF6, 0x47, 0xDC, 0x74, 0xF8, 0x4F, 0xC2, 0x74, 0xFF, 0x41, 0xD1, 0x63, 0xE3, 0x54, 0xD3, 0x68, 0xF4, 0x46, 0xC9, 0x64, 0xE5, 0x46, 0xCE, 0x63, 0xEA, 0x55, 0xC1, 0x73, 0xF6, 0x54, 0xC8, 0x71, 0xE3, 0x52, 0xD7, 0x77, 0xE7, 0x52, 0xD6, 0x6C, 0xFF, 0x54, 0xD5, 0x60, 0xE7, 0x58, 0xD3, 0x76, 0xF5, 0x5E, 0xC1, 0x77, 0xFD, 0x55, 0xCE, 0x6B, 0xF4, 0x45, 0xD0, 0x6D, 0xE6, 0x5C, 0xC9, 0x6F, 0xF8, 0x55, 0xD4, 0x69, 0xF9, 0x52, 0xD6, 0x67, 0xE8, 0x53, 0xCB, 0x71, 0xE8, 0x52, 0xC8, 0x6C, 0xF4, 0x5B, 0xCB, 0x73, 0xEB, 0x43, 0xC1, 0x75, 0xE4, 0x51, 0xC8, 0x61, 0xF9, 0x5C, 0xD4, 0x66, 0xE2, 0x5F, 0xD1, 0x76, 0xE8, 0x5C, 0xCD, 0x67, 0xE0, 0x53, 0xD7, 0x6E, 0xFF, 0x47, 0xCA, 0x64, 0xF4, 0x5C, 0xC6, 0x6D, 0xE4, 0x45, 0xC8, 0x74, 0xE5, 0x56, 0xD4, 0x63, 0xE6, 0x59, 0xD5, 0x6A, 0xFD, 0x5B, 0xC0, 0x76, 0xF9, 0x44, 0xCD, 0x70, 0xF6, 0x59, 0xC1, 0x73, 0xF3, 0x43, 0xC7, 0x62, 0xE0, 0x5B, 0xDD, 0x7F, 0xFB, 0x5F, 0xCC, 0x7C, 0xE5, 0x52, 0xD2, 0x7F, 0xE4, 0x53, 0xCD, 0x61, 0xFF, 0x53, 0xD3, 0x67, 0xFE, 0x41, 0xD5, 0x68, 0xF1, 0x5F, 0xC1, 0x6D, 0xFF, 0x47, 0xD4, 0x61, 0xEA, 0x5D, 0xCA, 0x6D, 0xE2, 0x46, 0xC3, 0x6D, 0xF7, 0x51, 0xD2, 0x62, 0xEA, 0x5D, 0xDE, 0x7F, 0xF7, 0x55, 0xCD, 0x72, 0xEA, 0x56, 0xD1, 0x72, 0xE7, 0x5B, 0xDC, 0x67, 0xF6, 0x4D, 0xC8, 0x62, 0xFD, 0x44, 0xC6, 0x69, 0xE2, 0x56, 0xCA, 0x72, 0xEA, 0x47, 0xDC, 0x62, 0xE8, 0x5D, 0xD4, 0x71, 0xFA, 0x52, 0xC0, 0x69, 0xFA, 0x43, 0xC2, 0x7D, 0xE2, 0x45, 0xCA, 0x60, 0xE1, 0x51, 0xC0, 0x60, 0xE6, 0x47, 0xD7, 0x60, 0xFA, 0x59, 0xCE, 0x60, 0xFC, 0x5A, 0xDE, 0x6C, 0xF6, 0x58, 0xDC, 0x6E, 0xE5, 0x52, 0xD0, 0x7C, 0xE5, 0x4D, 0xDE, 0x73, 0xFF, 0x52, 0xD3, 0x7C, 0xFC, 0x5D, 0xC0, 0x77, 0xFE, 0x44, 0xC9, 0x6F, 0xE0, 0x5C, 0xC8, 0x71, 0xE4, 0x4E, 0xDD, 0x73, 0xE6, 0x4F, 0xD1, 0x64, 0xE7, 0x54, 0xCC, 0x6A, 0xFE, 0x51, 0xCD, 0x71, 0xE3, 0x4F, 0xD6, 0x61, 0xE0, 0x5B, 0xD5, 0x61, 0xFB, 0x5F, 0xDC, 0x6E, 0xF0, 0x59, 0xD0, 0x69, 0xE6, 0x4D, 0xC0, 0x7D, 0xF0, 0x53, 0xC6, 0x75, 0xF9, 0x41, 0xC1, 0x69, 0xF0, 0x47, 0xC2, 0x62, 0xF8, 0x44, 0xD0, 0x70, 0xE6, 0x5E, 0xC7, 0x6F, 0xFB, 0x42, 0xC9, 0x69, 0xF3, 0x5D, 0xDE, 0x62, 0xFB, 0x40, 0xD2, 0x68, 0xF1, 0x5B, 0xD7, 0x68, 0xE4, 0x55, 0xD0, 0x73, 0xFA, 0x52, 0xC7, 0x71, 0xF1, 0x45, 0xC5, 0x6C, 0xE7, 0x4D, 0xD7, 0x6E, 0xE5, 0x43, 0xCB, 0x62, 0xE0, 0x46, 0xD4, 0x64, 0xE5, 0x4F, 0xC7, 0x60, 0xE6, 0x43, 0xC0, 0x7C, 0xF3, 0x50, 0xDD, 0x77, 0xE2, 0x5F, 0xC7, 0x61, 0xE1, 0x44, 0xCE, 0x6F, 0xFB, 0x46, 0xC9, 0x6F, 0xF7, 0x5C, 0xD4, 0x6C, 0xE5, 0x5A, 0xD1, 0x60, 0xFF, 0x44, 0xDD, 0x6F, 0xF1, 0x4F, 0xDE, 0x6C, 0xFC, 0x54, 0xCD, 0x6B, 0xF3, 0x56, 0xD2, 0x75, 0xE3, 0x5B, 0xCA, 0x7F, 0xFA, 0x50, 0xD7, 0x62, 0xF9, 0x43, 0xC5, 0x6F, 0xF6, 0x41, 0xC7, 0x6B, 0xFE, 0x43, 0xC2, 0x7D, 0xFB, 0x43, 0xC6, 0x73, 0xE1, 0x56, 0xD2, 0x62, 0xFA, 0x4D, 0xCE, 0x61, 0xE2, 0x56, 0xD6, 0x6E, 0xEB, 0x41, 0xDC, 0x63, 0xF3, 0x45, 0xDE, 0x6C, 0xE5, 0x46, 0xC2, 0x77, 0xF3, 0x41, 0xC6, 0x62, 0xE4, 0x4E, 0xC3, 0x7D, 0xF9, 0x44, 0xC3, 0x6D, 0xF9, 0x5B, 0xDE, 0x6E, 0xF8, 0x4F, 0xD1, 0x66, 0xF6, 0x45, 0xD4, 0x74, 0xE5, 0x4D, 0xD3, 0x74, 0xE6, 0x44, 0xDD, 0x66, 0xE7, 0x52, 0xC2, 0x68, 0xEB, 0x53, 0xCC, 0x74, 0xE7, 0x42, 0xC5, 0x63, 0xE2, 0x46, 0xD2, 0x6A, 0xE1, 0x58, 0xDE, 0x7F, 0xE5, 0x54, 0xCB, 0x6F, 0xF4, 0x5B, 0xCE, 0x72, 0xF0, 0x47, 0xC1, 0x77, 0xE6, 0x52, 0xC9, 0x62, 0xF4, 0x5A, 0xC9, 0x62, 0xE2, 0x53, 0xCD, 0x6F, 0xE3, 0x5D, 0xC5, 0x63, 0xF7, 0x5E, 0xDD, 0x60, 0xE6, 0x4D, 0xC2, 0x76, 0xE3, 0x40, 0xC3, 0x6B, 0xE6, 0x5C, 0xD4, 0x60, 0xE3, 0x5D, 0xC8, 0x6E, 0xF7, 0x58, 0xCD, 0x63, 0xF1, 0x43, 0xCE, 0x71, 0xE7, 0x52, 0xD7, 0x72, 0xF9, 0x52, 0xD7, 0x76, 0xE0, 0x4D, 0xDD, 0x70, 0xEB, 0x42, 0xD5, 0x6F, 0xF5, 0x4D, 0xCA, 0x63, 0xFC, 0x53, 0xD0, 0x6D, 0xEA, 0x46, 0xC6, 0x75, 0xF3, 0x44, 0xC7, 0x7F, 0xE3, 0x5A, 0xDC, 0x69, 0xF6, 0x5C, 0xC7, 0x75, 0xE1, 0x40, 0xCA, 0x74, 0xF9, 0x45, 0xC8, 0x6E, 0xEB, 0x4D, 0xDE, 0x61, 0xF5, 0x52, 0xC2, 0x74, 0xFE, 0x5B, 0xDD, 0x70, 0xF1, 0x53, 0xD7, 0x7C, 0xE5, 0x4E, 0xC1, 0x68, 0xE5, 0x52, 0xC2, 0x73, 0xE5, 0x4F, 0xCA, 0x74, 0xE3, 0x54, 0xD3, 0x62, 0xF7, 0x45, 0xD5, 0x67, 0xE6, 0x4D, 0xD0, 0x68, 0xFA, 0x5F, 0xC5, 0x76, 0xFF, 0x5E, 0xC9, 0x6A, 0xF7, 0x47, 0xD1, 0x69, 0xFF, 0x4D, 0xCA, 0x70, 0xE6, 0x53, 0xC3, 0x6C, 0xE0, 0x58, 0xC5, 0x6F, 0xE2, 0x45, 0xD5, 0x69, 0xFF, 0x45, 0xC1, 0x7D, 0xF7, 0x44, 0xC2, 0x6A, 0xF7, 0x59, 0xCE, 0x6A, 0xFE, 0x4E, 0xC8, 0x64, 0xFA, 0x5B, 0xD0, 0x60, 0xF6, 0x40, 0xCC, 0x74, 0xE1, 0x5B, 0xD1, 0x76, 0xFA, 0x45, 0xC0, 0x70, 0xE0, 0x55, 0xC6, 0x6B, 0xF9, 0x4F, 0xC3, 0x70, 0xE7, 0x4D, 0xD4, 0x62, 0xE6, 0x44, 0xD3, 0x76, 0xF1, 0x4D, 0xC6, 0x61, 0xEA, 0x5B, 0xCC, 0x74, 0xF8, 0x58, 0xC1, 0x71, 0xEA, 0x59, 0xCC, 0x6B, 0xF9, 0x47, 0xD3, 0x6E, 0xE5, 0x4E, 0xD4, 0x6E, 0xF7, 0x4D, 0xCF, 0x60, 0xF5, 0x56, 0xD3, 0x64, 0xE2, 0x54, 0xD4, 0x6C, 0xE2, 0x4D, 0xD3, 0x62, 0xE6, 0x5C, 0xC0, 0x72, 0xE6, 0x59, 0xDC, 0x6D, 0xE0, 0x54, 0xD3, 0x60, 0xE4, 0x5A, 0xD3, 0x60, 0xF8, 0x46, 0xDE, 0x7C, 0xF0, 0x54, 0xCF, 0x6C, 0xE1, 0x53, 0xC0, 0x73, 0xEA, 0x4D, 0xDC, 0x69, 0xE1, 0x46, 0xD5, 0x69, 0xE7, 0x43, 0xD6, 0x74, 0xF1, 0x54, 0xC9, 0x61, 0xF7, 0x45, 0xC2, 0x66, 0xF4, 0x5C, 0xDD, 0x7F, 0xE8, 0x4F, 0xC1, 0x74, 0xF3, 0x41, 0xC0, 0x75, 0xF8, 0x55, 0xCE, 0x76, 0xF7, 0x5B, 0xC8, 0x63, 0xE5, 0x5B, 0xCF, 0x75, 0xE8, 0x5D, 0xD3, 0x7C, 0xE3, 0x5F, 0xC1, 0x64, 0xEB, 0x55, 0xD5, 0x68, 0xF3, 0x4E, 0xC8, 0x70, 0xFE, 0x4D, 0xC8, 0x7F, 0xE3, 0x55, 0xC3, 0x64, 0xE6, 0x42, 0xDD, 0x71, 0xF4, 0x4D, 0xC3, 0x71, 0xF3, 0x5D, 0xDE, 0x74, 0xF0, 0x50, 0xC5, 0x76, 0xE3, 0x53, 0xD6, 0x6C, 0xFE, 0x40, 0xD7, 0x62, 0xE8, 0x45, 0xD0, 0x72, 0xF3, 0x5C, 0xDE, 0x7D, 0xF1, 0x41, 0xC2, 0x72, 0xFB, 0x5B, 0xD1, 0x7C, 0xE7, 0x40, 0xC9, 0x77, 0xEB, 0x41, 0xD0, 0x63, 0xE8, 0x44, 0xCD, 0x68, 0xF9, 0x4E, 0xD4, 0x72, 0xF0, 0x45, 0xD0, 0x6A, 0xF4, 0x5C, 0xD0, 0x75, 0xF8, 0x54, 0xCB, 0x63, 0xF3, 0x52, 0xCF, 0x63, 0xFB, 0x43, 0xCA, 0x75, 0xF9, 0x56, 0xCD, 0x61, 0xEA, 0x58, 0xDC, 0x6C, 0xF1, 0x5A, 0xCA, 0x61, 0xF5, 0x5E, 0xD1, 0x74, 0xE0, 0x50, 0xD7, 0x6E, 0xF6, 0x40, 0xCC, 0x72, 0xFA, 0x59, 0xC1, 0x73, 0xFB, 0x54, 0xC1, 0x74, 0xFE, 0x56, 0xC9, 0x64, 0xF8, 0x46, 0xD5, 0x77, 0xFC, 0x5B, 0xCA, 0x7D, 0xFD, 0x5C, 0xDE, 0x76, 0xF8, 0x43, 0xCB, 0x67, 0xF3, 0x53, 0xC9, 0x6B, 0xE6, 0x5A, 0xD3, 0x6F, 0xF9, 0x54, 0xC5, 0x7F, 0xFA, 0x40, 0xD7, 0x62, 0xE5, 0x43, 0xC3, 0x67, 0xE2, 0x56, 0xDC, 0x77, 0xFB, 0x5C, 0xCC, 0x72, 0xFD, 0x5A, 0xC8, 0x6A, 0xFD, 0x53, 0xD4, 0x6D, 0xFD, 0x4E, 0xCC, 0x7D, 0xE6, 0x50, 0xCC, 0x6E, 0xFE, 0x58, 0xD4, 0x77, 0xE1, 0x44, 0xD3, 0x74, 0xFF, 0x59, 0xC9, 0x64, 0xF4, 0x42, 0xC1, 0x6C, 0xF7, 0x56, 0xD2, 0x7C, 0xE3, 0x5B, 0xCF, 0x60, 0xFA, 0x5C, 0xD7 }; + + public static byte[][] GetHandShakeFrame(string url, int websocketVersion) + { + var address = new Uri(url); + + return new[] + { + Encoding.UTF8.GetBytes("GET " + address.PathAndQuery), + Encoding.UTF8.GetBytes(" HTTP/1.1\r\n"), + Encoding.UTF8.GetBytes(string.Format("Host: {0}:{1}", address.Host, address.Port)), + Encoding.UTF8.GetBytes("\r\nUpgrade: WebSocket\r\n"), + Encoding.UTF8.GetBytes("connection: upgrade\r\n"), + Encoding.UTF8.GetBytes("Sec-WebSocket-Origin: http://localhost:80\r\n"), + Encoding.UTF8.GetBytes("Sec-WebSocket-Version: "+websocketVersion+"\r\n"), + Encoding.UTF8.GetBytes("Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"), + Encoding.UTF8.GetBytes("Sec-WebSocket-Protocol: mywebsocketsubprotocol\r\n"), + Encoding.UTF8.GetBytes("\r"), + Encoding.UTF8.GetBytes("\n") + }; + } + + + public static byte[][] GetHandShakeFrameWithAffinityCookie(string url, int websocketVersion, string AffinityCookie) + { + var address = new Uri(url); + + return new[] + { + Encoding.UTF8.GetBytes("GET " + address.PathAndQuery), + Encoding.UTF8.GetBytes(" HTTP/1.1\r\n"), + Encoding.UTF8.GetBytes(string.Format("Host: {0}:{1}", address.Host, address.Port)), + Encoding.UTF8.GetBytes("\r\nUpgrade: WebSocket\r\n"), + Encoding.UTF8.GetBytes("connection: upgrade\r\n"), + Encoding.UTF8.GetBytes("Sec-WebSocket-Origin: http://localhost:80\r\n"), + Encoding.UTF8.GetBytes("Sec-WebSocket-Version: "+websocketVersion+"\r\n"), + Encoding.UTF8.GetBytes("Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"), + Encoding.UTF8.GetBytes("Cookie: "+AffinityCookie+"\r\n"), + Encoding.UTF8.GetBytes("\r"), + Encoding.UTF8.GetBytes("\n") + }; + } + + } +} diff --git a/test/AspNetCoreModule.Test/WebSocketClientHelper/WebSocketClientHelper.cs b/test/AspNetCoreModule.Test/WebSocketClientHelper/WebSocketClientHelper.cs new file mode 100644 index 0000000000..1410bda6e8 --- /dev/null +++ b/test/AspNetCoreModule.Test/WebSocketClientHelper/WebSocketClientHelper.cs @@ -0,0 +1,370 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using AspNetCoreModule.Test.Framework; +using System; +using System.Collections.Generic; +using System.Text; +using System.Collections; +using System.Threading; +using Xunit; + +namespace AspNetCoreModule.Test.WebSocketClient +{ + public class WebSocketClientHelper : IDisposable + { + public bool IsOpened { get; private set; } + public WebSocketConnect Connection { get; set; } + public bool StoreData { get; set; } + public bool IsAlwaysReading { get; private set; } + public Uri Address { get; set; } + public byte[][] HandShakeRequest { get; set; } + public WebSocketState WebSocketState { get; set; } + + public WebSocketClientHelper() + { + } + + public void Dispose() + { + if (IsOpened) + { + Close(); + } + } + + public Frame Connect(Uri address, bool storeData, bool isAlwaysReading) + { + Address = address; + StoreData = storeData; + + Connection = new WebSocketConnect(); + if (isAlwaysReading) + { + InitiateWithAlwaysReading(); + } + SendWebSocketRequest(WebSocketClientUtility.WebSocketVersion); + Thread.Sleep(3000); + + Frame openingFrame = null; + + if (!IsAlwaysReading) + openingFrame = ReadData(); + 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; + } + + public void Initiate() + { + string host = Address.DnsSafeHost; + int port = Address.Port; + + Connection = new WebSocketConnect(); + TestUtility.LogInformation("Connecting to {0} on {1}", host, port); + + Connection.TcpClient = new MyTcpClient(host, port); + Connection.Stream = Connection.TcpClient.GetStream(); + IsAlwaysReading = false; + + if (StoreData) + { + Connection.DataSent = new List(); + Connection.DataReceived = new List(); + } + } + + public void InitiateWithAlwaysReading() + { + Initiate(); + Connection.Stream.BeginRead(Connection.InputData, 0, Connection.InputData.Length, ReadDataCallback, Connection); + IsAlwaysReading = true; + } + + public void SendWebSocketRequest(int websocketVersion) + { + HandShakeRequest = Frames.GetHandShakeFrame(Address.AbsoluteUri, websocketVersion); + + byte[] outputData = null; + int offset = 0; + while (offset < HandShakeRequest.Length) + { + outputData = HandShakeRequest[offset++]; + + var result = Connection.Stream.BeginWrite(outputData, 0, outputData.Length, WriteCallback, Connection); + + //jhkim debug + //result.AsyncWaitHandle.WaitOne(); + + TestUtility.LogInformation("Client {0:D3}: Write {1} bytes: {2} ", Connection.Id, outputData.Length, + Encoding.UTF8.GetString(outputData, 0, outputData.Length)); + + //result.AsyncWaitHandle.Close(); + } + } + + public void SendWebSocketRequest(int websocketVersion, string AffinityCookie) + { + HandShakeRequest = Frames.GetHandShakeFrameWithAffinityCookie(Address.AbsoluteUri, websocketVersion, AffinityCookie); + + byte[] outputData = null; + int offset = 0; + while (offset < HandShakeRequest.Length) + { + outputData = HandShakeRequest[offset++]; + + Connection.Stream.BeginWrite(outputData, 0, outputData.Length, WriteCallback, Connection); + TestUtility.LogInformation("Client {0:D3}: Write {1} bytes: {2} ", Connection.Id, outputData.Length, + Encoding.UTF8.GetString(outputData, 0, outputData.Length)); + } + } + + public void ReadDataCallback(IAsyncResult result) + { + WebSocketConnect client = (WebSocketConnect) result.AsyncState; + + if (client.IsDisposed) + return; + + int bytesRead = client.Stream.EndRead(result); // wait until the buffer is filled + int bytesReadIntotal = bytesRead; + ArrayList InputDataArray = new ArrayList(); + byte[] tempBuffer = null; + + if (bytesRead > 0) + { + tempBuffer = WebSocketClientUtility.SubArray(Connection.InputData, 0, bytesRead); + + Frame temp = new Frame(tempBuffer); + + // start looping if there is still remaining data + if (tempBuffer.Length < temp.DataLength) + { + if (client.TcpClient.GetStream().DataAvailable) + { + // add the first buffer to the arrayList + InputDataArray.Add(tempBuffer); + + // start looping appending to the arrayList + while (client.TcpClient.GetStream().DataAvailable) + { + bytesRead = client.TcpClient.GetStream().Read(Connection.InputData, 0, Connection.InputData.Length); + tempBuffer = WebSocketClientUtility.SubArray(Connection.InputData, 0, bytesRead); + InputDataArray.Add(tempBuffer); + bytesReadIntotal += bytesRead; + TestUtility.LogInformation("ReadDataCallback: Looping: Client {0:D3}: bytesReadHere {1} ", Connection.Id, bytesRead); + } + + // create a single byte array with the arrayList + tempBuffer = new byte[bytesReadIntotal]; + int arrayIndex = 0; + foreach (byte[] item in InputDataArray.ToArray()) + { + for (int i = 0; i < item.Length; i++) + { + tempBuffer[arrayIndex] = item[i]; + arrayIndex++; + } + } + } + } + + // Create frame with the tempBuffer + Frame frame = new Frame(tempBuffer); + int nextFrameIndex = 0; + while (nextFrameIndex != -1) + { + TestUtility.LogInformation("ReadDataCallback: Client {0:D3}: Read Type {1} : {2} ", Connection.Id, frame.FrameType, bytesReadIntotal); + 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; + + // Start the Async Read to handle the next frame comming from server + client.Stream.BeginRead(client.InputData, 0, client.InputData.Length, ReadDataCallback, client); + } + else + { + client.Dispose(); + } + } + + public Frame ReadData() + { + Frame frame = new Frame(new byte[] { }); + + IAsyncResult result = Connection.Stream.BeginRead(Connection.InputData, 0, Connection.InputData.Length, null, Connection); + + if (result != null) + { + int bytesRead = Connection.Stream.EndRead(result); + if (bytesRead > 0) + { + frame = new Frame(WebSocketClientUtility.SubArray(Connection.InputData, 0, bytesRead)); + + ProcessReceivedData(frame); + + TestUtility.LogInformation("Client {0:D3}: Read Type {1} : {2} ", Connection.Id, frame.FrameType, frame.Content.Length); + } + + } + + return frame; + } + + public void SendTextData(string data) + { + Send(WebSocketClientUtility.GetFramedTextDataInBytes(data)); + } + + public void SendTextData(string data, byte opCode) + { + Send(WebSocketClientUtility.GetFramedTextDataInBytes(data, opCode)); + } + + public void SendHello() + { + Send(Frames.HELLO); + } + + public void SendPing() + { + Send(Frames.PING); + } + + public void SendPong() + { + Send(Frames.PONG); + } + + public void SendPong(Frame receivedPing) + { + var pong = new byte[receivedPing.Data.Length+4]; + for (int i = 1; i < receivedPing.Data.Length; i++) + { + if(i<2) + pong[i] = receivedPing.Data[i]; + else + pong[i+4] = receivedPing.Data[i]; + } + + pong[0] = 0x8A; + pong[1] = (byte)((int)pong[1] | 128); + + Send(pong); + } + + public void CloseConnection() + { + Connection.Done = true; + Send(Frames.CLOSE_FRAME); + } + + public static void WriteCallback(IAsyncResult result) + { + var client = result.AsyncState as WebSocketConnect; + if (client.IsDisposed) + return; + + client.Stream.EndWrite(result); + } + + override public string ToString() + { + return Connection.Id + ": " + WebSocketState.ToString(); + } + + #region Private Methods + + public Frame Send(byte[] outputData) + { + var frame = new Frame(outputData); + ProcessSentData(frame); + if (Connection.TcpClient.Connected) + { + var result = Connection.Stream.BeginWrite(outputData, 0, outputData.Length, WriteCallback, Connection); + TestUtility.LogInformation("Client {0:D3}: Write Type {1} : {2} ", Connection.Id, frame.FrameType, + frame.Content.Length); + } + else + { + TestUtility.LogInformation("Connection is disconnected"); + } + + return frame; + } + + private void ProcessSentData(Frame frame) + { + ProcessData(frame, true); + } + + 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; + ProcessData(frame, false); + } + + private void ProcessData(Frame frame, bool isSentData) + { + if (isSentData && StoreData) + StoreDataSent(frame); + else if (StoreData) + StoreDataReceived(frame); + } + + private void StoreDataReceived(Frame frame) + { + Connection.DataReceived.Add(frame); + Connection.TotalDataReceived += frame.Content.Length; + } + + private void StoreDataSent(Frame frame) + { + Connection.DataSent.Add(frame); + Connection.TotalDataSent += frame.Content.Length; + } + + #endregion + } +} diff --git a/test/AspNetCoreModule.Test/WebSocketClientHelper/WebSocketClientUtility.cs b/test/AspNetCoreModule.Test/WebSocketClientHelper/WebSocketClientUtility.cs new file mode 100644 index 0000000000..7c484af7de --- /dev/null +++ b/test/AspNetCoreModule.Test/WebSocketClientHelper/WebSocketClientUtility.cs @@ -0,0 +1,231 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Text; + +namespace AspNetCoreModule.Test.WebSocketClient +{ + public static class WebSocketClientUtility + { + public static FrameType GetFrameType(byte[] inputData) + { + if(inputData.Length==0) + return FrameType.NonControlFrame; + + byte firstByte = inputData[0]; + + switch (firstByte) + { + case 0x80: + return FrameType.ContinuationFrameEnd; + case 0: + return FrameType.Continuation; + case 0x81: + return FrameType.Text; + case 0x01: + return FrameType.SegmentedText; + case 0x82: + return FrameType.Binary; + case 0x02: + return FrameType.SegmentedBinary; + case 0x88: + return FrameType.Close; + case 0x89: + return FrameType.Ping; + case 0x8A: + return FrameType.Pong; + } + return FrameType.NonControlFrame; + } + + public static string GetFrameString(byte[] inputData) + { + int frameStartingIndex; + int dataLength; + return GetFrameString(inputData, out frameStartingIndex, out dataLength); + } + + public static string GetFrameString(byte[] inputData, out int frameStartingIndex, out int frameDataLength) + { + string content; + + FrameType frameType = GetFrameType(inputData); + int startingIndex = 2; + int dataLength = 0; + + if (frameType != FrameType.NonControlFrame && frameType != FrameType.ContinuationControlled) + { + int frameLength = inputData[1]; + + if (IsFrameMasked(inputData)) + { + frameLength = inputData[1] ^ 128; + + if (frameLength < WebSocketConstants.SMALL_LENGTH_FLAG) + { + startingIndex = 6; + dataLength = inputData[1] ^ 128; + } + else if (frameLength == WebSocketConstants.SMALL_LENGTH_FLAG) + { + startingIndex = 8; + dataLength = (int)GetFrameSize(inputData, 2, 4); + } + else if (frameLength == WebSocketConstants.LARGE_LENGTH_FLAG) + { + startingIndex = 14; + dataLength = (int)GetFrameSize(inputData, 2, 10); + } + } + else + { + if (frameLength < WebSocketConstants.SMALL_LENGTH_FLAG) + { + startingIndex = 2; + dataLength = inputData[1]; + } + else if (frameLength == WebSocketConstants.SMALL_LENGTH_FLAG) + { + startingIndex = 4; + dataLength = (int)GetFrameSize(inputData, 2, 4); + } + else if (frameLength == WebSocketConstants.LARGE_LENGTH_FLAG) + { + startingIndex = 10; + dataLength = (int)GetFrameSize(inputData, 2, 10); + } + } + + content = Encoding.UTF8.GetString(inputData, startingIndex, (inputData.Length - startingIndex < dataLength) ? inputData.Length - startingIndex : dataLength); + } + else + { + startingIndex = 0; + dataLength = 0; + content = Encoding.UTF8.GetString(inputData, 0, inputData.Length); + } + + frameStartingIndex = startingIndex; + frameDataLength = dataLength; + return content; + } + + public static uint GetFrameSize(byte[] inputData, int start, int length) + { + byte[] bytes = SubArray(inputData, 2, length - 2); + + if (BitConverter.IsLittleEndian) + Array.Reverse(bytes); + + if (length > 4) + return BitConverter.ToUInt32(bytes, 0); + else + return BitConverter.ToUInt16(bytes, 0); + } + + public static byte[] GetFramedTextDataInBytes(string data) + { + return GetFramedDataInBytes(0x81, data); + } + public static byte[] GetFramedTextDataInBytes(string data, byte opCode) + { + return GetFramedDataInBytes(opCode, data); + } + + public static byte[] GetFramedBinaryDataInBytes(string data) + { + return GetFramedDataInBytes(0x82, data); + } + + private static byte[] GetFramedDataInBytes(byte dataType, string data) + { + var a = BitConverter.GetBytes(data.Length); + var framelist = GetByteArrayFromNumber(dataType, data.Length); + + + byte[] datalist = Encoding.UTF8.GetBytes(data); + + var frame = JoinTwoArrays(framelist, datalist); + return frame; + } + + + public static byte[] GetByteArrayFromNumber(byte dataType, int number) + { + if (number < 126) + { + return new byte[] {dataType, (byte)(number | 128),0,0,0,0 }; + } + else + { + byte lengthByte = WebSocketConstants.LARGE_LENGTH_BYTE; + int lengthBits = 16; + + if (number < 65536) + { + lengthByte = WebSocketConstants.SMALL_LENGTH_BYTE; + lengthBits = 4; + } + + var framelist = new byte[] { dataType, lengthByte }; + string hexValue = (number).ToString("X"); + hexValue = PrependZeroes(hexValue, lengthBits - hexValue.Length); + + var sizeArray = JoinTwoArrays(StringToByteArray(hexValue), new byte[]{0,0,0,0}); + + return JoinTwoArrays(framelist, sizeArray); + } + } + + public static string PrependZeroes(string hex, int zeroes) + { + for (int i = 0; i < zeroes; i++) + { + hex = "0" + hex; + } + return hex; + } + + public static byte[] StringToByteArray(string hex) + { + return Enumerable.Range(0, hex.Length) + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) + .ToArray(); + } + + public static bool IsFrameMasked(byte[] inputData) + { + bool frameMasked = false; + FrameType frameType = GetFrameType(inputData); + + if (frameType != FrameType.NonControlFrame && inputData[1] > 127) + frameMasked = true; + + return frameMasked; + } + + public static byte[] JoinTwoArrays(byte[] aArray, byte[] bArray) + { + var concat = new byte[aArray.Length + bArray.Length]; + + Buffer.BlockCopy(aArray, 0, concat, 0, aArray.Length); + Buffer.BlockCopy(bArray, 0, concat, aArray.Length, bArray.Length); + + return concat; + + } + + public static T[] SubArray(this T[] data, int index, int length) + { + T[] result = new T[length]; + Array.Copy(data, index, result, 0, length); + return result; + } + + public static string WebSocketUri = null; + public static int WebSocketVersion { get { return 13; } } + } +} \ No newline at end of file diff --git a/test/AspNetCoreModule.Test/WebSocketClientHelper/WebSocketConnect.cs b/test/AspNetCoreModule.Test/WebSocketClientHelper/WebSocketConnect.cs new file mode 100644 index 0000000000..df0e99789b --- /dev/null +++ b/test/AspNetCoreModule.Test/WebSocketClientHelper/WebSocketConnect.cs @@ -0,0 +1,76 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Net.Sockets; +using System.IO; + +namespace AspNetCoreModule.Test.WebSocketClient +{ + public class MyTcpClient : TcpClient + { + public MyTcpClient(string hostname, int port) : base(hostname, port) + { + } + + public bool IsDead { get; set; } + protected override void Dispose(bool disposing) + { + Console.WriteLine("MyClient is disposed"); + IsDead = true; + base.Dispose(disposing); + } + } + + public class WebSocketConnect : IDisposable + { + private static int globalID; + + public WebSocketConnect() + { + Id = ++globalID; + InputData = new byte[10240]; + } + + 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; } + public Stream Stream { get; set; } + + public List DataSent { get; set; } + public long TotalDataSent { get; set; } + public List DataReceived { get; set; } + public long TotalDataReceived { get; set; } + + override public string ToString() + { + return Id+""; + + } + + #region IDisposable Members + + /// + /// Dispose this instance. + /// + public void Dispose() + { + Console.WriteLine("Client object is disposed"); + + IsDisposed = true; + if (Stream != null) + Stream.Close(); + + if (TcpClient != null) + TcpClient.Close(); + } + + #endregion + } +} diff --git a/test/AspNetCoreModule.Test/WebSocketClientHelper/WebSocketConstants.cs b/test/AspNetCoreModule.Test/WebSocketClientHelper/WebSocketConstants.cs new file mode 100644 index 0000000000..413b0a3c69 --- /dev/null +++ b/test/AspNetCoreModule.Test/WebSocketClientHelper/WebSocketConstants.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace AspNetCoreModule.Test.WebSocketClient +{ + public static class WebSocketConstants + { + public static int SMALL_LENGTH_FLAG = 126; + public static int LARGE_LENGTH_FLAG = 127; + + public static byte SMALL_LENGTH_BYTE = 0XFE; + public static byte LARGE_LENGTH_BYTE = 0XFF; + + } +} diff --git a/test/AspNetCoreModule.Test/WebSocketClientHelper/WebSocketState.cs b/test/AspNetCoreModule.Test/WebSocketClientHelper/WebSocketState.cs new file mode 100644 index 0000000000..79a711fa56 --- /dev/null +++ b/test/AspNetCoreModule.Test/WebSocketClientHelper/WebSocketState.cs @@ -0,0 +1,12 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace AspNetCoreModule.Test.WebSocketClient +{ + public enum WebSocketState + { + NonWebSocket, + ConnectionOpen, + ConnectionClosed + } +} diff --git a/test/AspNetCoreModule.Test/app.config b/test/AspNetCoreModule.Test/app.config new file mode 100644 index 0000000000..d36d43e498 --- /dev/null +++ b/test/AspNetCoreModule.Test/app.config @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/AspNetCoreModule.Test/project.json b/test/AspNetCoreModule.Test/project.json new file mode 100644 index 0000000000..4fb9bb1370 --- /dev/null +++ b/test/AspNetCoreModule.Test/project.json @@ -0,0 +1,35 @@ +{ + "buildOptions": { + "warningsAsErrors": true, + "copyToOutput": { + "include": [ + "Http.config" + ] + } + }, + "testRunner": "xunit", + "dependencies": { + "Microsoft.Web.Administration": "7.0.0", + "dotnet-test-xunit": "2.2.0-*", + "Microsoft.AspNetCore.Testing": "1.2.0-*", + "Microsoft.Extensions.Logging": "1.2.0-*", + "Microsoft.Extensions.Logging.Console": "1.2.0-*", + "Microsoft.Extensions.PlatformAbstractions": "1.2.0-*", + "Microsoft.Net.Http.Headers": "1.1.0-*", + "xunit": "2.2.0-*" + }, + "frameworks": { + "net451": { + "frameworkAssemblies": { + "System.Data": "", + "System.IO.Compression.FileSystem": "", + "System.Management": "", + "System.Net.Http": "", + "System.Net.Http.WebRequest": "", + "System.Runtime": "", + "System.ServiceProcess": "", + "System.Xml": "" + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCoreModule.Test/project.json.compileerror.txt b/test/AspNetCoreModule.Test/project.json.compileerror.txt new file mode 100644 index 0000000000..e3833d3d0b --- /dev/null +++ b/test/AspNetCoreModule.Test/project.json.compileerror.txt @@ -0,0 +1,38 @@ +{ + "version": "1.1.0-*", + "dependencies": { + "Microsoft.AspNetCore.WebSockets.Server": "*", + "Microsoft.AspNetCore.Server.IISIntegration": "1.2.0-*", + "Microsoft.AspNetCore.Server.Kestrel": "1.2.0-*", + "Microsoft.AspNetCore.Server.WebListener": "1.2.0-*", + "Microsoft.AspNetCore.WebUtilities": "1.2.0-*", + "Microsoft.Extensions.Configuration.CommandLine": "1.2.0-*", + "Microsoft.Extensions.Configuration.Json": "1.2.0-*", + "Microsoft.Extensions.Logging.Console": "1.2.0-*", + "Microsoft.Net.Http.Headers": "1.2.0-*" + }, + "buildOptions": { + "emitEntryPoint": true + }, + "publishOptions": { + "include": [ + "web.config" + ] + }, + "frameworks": { + "netcoreapp1.1": { + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.1.0-*", + "type": "platform" + } + } + } + }, + "tools": { + "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final" + }, + "scripts": { + "postpublish": "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" + } +} \ No newline at end of file diff --git a/test/AspNetCoreModule.TestSites.Standard/AspnetCoreModule.TestSites.Standard.xproj b/test/AspNetCoreModule.TestSites.Standard/AspnetCoreModule.TestSites.Standard.xproj new file mode 100644 index 0000000000..9ad0d6bf8b --- /dev/null +++ b/test/AspNetCoreModule.TestSites.Standard/AspnetCoreModule.TestSites.Standard.xproj @@ -0,0 +1,18 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 030225d8-4ee8-47e5-b692-2a96b3b51a38 + .\obj + .\bin\ + + + 2.0 + 49212 + + + \ No newline at end of file diff --git a/test/AspNetCoreModule.TestSites.Standard/ImpersonateMiddleware.cs b/test/AspNetCoreModule.TestSites.Standard/ImpersonateMiddleware.cs new file mode 100644 index 0000000000..dd0c68166a --- /dev/null +++ b/test/AspNetCoreModule.TestSites.Standard/ImpersonateMiddleware.cs @@ -0,0 +1,36 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Http; +using System.Security.Principal; +using System.Threading.Tasks; + +namespace AspnetCoreModule.TestSites.Standard +{ + public class ImpersonateMiddleware + { + private readonly RequestDelegate next; + public ImpersonateMiddleware(RequestDelegate next) + { + this.next = next; + } + + public async Task Invoke(HttpContext context) + { + var winIdent = context.User.Identity as WindowsIdentity; + if (winIdent == null) + { + await context.Response.WriteAsync("ImpersonateMiddleware-UserName = NoAuthentication"); + await next.Invoke(context); + } + else + { + await WindowsIdentity.RunImpersonated(winIdent.AccessToken, async () => { + string currentUserName = $"{ WindowsIdentity.GetCurrent().Name}"; + await context.Response.WriteAsync("ImpersonateMiddleware-UserName = " + currentUserName); + await next.Invoke(context); + }); + } + } + } +} diff --git a/test/AspNetCoreModule.TestSites.Standard/Program.cs b/test/AspNetCoreModule.TestSites.Standard/Program.cs new file mode 100644 index 0000000000..2bcffdbb43 --- /dev/null +++ b/test/AspNetCoreModule.TestSites.Standard/Program.cs @@ -0,0 +1,123 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Net.Http.Server; +using System; +using System.IO; +using System.Threading; + +namespace AspnetCoreModule.TestSites.Standard +{ + public static class Program + { + public static void Main(string[] args) + { + var config = new ConfigurationBuilder() + .AddCommandLine(args) + .Build(); + + string startUpClassString = Environment.GetEnvironmentVariable("ANCMTestStartupClassName"); + IWebHostBuilder builder = null; + if (!string.IsNullOrEmpty(startUpClassString)) + { + if (startUpClassString == "StartupCompressionCaching") + { + builder = new WebHostBuilder() + .UseConfiguration(config) + .UseIISIntegration() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseStartup(); + } + else if (startUpClassString == "StartupNoCompressionCaching") + { + StartupCompressionCaching.CompressionMode = false; + builder = new WebHostBuilder() + .UseConfiguration(config) + .UseIISIntegration() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseStartup(); + } + else if (startUpClassString == "StartupHelloWorld") + { + builder = new WebHostBuilder() + .UseConfiguration(config) + .UseIISIntegration() + .UseStartup(); + } + else if (startUpClassString == "StartupNtlmAuthentication") + { + builder = new WebHostBuilder() + .UseConfiguration(config) + .UseIISIntegration() + .UseStartup(); + } + else + { + throw new System.Exception("Invalid startup class name : " + startUpClassString); + } + } + else + { + builder = new WebHostBuilder() + .UseConfiguration(config) + .UseIISIntegration() + .UseStartup(); + } + + string startupDelay = Environment.GetEnvironmentVariable("ANCMTestStartUpDelay"); + if (!string.IsNullOrEmpty(startupDelay)) + { + Startup.SleeptimeWhileStarting = Convert.ToInt32(startupDelay); + } + + if (Startup.SleeptimeWhileStarting != 0) + { + Thread.Sleep(Startup.SleeptimeWhileStarting); + } + + string shutdownDelay = Environment.GetEnvironmentVariable("ANCMTestShutdownDelay"); + if (!string.IsNullOrEmpty(shutdownDelay)) + { + Startup.SleeptimeWhileClosing = Convert.ToInt32(shutdownDelay); + } + + // Switch between Kestrel and WebListener for different tests. Default to Kestrel for normal app execution. + if (string.Equals(builder.GetSetting("server"), "Microsoft.AspNetCore.Server.WebListener", System.StringComparison.Ordinal)) + { + if (string.Equals(builder.GetSetting("environment") ?? + Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"), + "NtlmAuthentication", System.StringComparison.Ordinal)) + { + // Set up NTLM authentication for WebListener as follows. + // For IIS and IISExpress use inetmgr to setup NTLM authentication on the application or + // modify the applicationHost.config to enable NTLM. + builder.UseWebListener(options => + { + options.ListenerSettings.Authentication.AllowAnonymous = true; + options.ListenerSettings.Authentication.Schemes = + AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM; + }); + } + else + { + builder.UseWebListener(); + } + } + else + { + builder.UseKestrel(); + } + + var host = builder.Build(); + + host.Run(); + if (Startup.SleeptimeWhileClosing != 0) + { + Thread.Sleep(Startup.SleeptimeWhileClosing); + } + } + } +} + diff --git a/test/AspNetCoreModule.TestSites.Standard/Properties/launchSettings.json b/test/AspNetCoreModule.TestSites.Standard/Properties/launchSettings.json new file mode 100644 index 0000000000..b4fe3e5a15 --- /dev/null +++ b/test/AspNetCoreModule.TestSites.Standard/Properties/launchSettings.json @@ -0,0 +1,26 @@ +{ + "iisSettings": { + "windowsAuthentication": true, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:39982/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ANCMTestStartupClassName": "StartupCompression", + "ASPNET_ENVIRONMENT": "HelloWorld" + } + }, + "web": { + "commandName": "web", + "environmentVariables": { + "ASPNET_ENVIRONMENT": "HelloWorld" + } + } + } +} \ No newline at end of file diff --git a/test/AspNetCoreModule.TestSites.Standard/Startup.cs b/test/AspNetCoreModule.TestSites.Standard/Startup.cs new file mode 100644 index 0000000000..ab01b39ab8 --- /dev/null +++ b/test/AspNetCoreModule.TestSites.Standard/Startup.cs @@ -0,0 +1,311 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; +using Microsoft.Net.Http.Headers; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net.WebSockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace AspnetCoreModule.TestSites.Standard +{ + public class Startup + { + public static int SleeptimeWhileClosing = 0; + public static int SleeptimeWhileStarting = 0; + public static List MemoryLeakList = new List(); + + public void ConfigureServices(IServiceCollection services) + { + services.Configure(options => { + // Considering the default value of ForwardWindowsAuthentication is true, + // 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(buffer), CancellationToken.None); + while (!result.CloseStatus.HasValue) + { + await webSocket.SendAsync(new ArraySegment(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None); + result = await webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + } + await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); + } + + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + { + loggerFactory.AddConsole(minLevel: LogLevel.Warning); + + app.Map("/websocketSubProtocol", subApp => + { + app.UseWebSockets(new WebSocketOptions + { + ReplaceFeature = true + }); + + subApp.Use(async (context, next) => + { + if (context.WebSockets.IsWebSocketRequest) + { + var webSocket = await context.WebSockets.AcceptWebSocketAsync("mywebsocketsubprotocol"); + await Echo(webSocket); + } + else + { + var wsScheme = context.Request.IsHttps ? "wss" : "ws"; + var wsUrl = $"{wsScheme}://{context.Request.Host.Host}:{context.Request.Host.Port}{context.Request.Path}"; + await context.Response.WriteAsync($"Ready to accept a WebSocket request at: {wsUrl}"); + } + }); + }); + + app.Map("/websocket", subApp => + { + app.UseWebSockets(new WebSocketOptions + { + ReplaceFeature = true + }); + + subApp.Use(async (context, next) => + { + if (context.WebSockets.IsWebSocketRequest) + { + var webSocket = await context.WebSockets.AcceptWebSocketAsync(""); + await Echo(webSocket); + } + else + { + var wsScheme = context.Request.IsHttps ? "wss" : "ws"; + var wsUrl = $"{wsScheme}://{context.Request.Host.Host}:{context.Request.Host.Port}{context.Request.Path}"; + await context.Response.WriteAsync($"Ready to accept a WebSocket request at: {wsUrl}"); + } + }); + }); + + app.Map("/GetProcessId", subApp => + { + subApp.Run(context => + { + var process = Process.GetCurrentProcess(); + return context.Response.WriteAsync(process.Id.ToString()); + }); + }); + + app.Map("/EchoPostData", subApp => + { + subApp.Run(context => + { + string responseBody = string.Empty; + if (string.Equals(context.Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) + { + var form = context.Request.ReadFormAsync().GetAwaiter().GetResult(); + int counter = 0; + foreach (var key in form.Keys) + { + StringValues output; + if (form.TryGetValue(key, out output)) + { + responseBody += key + "="; + foreach (var line in output) + { + responseBody += line; + } + if (++counter < form.Count) + { + responseBody += "&"; + } + } + } + } + else + { + responseBody = "NoAction"; + } + return context.Response.WriteAsync(responseBody); + }); + }); + + app.Map("/contentlength", subApp => + { + subApp.Run(context => + { + context.Response.ContentLength = 14; + return context.Response.WriteAsync("Content Length"); + }); + }); + + app.Map("/connectionclose", subApp => + { + subApp.Run(async context => + { + context.Response.Headers[HeaderNames.Connection] = "close"; + await context.Response.WriteAsync("Connnection Close"); + await context.Response.Body.FlushAsync(); // Bypass IIS write-behind buffering + }); + }); + + app.Map("/notchunked", subApp => + { + subApp.Run(async context => + { + var encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); + //context.Response.ContentLength = encoding.GetByteCount(document); + context.Response.ContentType = "text/html;charset=UTF-8"; + await context.Response.WriteAsync("NotChunked", encoding, context.RequestAborted); + await context.Response.Body.FlushAsync(); // Bypass IIS write-behind buffering + }); + }); + + app.Map("/chunked", subApp => + { + subApp.Run(async context => + { + await context.Response.WriteAsync("Chunked"); + await context.Response.Body.FlushAsync(); // Bypass IIS write-behind buffering + }); + }); + + app.Map("/manuallychunked", subApp => + { + subApp.Run(context => + { + context.Response.Headers[HeaderNames.TransferEncoding] = "chunked"; + return context.Response.WriteAsync("10\r\nManually Chunked\r\n0\r\n\r\n"); + }); + }); + + app.Map("/manuallychunkedandclose", subApp => + { + subApp.Run(context => + { + context.Response.Headers[HeaderNames.Connection] = "close"; + context.Response.Headers[HeaderNames.TransferEncoding] = "chunked"; + return context.Response.WriteAsync("1A\r\nManually Chunked and Close\r\n0\r\n\r\n"); + }); + }); + + app.Map("/ImpersonateMiddleware", subApp => + { + subApp.UseMiddleware(); + subApp.Run(context => + { + return context.Response.WriteAsync(""); + }); + }); + + app.Run(context => + { + string response = "Running"; + string[] paths = context.Request.Path.Value.Split(new char[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries); + foreach (string item in paths) + { + string action = string.Empty; + string parameter = string.Empty; + + action = "DoSleep"; + if (item.StartsWith(action)) + { + /* + Process "DoSleep" command here. + For example, if path contains "DoSleep" such as /DoSleep1000, run Thread.Sleep(1000) + */ + int sleepTime = 1000; + if (item.Length > action.Length) + { + parameter = item.Substring(action.Length); + sleepTime = Convert.ToInt32(parameter); + } + Thread.Sleep(sleepTime); + } + + action = "MemoryLeak"; + if (item.StartsWith(action)) + { + parameter = "1024"; + if (item.Length > action.Length) + { + parameter = item.Substring(action.Length); + } + long size = Convert.ToInt32(parameter); + var rnd = new Random(); + byte[] b = new byte[size*1024]; + b[rnd.Next(0, b.Length)] = byte.MaxValue; + MemoryLeakList.Add(b); + response = "MemoryLeak, size:" + size.ToString() + " KB, total: " + MemoryLeakList.Count.ToString(); + } + + action = "ExpandEnvironmentVariables"; + if (item.StartsWith(action)) + { + if (item.Length > action.Length) + { + parameter = item.Substring(action.Length); + response = Environment.ExpandEnvironmentVariables("%" + parameter + "%"); + } + } + + action = "GetEnvironmentVariables"; + if (item.StartsWith(action)) + { + parameter = item.Substring(action.Length); + response = Environment.GetEnvironmentVariables().Count.ToString(); + } + + action = "DumpEnvironmentVariables"; + if (item.StartsWith(action)) + { + response = String.Empty; + + foreach (DictionaryEntry de in Environment.GetEnvironmentVariables()) + { + response += de.Key + ":" + de.Value + "
"; + } + } + + action = "GetRequestHeaderValue"; + if (item.StartsWith(action)) + { + if (item.Length > action.Length) + { + parameter = item.Substring(action.Length); + + if (context.Request.Headers.ContainsKey(parameter)) + { + response = context.Request.Headers[parameter]; + } + else + { + response = ""; + } + } + } + + action = "DumpRequestHeaders"; + if (item.StartsWith(action)) + { + response = String.Empty; + + foreach (var de in context.Request.Headers) + { + response += de.Key + ":" + de.Value + "
"; + } + } + } + return context.Response.WriteAsync(response); + }); + } + } +} diff --git a/test/AspNetCoreModule.TestSites.Standard/StartupCompressionCaching.cs b/test/AspNetCoreModule.TestSites.Standard/StartupCompressionCaching.cs new file mode 100644 index 0000000000..d7db198bab --- /dev/null +++ b/test/AspNetCoreModule.TestSites.Standard/StartupCompressionCaching.cs @@ -0,0 +1,61 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; +using Microsoft.Net.Http.Headers; +using System; + +namespace AspnetCoreModule.TestSites.Standard +{ + public class StartupCompressionCaching + { + public static bool CompressionMode = true; + + public void ConfigureServices(IServiceCollection services) + { + if (CompressionMode) + { + services.AddResponseCompression(); + } + services.AddResponseCaching(); + } + + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + { + if (CompressionMode) + { + app.UseResponseCompression(); + } + app.UseResponseCaching(); + app.UseDefaultFiles(); + app.UseStaticFiles( + new StaticFileOptions() + { + OnPrepareResponse = context => + { + // + // FYI, below line can be simplified with + // context.Context.Response.Headers[HeaderNames.CacheControl] = "public,max-age=10"; + // + context.Context.Response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue() + { + Public = true, + MaxAge = TimeSpan.FromSeconds(10) + }; + context.Context.Response.Headers.Append("MyCustomHeader", DateTime.Now.Second.ToString()); + var accept = context.Context.Request.Headers[HeaderNames.AcceptEncoding]; + if (!StringValues.IsNullOrEmpty(accept)) + { + context.Context.Response.Headers.Append(HeaderNames.Vary, HeaderNames.AcceptEncoding); + } + context.Context.Response.ContentType = "text/plain"; + } + } + ); + } + } +} diff --git a/test/AspNetCoreModule.TestSites.Standard/StartupHelloWorld.cs b/test/AspNetCoreModule.TestSites.Standard/StartupHelloWorld.cs new file mode 100644 index 0000000000..6e64632365 --- /dev/null +++ b/test/AspNetCoreModule.TestSites.Standard/StartupHelloWorld.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; + +namespace AspnetCoreModule.TestSites.Standard +{ + public class StartupHelloWorld + { + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + { + loggerFactory.AddConsole(minLevel: LogLevel.Warning); + + app.Run(ctx => + { + return ctx.Response.WriteAsync("Hello World"); + }); + } + } +} \ No newline at end of file diff --git a/test/AspNetCoreModule.TestSites.Standard/StartupNtlmAuthentication.cs b/test/AspNetCoreModule.TestSites.Standard/StartupNtlmAuthentication.cs new file mode 100644 index 0000000000..034b27e1f8 --- /dev/null +++ b/test/AspNetCoreModule.TestSites.Standard/StartupNtlmAuthentication.cs @@ -0,0 +1,92 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; + +namespace AspnetCoreModule.TestSites.Standard +{ + public class StartupNtlmAuthentication + { + public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) + { + loggerFactory.AddConsole(minLevel: LogLevel.Warning); + + app.Use(async (context, next) => + { + try + { + await next(); + } + catch (Exception ex) + { + if (context.Response.HasStarted) + { + throw; + } + context.Response.Clear(); + context.Response.StatusCode = 500; + await context.Response.WriteAsync(ex.ToString()); + } + }); + + app.Use((context, next) => + { + if (context.Request.Path.Equals("/Anonymous")) + { + return context.Response.WriteAsync("Anonymous?" + !context.User.Identity.IsAuthenticated); + } + + if (context.Request.Path.Equals("/Restricted")) + { + if (context.User.Identity.IsAuthenticated) + { + return context.Response.WriteAsync(context.User.Identity.AuthenticationType); + } + else + { + return context.Authentication.ChallengeAsync(); + } + } + + if (context.Request.Path.Equals("/Forbidden")) + { + return context.Authentication.ForbidAsync(Microsoft.AspNetCore.Http.Authentication.AuthenticationManager.AutomaticScheme); + } + + if (context.Request.Path.Equals("/AutoForbid")) + { + return context.Authentication.ChallengeAsync(); + } + + if (context.Request.Path.Equals("/RestrictedNegotiate")) + { + if (string.Equals("Negotiate", context.User.Identity.AuthenticationType, System.StringComparison.Ordinal)) + { + return context.Response.WriteAsync("Negotiate"); + } + else + { + return context.Authentication.ChallengeAsync("Negotiate"); + } + } + + if (context.Request.Path.Equals("/RestrictedNTLM")) + { + if (string.Equals("NTLM", context.User.Identity.AuthenticationType, System.StringComparison.Ordinal)) + { + return context.Response.WriteAsync("NTLM"); + } + else + { + return context.Authentication.ChallengeAsync("NTLM"); + } + } + + return context.Response.WriteAsync("Running NTLM"); + }); + } + } +} \ No newline at end of file diff --git a/test/AspNetCoreModule.TestSites.Standard/project.json b/test/AspNetCoreModule.TestSites.Standard/project.json new file mode 100644 index 0000000000..cf5023ba58 --- /dev/null +++ b/test/AspNetCoreModule.TestSites.Standard/project.json @@ -0,0 +1,46 @@ +{ + "dependencies": { + "Microsoft.AspNetCore.WebSockets.Server": "0.1.0-rc2-final", + "Microsoft.AspNetCore.Diagnostics": "1.0.1-*", + "Microsoft.AspNetCore.Server.IISIntegration": "1.0.1-*", + "Microsoft.AspNetCore.Server.Kestrel": "1.0.1-*", + "Microsoft.AspNetCore.Server.WebListener": "1.0.1-*", + "Microsoft.AspNetCore.Server.Kestrel.Https": "1.0.1-*", + "Microsoft.AspNetCore.ResponseCompression": "*", + "Microsoft.AspNetCore.ResponseCaching": "*", + "Microsoft.AspNetCore.StaticFiles": "*", + "Microsoft.Extensions.Configuration.CommandLine": "1.0.1-*", + "Microsoft.Extensions.Logging.Console": "1.0.1-*" + }, + + "buildOptions": { + "emitEntryPoint": true, + "copyToOutput": [ + ] + }, + + "publishOptions": { + "include": [ + "web.config" + ] + }, + + "frameworks": { + "netcoreapp1.1": { + "dependencies": { + "Microsoft.NETCore.App": { + "version": "1.0.1-*", + "type": "platform" + } + } + } + }, + + "tools": { + "Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final" + }, + + "scripts": { + "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ] + } +} diff --git a/test/AspNetCoreModule.TestSites.Standard/web.config b/test/AspNetCoreModule.TestSites.Standard/web.config new file mode 100644 index 0000000000..21a7c68e2d --- /dev/null +++ b/test/AspNetCoreModule.TestSites.Standard/web.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/test/WebRoot/ErrorHandling_NotExisting/web.config b/test/WebRoot/ErrorHandling_NotExisting/web.config new file mode 100644 index 0000000000..4004a72fcc --- /dev/null +++ b/test/WebRoot/ErrorHandling_NotExisting/web.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/test/WebRoot/URLRewrite/article.aspx b/test/WebRoot/URLRewrite/article.aspx new file mode 100644 index 0000000000..a7356985d9 --- /dev/null +++ b/test/WebRoot/URLRewrite/article.aspx @@ -0,0 +1,25 @@ +<%@ Page Language="C#" %> + + + + +URL Rewrite Module Test + + +

URL Rewrite Module Test Page

+ + + + + + + + + + + + + +
Server VariableValue
Original URL: <%= Request.ServerVariables["HTTP_X_ORIGINAL_URL"] %>
Final URL: <%= Request.ServerVariables["SCRIPT_NAME"] + "?" + Request.ServerVariables["QUERY_STRING"] %>
+ + \ No newline at end of file diff --git a/test/WebRoot/URLRewrite/default.htm b/test/WebRoot/URLRewrite/default.htm new file mode 100644 index 0000000000..082904a8e1 --- /dev/null +++ b/test/WebRoot/URLRewrite/default.htm @@ -0,0 +1,11 @@ + + + + +IIS Windows + + + +URLRewrite + + \ No newline at end of file diff --git a/test/WebRoot/URLRewrite/web.config b/test/WebRoot/URLRewrite/web.config new file mode 100644 index 0000000000..79d3fa79ff --- /dev/null +++ b/test/WebRoot/URLRewrite/web.config @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/WebRoot/URLRewrite/web.config.bak b/test/WebRoot/URLRewrite/web.config.bak new file mode 100644 index 0000000000..7e914efb5c --- /dev/null +++ b/test/WebRoot/URLRewrite/web.config.bak @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/WebRoot/WebSite1/bkg-blu.jpg b/test/WebRoot/WebSite1/bkg-blu.jpg new file mode 100644 index 0000000000..4eff83ac8e Binary files /dev/null and b/test/WebRoot/WebSite1/bkg-blu.jpg differ diff --git a/test/WebRoot/WebSite1/iis.png b/test/WebRoot/WebSite1/iis.png new file mode 100644 index 0000000000..29f1c90859 Binary files /dev/null and b/test/WebRoot/WebSite1/iis.png differ diff --git a/test/WebRoot/WebSite1/iisstart.htm b/test/WebRoot/WebSite1/iisstart.htm new file mode 100644 index 0000000000..53fa9d0858 --- /dev/null +++ b/test/WebRoot/WebSite1/iisstart.htm @@ -0,0 +1,32 @@ + + + + +IIS Windows + + + +
+IIS +
+ + \ No newline at end of file diff --git a/test/WebRoot/WebSite1/msweb-brand.png b/test/WebRoot/WebSite1/msweb-brand.png new file mode 100644 index 0000000000..c8d842af18 Binary files /dev/null and b/test/WebRoot/WebSite1/msweb-brand.png differ diff --git a/test/WebRoot/WebSite1/small.htm b/test/WebRoot/WebSite1/small.htm new file mode 100644 index 0000000000..64c3ecd7ab --- /dev/null +++ b/test/WebRoot/WebSite1/small.htm @@ -0,0 +1 @@ +small \ No newline at end of file diff --git a/test/WebRoot/WebSite1/test.asp b/test/WebRoot/WebSite1/test.asp new file mode 100644 index 0000000000..87caddf4e2 --- /dev/null +++ b/test/WebRoot/WebSite1/test.asp @@ -0,0 +1,13 @@ + + + +<% +if request.form("cars") = "volvo" then +Response.write ("Hello " & Request.form("title") & " " & Request.form("FirstName") & " " & Request.form("LastName")) +Response.write ("
How are you today?") +Response.Write("
you have a " & Request.form("cars")) +Response.write("
comment " & Request.form("textarea")) +end if +%> + + diff --git a/test/WebRoot/WebSite1/w-brand.png b/test/WebRoot/WebSite1/w-brand.png new file mode 100644 index 0000000000..3fe9d7832f Binary files /dev/null and b/test/WebRoot/WebSite1/w-brand.png differ diff --git a/test/WebRoot/WebSite1/web.config b/test/WebRoot/WebSite1/web.config new file mode 100644 index 0000000000..07ce3d2015 --- /dev/null +++ b/test/WebRoot/WebSite1/web.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/test/WebRoot/WebSite1/web.config.bak b/test/WebRoot/WebSite1/web.config.bak new file mode 100644 index 0000000000..07ce3d2015 --- /dev/null +++ b/test/WebRoot/WebSite1/web.config.bak @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/test/WebRoot/WebSocket/ChatHandler.ashx b/test/WebRoot/WebSocket/ChatHandler.ashx new file mode 100644 index 0000000000..714fbecac2 --- /dev/null +++ b/test/WebRoot/WebSocket/ChatHandler.ashx @@ -0,0 +1,70 @@ +<%@ WebHandler Language="C#" Class="ChatStartHandler" %> + +using System; +using System.Web; +using System.Net.WebSockets; +using System.Web.WebSockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Net; +using System.Collections.Generic; +using System.Collections.Concurrent; + +public class ChatStartHandler : IHttpHandler +{ + + static int clientCount=0; + + public void ProcessRequest(HttpContext context) + { + + context.AcceptWebSocketRequest(async wsContext => + { + ArraySegment buffer = new ArraySegment(new byte[1024]); + WebSocket socket = wsContext.WebSocket; + ChatList.ActiveChatSessions.TryAdd(clientCount++, socket); + + // set up the loop + while (true) + { + Thread.Sleep(100); + var input = await socket.ReceiveAsync(buffer, CancellationToken.None); + + if (input.CloseStatus != null) + { + await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + break; + } + else + { + + foreach (KeyValuePair kvp in ChatList.ActiveChatSessions) + { + WebSocket ws = kvp.Value; + if (ws.State == WebSocketState.Open) + { + var outputBuffer = new ArraySegment(buffer.Array, 0, input.Count); + await ws.SendAsync(outputBuffer, input.MessageType, input.EndOfMessage, CancellationToken.None); + } + } + } + } + }); + //}, new System.Web.WebSockets.AspNetWebSocketOptions { Subprotocol = "ECHO" }); + } + + public bool IsReusable + { + get + { + return false; + } + } + +} + +public static class ChatList +{ + public static ConcurrentDictionary ActiveChatSessions = new ConcurrentDictionary(); +} diff --git a/test/WebRoot/WebSocket/EchoHandler.ashx b/test/WebRoot/WebSocket/EchoHandler.ashx new file mode 100644 index 0000000000..d3cd0cb77c --- /dev/null +++ b/test/WebRoot/WebSocket/EchoHandler.ashx @@ -0,0 +1,50 @@ +<%@ WebHandler Language="C#" Class="Handler" %> + +using System; +using System.Web; +using System.Net.WebSockets; +using System.Web.WebSockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Net; + +public class Handler : IHttpHandler +{ + public void ProcessRequest(HttpContext context) + { + context.AcceptWebSocketRequest(async wsContext => + { + //ArraySegment buffer = new ArraySegment(new byte[1024*1024]); + ArraySegment buffer = new ArraySegment(new byte[1024]); + WebSocket socket = wsContext.WebSocket; + + // set up the loop + while (true) + { + var input = await socket.ReceiveAsync(buffer, CancellationToken.None); + //Thread.Sleep(100); + + if (input.CloseStatus != null) + { + await socket.CloseAsync(input.CloseStatus.Value, input.CloseStatusDescription, CancellationToken.None); + break; + } + else + { + var outputBuffer = new ArraySegment(buffer.Array, 0, input.Count); + await socket.SendAsync(outputBuffer, input.MessageType, input.EndOfMessage, CancellationToken.None); + } + } + }); + } + + public bool IsReusable + { + get + { + return false; + } + } + +} \ No newline at end of file diff --git a/test/WebRoot/WebSocket/OtherExamples/ChatHandler.ashx b/test/WebRoot/WebSocket/OtherExamples/ChatHandler.ashx new file mode 100644 index 0000000000..99bea256d3 --- /dev/null +++ b/test/WebRoot/WebSocket/OtherExamples/ChatHandler.ashx @@ -0,0 +1,69 @@ +<%@ WebHandler Language="C#" Class="ChatStartHandler" %> + +using System; +using System.Web; +using System.Net.WebSockets; +using System.Web.WebSockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Net; +using System.Collections.Generic; +using System.Collections.Concurrent; + +public class ChatStartHandler : IHttpHandler +{ + + static int clientCount=0; + + public void ProcessRequest(HttpContext context) + { + + context.AcceptWebSocketRequest(async wsContext => + { + ArraySegment buffer = new ArraySegment(new byte[1024]); + WebSocket socket = wsContext.WebSocket; + ChatList.ActiveChatSessions.TryAdd(clientCount++, socket); + + // set up the loop + while (true) + { + var input = await socket.ReceiveAsync(buffer, CancellationToken.None); + + if (input.CloseStatus != null) + { + await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + break; + } + else + { + + foreach (KeyValuePair kvp in ChatList.ActiveChatSessions) + { + WebSocket ws = kvp.Value; + if (ws.State == WebSocketState.Open) + { + var outputBuffer = new ArraySegment(buffer.Array, 0, input.Count); + await ws.SendAsync(outputBuffer, input.MessageType, input.EndOfMessage, CancellationToken.None); + } + } + } + } + }); + //}, new System.Web.WebSockets.AspNetWebSocketOptions { Subprotocol = "ECHO" }); + } + + public bool IsReusable + { + get + { + return false; + } + } + +} + +public static class ChatList +{ + public static ConcurrentDictionary ActiveChatSessions = new ConcurrentDictionary(); +} diff --git a/test/WebRoot/WebSocket/OtherExamples/Default.aspx b/test/WebRoot/WebSocket/OtherExamples/Default.aspx new file mode 100644 index 0000000000..1b6e9f6de0 --- /dev/null +++ b/test/WebRoot/WebSocket/OtherExamples/Default.aspx @@ -0,0 +1,70 @@ +<%@ Page Language="C#" AutoEventWireUp="true" %> + + + + + +

#active websocket connection <%= "foo" %>

+ +

Web Socket Echo Demo (run from Minefield!)

+ + + + + + +

+ + +This text will be sent on the socket: + + + +

+ + + + \ No newline at end of file diff --git a/test/WebRoot/WebSocket/OtherExamples/Default.htm b/test/WebRoot/WebSocket/OtherExamples/Default.htm new file mode 100644 index 0000000000..7c3b07ba22 --- /dev/null +++ b/test/WebRoot/WebSocket/OtherExamples/Default.htm @@ -0,0 +1,70 @@ +<%@ Page Language="C#" AutoEventWireUp="true" %> + + + + + +

#active websocket connection <%= "foo" %>

+ +

Web Socket Echo Demo (run from Minefield!)

+ + + + + + +

+ + +This text will be sent on the socket: + + + +

+ + + + \ No newline at end of file diff --git a/test/WebRoot/WebSocket/OtherExamples/DownloaderHandler.ashx b/test/WebRoot/WebSocket/OtherExamples/DownloaderHandler.ashx new file mode 100644 index 0000000000..b5cf323933 --- /dev/null +++ b/test/WebRoot/WebSocket/OtherExamples/DownloaderHandler.ashx @@ -0,0 +1,112 @@ +<%@ WebHandler Language="C#" Class="Handler" %> + +using System; +using System.Web; +using System.Net.WebSockets; +using System.Web.WebSockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Net; +using System.Collections.Generic; +using System.Linq; + +public class Handler : IHttpHandler +{ + const int BUFFER = 1024*5; + bool StopFalg = false; + + public void ProcessRequest(HttpContext context) + { + if (!context.IsWebSocketRequest) + { + HttpRequest Request = context.Request; + HttpResponse Response = context.Response; + Response.Write("Simple Http"); + return; + } + + context.AcceptWebSocketRequest(async wsContext => + { + + // set up the loop + WebSocket socket = wsContext.WebSocket; + WebSocketMessageType responseType = WebSocketMessageType.Text; + int returnSize = 1024*1024; + + Thread.Sleep(500); + + Task.Run(() => + { + Recieve(socket); + }); + + Thread.Sleep(500); + + while (socket.State == WebSocketState.Open) + { + int bytesLeft = returnSize; + var tempString = string.Empty; + + while (bytesLeft > BUFFER) + { + tempString = RandomString(BUFFER); + bytesLeft -= BUFFER; + await socket.SendAsync(new ArraySegment(Encoding.UTF8.GetBytes(tempString)), responseType, false, CancellationToken.None); + Thread.Sleep(500); + } + + if (bytesLeft <= BUFFER && bytesLeft >= 0) + { + tempString = RandomString(bytesLeft); + await socket.SendAsync(new ArraySegment(Encoding.UTF8.GetBytes(tempString)), responseType, true, CancellationToken.None); + Thread.Sleep(500); + } + + if (StopFalg) break; + } + }); + } + + async Task Recieve(WebSocket webSocket) + { + ArraySegment buffer = new ArraySegment(new byte[1024]); + while (webSocket.State == WebSocketState.Open) + { + WebSocketReceiveResult input = await webSocket.ReceiveAsync(buffer, CancellationToken.None); + if (input.CloseStatus == WebSocketCloseStatus.NormalClosure) + { + StopFalg = true; + await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + break; + } + } + } + + + public bool IsReusable + { + get + { + return false; + } + } + + public string RandomString(int size) + { + if (size < 1) + return string.Empty; + + Random random = new Random((int)DateTime.Now.Ticks); + StringBuilder builder = new StringBuilder(); + char ch; + for (int i = 0; i < size; i++) + { + ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))); + builder.Append(ch); + } + + return builder.ToString(); + } + +} \ No newline at end of file diff --git a/test/WebRoot/WebSocket/OtherExamples/GetWebSocketConnectionCount.ashx b/test/WebRoot/WebSocket/OtherExamples/GetWebSocketConnectionCount.ashx new file mode 100644 index 0000000000..c474915a50 --- /dev/null +++ b/test/WebRoot/WebSocket/OtherExamples/GetWebSocketConnectionCount.ashx @@ -0,0 +1,28 @@ +<%@ WebHandler Language="C#" Class="GetWebSocketConnectionCount" %> + +using System; +using System.Web; + +public class GetWebSocketConnectionCount : IHttpHandler { + + public void ProcessRequest (HttpContext context) { + + System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(@"System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); + object mc = assembly.CreateInstance("System.Web.WebSockets.AspNetWebSocketManager"); + Type t = mc.GetType(); + System.Reflection.BindingFlags bf =System.Reflection.BindingFlags.Static| System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public; + //MethodInfo mi = t.GetProperty + object temp1 = t.GetField("Current", bf).GetValue(null); + object temp2 = t.GetField("_activeSockets", bf).GetValue(temp1); + + context.Response.ContentType = "text/plain"; + context.Response.Write("Active WebSocket Connections="+((System.Collections.Generic.HashSet)temp2).Count); + } + + public bool IsReusable { + get { + return false; + } + } + +} \ No newline at end of file diff --git a/test/WebRoot/WebSocket/OtherExamples/HandleBinaryEcho.ashx b/test/WebRoot/WebSocket/OtherExamples/HandleBinaryEcho.ashx new file mode 100644 index 0000000000..aab07dd02e --- /dev/null +++ b/test/WebRoot/WebSocket/OtherExamples/HandleBinaryEcho.ashx @@ -0,0 +1,166 @@ +<%@ WebHandler Language="C#" Class="Handler" %> + +using System; +using System.Web; +using System.Net.WebSockets; +using System.Web.WebSockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Net; +using System.Collections.Generic; +using System.Linq; + +public class Handler : IHttpHandler +{ + const int BUFFER = 1000 * 1000; + + public void ProcessRequest(HttpContext context) + { + if (!context.IsWebSocketRequest) + { + HttpRequest Request = context.Request; + HttpResponse Response = context.Response; + Response.Write("Simple Http"); + return; + } + + context.AcceptWebSocketRequest(async wsContext => + { + + // set up the loop + WebSocket socket = wsContext.WebSocket; + + ////determine return type + List query = wsContext.SecWebSocketProtocols.ToList(); + + WebSocketMessageType responseType = WebSocketMessageType.Text; + if (query[0].Split('-')[0] == WebSocketMessageType.Binary.ToString()) + { + responseType = WebSocketMessageType.Binary; + } + WebSocketMessageType requestType = WebSocketMessageType.Text; + if (query[0].Split('-')[1] == WebSocketMessageType.Binary.ToString()) + { + requestType = WebSocketMessageType.Binary; + } + int returnSize = Int32.Parse(query[0].Split('-')[2]); + int requestSize = Int32.Parse(query[0].Split('-')[3]); + bool canSend = Boolean.Parse(query[0].Split('-')[4]); + bool canReceive = Boolean.Parse(query[0].Split('-')[5]); + + + + + + if (canSend && !canReceive) + { + await Send(socket, responseType, returnSize); + } + + else if (canReceive && !canSend) + { + await Recieve(socket, requestType); + } + + else if (canReceive && canSend) + { + + Task.Run(() => + { + Recieve(socket, requestType); + }); + + while (socket.State == WebSocketState.Open) + { + int bytesLeft = returnSize; + var tempString = string.Empty; + + while (bytesLeft > BUFFER) + { + tempString = RandomString(BUFFER); + bytesLeft -= BUFFER; + await socket.SendAsync(new ArraySegment(Encoding.UTF8.GetBytes(tempString)), responseType, false, CancellationToken.None); + Thread.Sleep(200); + } + + if (bytesLeft <= BUFFER && bytesLeft >= 0) + { + tempString = RandomString(bytesLeft); + await socket.SendAsync(new ArraySegment(Encoding.UTF8.GetBytes(tempString)), responseType, true, CancellationToken.None); + Thread.Sleep(500); + } + } + } + else + { + + Task.Run(() => + { + Recieve(socket, requestType); + }); + } + }); + } + + async Task Recieve(WebSocket webSocket, WebSocketMessageType messageType) + { + ArraySegment buffer = new ArraySegment(new byte[1024]); + while (webSocket.State == WebSocketState.Open) + { + WebSocketReceiveResult input = await webSocket.ReceiveAsync(buffer, CancellationToken.None); + if (input.CloseStatus == WebSocketCloseStatus.NormalClosure) + { + await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + break; + } + } + } + + async Task Send(WebSocket socket, WebSocketMessageType responseType, int returnSize) + { + while (socket.State == WebSocketState.Open) + { + int bytesLeft = returnSize; + + while (bytesLeft > BUFFER) + { + bytesLeft -= BUFFER; + await socket.SendAsync(new ArraySegment(Encoding.UTF8.GetBytes(RandomString(BUFFER))), responseType, false, CancellationToken.None); + Thread.Sleep(200); + } + + if (bytesLeft <= BUFFER && bytesLeft >= 0) + { + await socket.SendAsync(new ArraySegment(Encoding.UTF8.GetBytes(RandomString(bytesLeft))), responseType, true, CancellationToken.None); + Thread.Sleep(500); + } + } + } + + public bool IsReusable + { + get + { + return false; + } + } + + public string RandomString(int size) + { + if (size < 1) + return string.Empty; + + Random random = new Random((int)DateTime.Now.Ticks); + StringBuilder builder = new StringBuilder(); + char ch; + for (int i = 0; i < size; i++) + { + ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))); + builder.Append(ch); + } + + return builder.ToString(); + } + +} \ No newline at end of file diff --git a/test/WebRoot/WebSocket/OtherExamples/Handler.ashx b/test/WebRoot/WebSocket/OtherExamples/Handler.ashx new file mode 100644 index 0000000000..02f969a3df --- /dev/null +++ b/test/WebRoot/WebSocket/OtherExamples/Handler.ashx @@ -0,0 +1,49 @@ +<%@ WebHandler Language="C#" Class="Handler" %> + +using System; +using System.Web; +using System.Net.WebSockets; +using System.Web.WebSockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Net; + +public class Handler : IHttpHandler +{ + public void ProcessRequest(HttpContext context) + { + context.AcceptWebSocketRequest(async wsContext => + { + ArraySegment buffer = new ArraySegment(new byte[1024*1024]); + WebSocket socket = wsContext.WebSocket; + + // set up the loop + while (true) + { + var input = await socket.ReceiveAsync(buffer, CancellationToken.None); + Thread.Sleep(100); + + if (input.CloseStatus != null) + { + await socket.CloseAsync(input.CloseStatus.Value, input.CloseStatusDescription, CancellationToken.None); + break; + } + else + { + var outputBuffer = new ArraySegment(buffer.Array, 0, input.Count); + await socket.SendAsync(outputBuffer, input.MessageType, input.EndOfMessage, CancellationToken.None); + } + } + }); + } + + public bool IsReusable + { + get + { + return false; + } + } + +} \ No newline at end of file diff --git a/test/WebRoot/WebSocket/OtherExamples/UploadHandler.ashx b/test/WebRoot/WebSocket/OtherExamples/UploadHandler.ashx new file mode 100644 index 0000000000..99d7a90162 --- /dev/null +++ b/test/WebRoot/WebSocket/OtherExamples/UploadHandler.ashx @@ -0,0 +1,49 @@ +<%@ WebHandler Language="C#" Class="Handler" %> + +using System; +using System.Web; +using System.Net.WebSockets; +using System.Web.WebSockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Net; +using System.Collections.Generic; +using System.Linq; + +public class Handler : IHttpHandler +{ + public void ProcessRequest(HttpContext context) + { + + context.AcceptWebSocketRequest(async wsContext => + { + + // set up the loop + WebSocket socket = wsContext.WebSocket; + + ArraySegment buffer = new ArraySegment(new byte[1024]); + + while (socket.State == WebSocketState.Open) + { + WebSocketReceiveResult input = await socket.ReceiveAsync(buffer, CancellationToken.None); + if (input.CloseStatus == WebSocketCloseStatus.NormalClosure) + { + await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + break; + } + } + + }); + } + + + public bool IsReusable + { + get + { + return false; + } + } + +} \ No newline at end of file diff --git a/test/WebRoot/WebSocket/OtherExamples/web.config b/test/WebRoot/WebSocket/OtherExamples/web.config new file mode 100644 index 0000000000..439329d7ec --- /dev/null +++ b/test/WebRoot/WebSocket/OtherExamples/web.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/test/WebRoot/WebSocket/chat.aspx b/test/WebRoot/WebSocket/chat.aspx new file mode 100644 index 0000000000..96448b0b18 --- /dev/null +++ b/test/WebRoot/WebSocket/chat.aspx @@ -0,0 +1,62 @@ +<%@ Page Language="C#" AutoEventWireUp="true" %> + + +

#active websocket connection <%= "foo" %>

+

Web Socket Echo Demo (run from Minefield!)

+ + + + +

+This text will be sent on the socket: + + +

+ + \ No newline at end of file diff --git a/test/WebRoot/WebSocket/echo.aspx b/test/WebRoot/WebSocket/echo.aspx new file mode 100644 index 0000000000..c055ee8300 --- /dev/null +++ b/test/WebRoot/WebSocket/echo.aspx @@ -0,0 +1,65 @@ +<%@ Page Language="C#" AutoEventWireUp="true" %> + + +

#active websocket connection <%= "foo" %>

+

Web Socket Echo Demo (run from Minefield!)

+ + + + +

+This text will be sent on the socket: + + +

+ + \ No newline at end of file diff --git a/test/WebRoot/WebSocket/echoAspnetCore.aspx b/test/WebRoot/WebSocket/echoAspnetCore.aspx new file mode 100644 index 0000000000..3fe9deaf28 --- /dev/null +++ b/test/WebRoot/WebSocket/echoAspnetCore.aspx @@ -0,0 +1,62 @@ +<%@ Page Language="C#" AutoEventWireUp="true" %> + + +

#active websocket connection <%= "foo" %>

+

Web Socket Echo Demo (run from Minefield!)

+ + + + +

+This text will be sent on the socket: + + +

+ + \ No newline at end of file diff --git a/test/WebRoot/WebSocket/echoSubProtocol.aspx b/test/WebRoot/WebSocket/echoSubProtocol.aspx new file mode 100644 index 0000000000..f349a3f96e --- /dev/null +++ b/test/WebRoot/WebSocket/echoSubProtocol.aspx @@ -0,0 +1,65 @@ +<%@ Page Language="C#" AutoEventWireUp="true" %> + + +

#active websocket connection <%= "foo" %>

+

Web Socket Echo Demo (run from Minefield!)

+ + + + +

+This text will be sent on the socket: + + +

+ + \ No newline at end of file diff --git a/test/WebRoot/WebSocket/web.config b/test/WebRoot/WebSocket/web.config new file mode 100644 index 0000000000..5204b66b4a --- /dev/null +++ b/test/WebRoot/WebSocket/web.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/test/WebRoot/WebSocket/web.config.bak b/test/WebRoot/WebSocket/web.config.bak new file mode 100644 index 0000000000..5204b66b4a --- /dev/null +++ b/test/WebRoot/WebSocket/web.config.bak @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/test/WebRoot/applicationhost.config.txt b/test/WebRoot/applicationhost.config.txt new file mode 100644 index 0000000000..6e64698b92 --- /dev/null +++ b/test/WebRoot/applicationhost.config.txt @@ -0,0 +1,1233 @@ + + + + + + + +

+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/WebRoot/parent/web.config b/test/WebRoot/parent/web.config new file mode 100644 index 0000000000..5c7fb464da --- /dev/null +++ b/test/WebRoot/parent/web.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file