diff --git a/Build/common.props b/Build/common.props index c87cc995fa..7eb2e446b7 100644 --- a/Build/common.props +++ b/Build/common.props @@ -12,12 +12,18 @@ $(VersionSuffix)-$(BuildNumber) + + RunConfiguration.TargetPlatform=x64 + + + \ No newline at end of file diff --git a/Build/repo.props b/Build/repo.props index a56e75519b..1d5d44d0dd 100644 --- a/Build/repo.props +++ b/Build/repo.props @@ -3,4 +3,8 @@ + + + RunConfiguration.TargetPlatform=x64 + \ No newline at end of file diff --git a/Build/repo.targets b/Build/repo.targets new file mode 100644 index 0000000000..2138b46789 --- /dev/null +++ b/Build/repo.targets @@ -0,0 +1,59 @@ + + + true + $(VerifyDependsOn);PublishPackage + https://dotnet.myget.org/F/aspnetcoremodule/api/v2/package + + + + + + + + + + + + + + + %(MSBuild15ExePaths.FullPath) + + + + + + + + + + + + + + $(RepositoryRoot).build\nuget.exe + $(RepositoryRoot)nuget\AspNetCore.nuspec + + + + + + + + + + + + <_PackagePublisherPath>@(PackagePublisherPath)\PackagePublisher.exe + + + + + + + + \ No newline at end of file diff --git a/makefile.shade b/makefile.shade deleted file mode 100644 index 6be672789f..0000000000 --- a/makefile.shade +++ /dev/null @@ -1,20 +0,0 @@ -default BASE_DIR_LOCAL='${Directory.GetCurrentDirectory()}' -default BUILD_DIR_LOCAL='${Path.Combine(BASE_DIR_LOCAL, "artifacts", "build")}' -var VERSION='0.1' -var FULL_VERSION='0.1' - -use-standard-lifecycle -k-standard-goals - -#make-nupkg target='package' - log info='Make nuget package containing ASP.NET Core Module' - @{ - var nugetExePath = Environment.GetEnvironmentVariable("KOREBUILD_NUGET_EXE"); - if (string.IsNullOrEmpty(nugetExePath)) - { - nugetExePath = Path.Combine(BASE_DIR_LOCAL, ".build", "nuget.exe"); - } - - var nuspecPath = Path.Combine(BASE_DIR_LOCAL, "nuget", "AspNetCore.nuspec"); - ExecClr(nugetExePath, "pack " + nuspecPath + " -OutputDirectory " + BUILD_DIR_LOCAL + " -prop VERSION=1.0.0-" + BuildNumber); - } diff --git a/src/AspNetCore/AspNetCore.vcxproj b/src/AspNetCore/AspNetCore.vcxproj index 423cf6e188..c795aed3da 100644 --- a/src/AspNetCore/AspNetCore.vcxproj +++ b/src/AspNetCore/AspNetCore.vcxproj @@ -71,6 +71,9 @@ + + $(SolutionDir)artifacts\build\$(ProjectName)\bin\$(Configuration)\$(Platform) + NotUsing diff --git a/test/AspNetCoreModule.Test/Framework/EnvironmentVariableTestConditionAttribute.cs b/test/AspNetCoreModule.Test/Framework/EnvironmentVariableTestConditionAttribute.cs deleted file mode 100644 index 8e17664271..0000000000 --- a/test/AspNetCoreModule.Test/Framework/EnvironmentVariableTestConditionAttribute.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the MIT License. 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 index 0a77d73e57..5e122c900b 100644 --- a/test/AspNetCoreModule.Test/Framework/IISConfigUtility.cs +++ b/test/AspNetCoreModule.Test/Framework/IISConfigUtility.cs @@ -22,9 +22,10 @@ namespace AspNetCoreModule.Test.Framework public static string DefaultAppPool = "DefaultAppPool"; } + public static string ApppHostTemporaryBackupFileExtention = null; private ServerType _serverType = ServerType.IIS; private string _iisExpressConfigPath = null; - + public enum AppPoolBitness { enable32Bit, @@ -53,63 +54,167 @@ namespace AspNetCoreModule.Test.Framework } } - public IISConfigUtility(ServerType type, string iisExpressConfigPath = null) + public IISConfigUtility(ServerType type, string iisExpressConfigPath) { _serverType = type; _iisExpressConfigPath = iisExpressConfigPath; } - public static void BackupAppHostConfig() + public static bool BackupAppHostConfig(string fileExtenstion, bool overWriteMode) { + bool result = true; string fromfile = Strings.AppHostConfigPath; - string tofile = Strings.AppHostConfigPath + ".ancmtest.bak"; + string tofile = Strings.AppHostConfigPath + fileExtenstion; if (File.Exists(fromfile)) { - TestUtility.FileCopy(fromfile, tofile, overWrite: false); + try + { + TestUtility.FileCopy(fromfile, tofile, overWrite: overWriteMode); + } + catch + { + result = false; + } + } + return result; + } + + public static void RestoreAppHostConfig(bool restoreFromMasterBackupFile = true) + { + string masterBackupFileExtension = ".ancmtest.mastebackup"; + string masterBackupFilePath = Strings.AppHostConfigPath + masterBackupFileExtension; + string temporaryBackupFileExtenstion = null; + string temporaryBackupFilePath = null; + string tofile = Strings.AppHostConfigPath; + + string backupFileExentsionForDebug = ".ancmtest.debug"; + string backupFilePathForDebug = Strings.AppHostConfigPath + backupFileExentsionForDebug; + TestUtility.DeleteFile(backupFilePathForDebug); + + // Create a master backup file + if (restoreFromMasterBackupFile) + { + // Create a master backup file if it does not exist + if (!File.Exists(masterBackupFilePath)) + { + if (!File.Exists(tofile)) + { + throw new ApplicationException("Can't find " + tofile); + } + BackupAppHostConfig(masterBackupFileExtension, overWriteMode: false); + } + + if (!File.Exists(masterBackupFilePath)) + { + throw new ApplicationException("Not found master backup file " + masterBackupFilePath); + } + } + + // if applicationhost.config does not exist but master backup file is available, create a new applicationhost.config from the master backup file first + if (!File.Exists(tofile)) + { + CopyAppHostConfig(masterBackupFilePath, tofile); + } + + // Create a temporary backup file with the current applicationhost.config to rollback after test is completed. + if (ApppHostTemporaryBackupFileExtention == null) + { + // retry 10 times until it really creates the temporary backup file + for (int i = 0; i < 10; i++) + { + temporaryBackupFileExtenstion = "." + TestUtility.RandomString(5); + string tempFile = Strings.AppHostConfigPath + temporaryBackupFileExtenstion; + if (File.Exists(tempFile)) + { + // file already exists, try with a different file name + continue; + } + + bool backupSuccess = BackupAppHostConfig(temporaryBackupFileExtenstion, overWriteMode: false); + if (backupSuccess && File.Exists(tempFile)) + { + if (File.Exists(tempFile)) + { + ApppHostTemporaryBackupFileExtention = temporaryBackupFileExtenstion; + break; + } + } + } + + if (ApppHostTemporaryBackupFileExtention == null) + { + throw new ApplicationException("Can't make a temporary backup file"); + } + } + + if (restoreFromMasterBackupFile) + { + // restoring applicationhost.config from the master backup file + CopyAppHostConfig(masterBackupFilePath, tofile); + } + else + { + // Create a temporary backup file to preserve the last state for debugging purpose before rolling back from the temporary backup file + try + { + BackupAppHostConfig(backupFileExentsionForDebug, overWriteMode: true); + } + catch + { + TestUtility.LogInformation("Failed to create a backup file for debugging"); + } + + // restoring applicationhost.config from the temporary backup file + temporaryBackupFilePath = Strings.AppHostConfigPath + ApppHostTemporaryBackupFileExtention; + CopyAppHostConfig(temporaryBackupFilePath, tofile); + + // delete the temporary backup file because it is not used anymore + try + { + TestUtility.DeleteFile(temporaryBackupFilePath); + } + catch + { + TestUtility.LogInformation("Failed to cleanup temporary backup file : " + temporaryBackupFilePath); + } } } - public static void RestoreAppHostConfig() + private static void CopyAppHostConfig(string fromfile, string tofile) { - 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(); + throw new System.ApplicationException("Failed to backup " + tofile); } - // try again after the ininial clean up - if (File.Exists(fromfile)) + // try restoring applicationhost.config again after the ininial clean up for better reliability + try { - try - { - TestUtility.FileCopy(fromfile, tofile, true, true); - } - catch - { - // ignore - } + 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); - } + // 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"); - } + // verify restoration is done successfully + if (File.ReadAllBytes(fromfile).Length != File.ReadAllBytes(tofile).Length) + { + throw new System.ApplicationException("Failed to restore applicationhost.config from " + fromfile + " to " + tofile); } } @@ -243,7 +348,7 @@ namespace AspNetCoreModule.Test.Framework } } - public void EnableWindowsAuthentication(string siteName) + public void EnableIISAuthentication(string siteName, bool windows, bool basic, bool anonymous) { TestUtility.LogInformation("Enable Windows authentication : " + siteName); using (ServerManager serverManager = GetServerManager()) @@ -251,9 +356,11 @@ namespace AspNetCoreModule.Test.Framework Configuration config = serverManager.GetApplicationHostConfiguration(); ConfigurationSection anonymousAuthenticationSection = config.GetSection("system.webServer/security/authentication/anonymousAuthentication", siteName); - anonymousAuthenticationSection["enabled"] = false; + anonymousAuthenticationSection["enabled"] = anonymous; + ConfigurationSection basicAuthenticationSection = config.GetSection("system.webServer/security/authentication/basicAuthentication", siteName); + basicAuthenticationSection["enabled"] = basic; ConfigurationSection windowsAuthenticationSection = config.GetSection("system.webServer/security/authentication/windowsAuthentication", siteName); - windowsAuthenticationSection["enabled"] = true; + windowsAuthenticationSection["enabled"] = windows; serverManager.CommitChanges(); } @@ -265,7 +372,7 @@ namespace AspNetCoreModule.Test.Framework using (ServerManager serverManager = GetServerManager()) { Configuration config = serverManager.GetApplicationHostConfiguration(); - + ConfigurationSection iisClientCertificateMappingAuthenticationSection = config.GetSection("system.webServer/security/authentication/iisClientCertificateMappingAuthentication", siteName); // enable iisClientCertificateMappingAuthentication @@ -275,7 +382,10 @@ namespace AspNetCoreModule.Test.Framework // add a new oneToOne mapping collection item ConfigurationElement addElement = oneToOneMappingsCollection.CreateElement("add"); addElement["userName"] = userName; - addElement["password"] = password; + if (password != null) + { + addElement["password"] = password; + } addElement["certificate"] = publicKey; oneToOneMappingsCollection.Add(addElement); @@ -326,32 +436,39 @@ namespace AspNetCoreModule.Test.Framework public void SetANCMConfig(string siteName, string appName, string attributeName, object attributeValue) { - using (ServerManager serverManager = GetServerManager()) + try { - Configuration config = serverManager.GetWebConfiguration(siteName, appName); - ConfigurationSection aspNetCoreSection = config.GetSection("system.webServer/aspNetCore"); - if (attributeName == "environmentVariable") + using (ServerManager serverManager = GetServerManager()) { - 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) + Configuration config = serverManager.GetWebConfiguration(siteName, appName); + ConfigurationSection aspNetCoreSection = config.GetSection("system.webServer/aspNetCore"); + if (attributeName == "environmentVariable") { - throw new System.ApplicationException("duplicated collection item"); + 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; } - environmentVariablesCollection.Add(environmentVariableElement); - } - else - { - aspNetCoreSection[attributeName] = attributeValue; - } - serverManager.CommitChanges(); - } + serverManager.CommitChanges(); + } + } + catch (Exception ex) + { + throw ex; + } } public void ConfigureCustomLogging(string siteName, string appName, int statusCode, int subStatusCode, string path) @@ -387,21 +504,29 @@ namespace AspNetCoreModule.Test.Framework { if (_isIISInstalled == null) { - bool result = true; - if (!File.Exists(Path.Combine(Strings.IIS64BitPath, "iiscore.dll"))) + _isIISInstalled = true; + if (_isIISInstalled == true && !File.Exists(Path.Combine(Strings.IIS64BitPath, "iiscore.dll"))) { - result = false; + _isIISInstalled = false; } - if (!File.Exists(Path.Combine(Strings.IIS64BitPath, "config", "applicationhost.config"))) + if (_isIISInstalled == true && !File.Exists(Path.Combine(Strings.IIS64BitPath, "config", "applicationhost.config"))) { - result = false; + _isIISInstalled = false; } - _isIISInstalled = result; } return _isIISInstalled; } + set + { + _isIISInstalled = value; + } } + public static bool IsIISReady { + get; + set; + } + public bool IsAncmInstalled(ServerType servertype) { bool result = true; @@ -422,7 +547,7 @@ namespace AspNetCoreModule.Test.Framework return result; } - public string GetServiceStatus(string serviceName) + public static string GetServiceStatus(string serviceName) { ServiceController sc = new ServiceController(serviceName); @@ -959,26 +1084,8 @@ namespace AspNetCoreModule.Test.Framework public string CreateSelfSignedCertificateWithMakeCert(string subjectName, string issuerName = null, string extendedKeyUsage = null) { - string makecertExeFilePath = "makecert.exe"; - var makecertExeFilePaths = new string[] - { - Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"), "Windows Kits", "8.1", "bin", "x64", "makecert.exe"), - Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "Windows Kits", "8.1", "bin", "x86", "makecert.exe"), - Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"), "Windows Kits", "8.0", "bin", "x64", "makecert.exe"), - Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "Windows Kits", "8.0", "bin", "x86", "makecert.exe"), - Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"), "Windows SKDs", "Windows", "v7.1A", "bin", "x64", "makecert.exe"), - Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "Windows SKDs", "Windows", "v7.1A", "bin", "makecert.exe") - }; + string makecertExeFilePath = TestUtility.GetMakeCertPath(); - foreach (string item in makecertExeFilePaths) - { - if (File.Exists(item)) - { - makecertExeFilePath = item; - break; - } - } - string parameter; string targetSSLStore = string.Empty; if (issuerName == null) diff --git a/test/AspNetCoreModule.Test/Framework/InitializeTestMachine.cs b/test/AspNetCoreModule.Test/Framework/InitializeTestMachine.cs index 0bf79b4e25..12df0ca755 100644 --- a/test/AspNetCoreModule.Test/Framework/InitializeTestMachine.cs +++ b/test/AspNetCoreModule.Test/Framework/InitializeTestMachine.cs @@ -11,14 +11,18 @@ namespace AspNetCoreModule.Test.Framework public class InitializeTestMachine : IDisposable { // - // By default, we don't use the private AspNetCoreFile + // By default, we use the private AspNetCoreFile which were created from this solution // - public static bool UsePrivateAspNetCoreFile = false; + public static bool UsePrivateAspNetCoreFile = true; public static int SiteId = 40000; - public static string Aspnetcore_path = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "system32", "inetsrv", "aspnetcore_private.dll"); + public const string PrivateFileName = "aspnetcore_private.dll"; + public static string Aspnetcore_path = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "system32", "inetsrv", PrivateFileName); 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 Aspnetcore_X86_path = Path.Combine(Environment.ExpandEnvironmentVariables("%windir%"), "syswow64", "inetsrv", PrivateFileName); + public static string IISExpressAspnetcore_path = Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "IIS Express", PrivateFileName); + public static string IISExpressAspnetcore_X86_path = Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"), "IIS Express", PrivateFileName); + 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; @@ -27,11 +31,6 @@ namespace AspNetCoreModule.Test.Framework public InitializeTestMachine() { - if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess) - { - TestUtility.LogInformation("Error!!! Skipping to run InitializeTestMachine::InitializeTestMachine() because the test process is started on syswow mode"); - return; - } _referenceCount++; if (_referenceCount == 1) @@ -41,27 +40,87 @@ namespace AspNetCoreModule.Test.Framework _InitializeTestMachineCompleted = false; TestUtility.LogInformation("InitializeTestMachine::Start"); - if (Environment.ExpandEnvironmentVariables("%ANCMDebug%").ToLower() == "true") + if (Environment.ExpandEnvironmentVariables("%ANCMTEST_DEBUG%").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"); + // check Makecert.exe exists try { - if (IISConfigUtility.IsIISInstalled == true) - { - IISConfigUtility.RestoreAppHostConfig(); - } + string makecertExeFilePath = TestUtility.GetMakeCertPath(); + TestUtility.RunCommand(makecertExeFilePath, null, true, true); + TestUtility.LogInformation("Verified makecert.exe is available : " + makecertExeFilePath); } - catch + catch (Exception ex) { - TestUtility.LogInformation("Failed to restore applicationhost.config"); + throw new System.ApplicationException("makecert.exe is not available : " + ex.Message); } + TestUtility.ResetHelper(ResetHelperMode.KillIISExpress); + + // check if we can use IIS server instead of IISExpress + try + { + IISConfigUtility.IsIISReady = false; + if (IISConfigUtility.IsIISInstalled == true) + { + if (Environment.GetEnvironmentVariable("ANCMTEST_USE_IISEXPRESS") != null && Environment.GetEnvironmentVariable("ANCMTEST_USE_IISEXPRESS").Equals("true", StringComparison.InvariantCultureIgnoreCase)) + { + throw new System.ApplicationException("'ANCMTestServerType' environment variable is set to 'true'"); + } + + // check websocket is installed + if (File.Exists(Path.Combine(IISConfigUtility.Strings.IIS64BitPath, "iiswsock.dll"))) + { + TestUtility.LogInformation("Websocket is installed"); + } + else + { + throw new System.ApplicationException("websocket module is not installed"); + } + + TestUtility.ResetHelper(ResetHelperMode.KillWorkerProcess); + + // Reset applicationhost.config + TestUtility.LogInformation("Restoring applicationhost.config"); + IISConfigUtility.RestoreAppHostConfig(restoreFromMasterBackupFile:true); + TestUtility.StartW3svc(); + + // check w3svc is running after resetting applicationhost.config + if (IISConfigUtility.GetServiceStatus("w3svc") == "Running") + { + TestUtility.LogInformation("W3SVC service is restarted after restoring applicationhost.config"); + } + else + { + throw new System.ApplicationException("WWW service can't start"); + } + + // check URLRewrite module exists + if (File.Exists(Path.Combine(IISConfigUtility.Strings.IIS64BitPath, "rewrite.dll"))) + { + TestUtility.LogInformation("Verified URL Rewrite module installed for IIS server"); + } + else + { + throw new System.ApplicationException("URL Rewrite module is not installed"); + } + + if (IISConfigUtility.ApppHostTemporaryBackupFileExtention == null) + { + throw new System.ApplicationException("Failed to backup applicationhost.config"); + } + IISConfigUtility.IsIISReady = true; + } + } + catch (Exception ex) + { + RollbackIISApplicationhostConfigFile(); + TestUtility.LogInformation("We will use IISExpress instead of IIS: " + ex.Message); + } + + string siteRootPath = Path.Combine(Environment.ExpandEnvironmentVariables("%SystemDrive%") + @"\", "inetpub", "ANCMTest"); if (!Directory.Exists(siteRootPath)) { Directory.CreateDirectory(siteRootPath); @@ -97,18 +156,16 @@ namespace AspNetCoreModule.Test.Framework PreparePrivateANCMFiles(); // update applicationhost.config for IIS server - if (IISConfigUtility.IsIISInstalled == true) + if (IISConfigUtility.IsIISReady) { - - using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + using (var iisConfig = new IISConfigUtility(ServerType.IIS, null)) { iisConfig.AddModule("AspNetCoreModule", Aspnetcore_path, null); } } } - - _InitializeTestMachineCompleted = true; + _InitializeTestMachineCompleted = true; TestUtility.LogInformation("InitializeTestMachine::InitializeTestMachine() End"); } @@ -122,7 +179,7 @@ namespace AspNetCoreModule.Test.Framework { TestUtility.LogInformation("InitializeTestMachine::InitializeTestMachine() Waiting..."); Thread.Sleep(500); - } + } } if (!_InitializeTestMachineCompleted) { @@ -138,60 +195,68 @@ namespace AspNetCoreModule.Test.Framework { 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!!!"); - } - } - } - } + RollbackIISApplicationhostConfigFile(); TestUtility.LogInformation("InitializeTestMachine::Dispose() End"); } } - + + private void RollbackIISApplicationhostConfigFile() + { + if (IISConfigUtility.ApppHostTemporaryBackupFileExtention != null) + { + try + { + TestUtility.ResetHelper(ResetHelperMode.KillWorkerProcess); + } + catch + { + TestUtility.LogInformation("Failed to stop IIS worker processes"); + } + try + { + IISConfigUtility.RestoreAppHostConfig(restoreFromMasterBackupFile: false); + } + catch + { + TestUtility.LogInformation("Failed to rollback applicationhost.config"); + } + try + { + TestUtility.StartW3svc(); + } + catch + { + TestUtility.LogInformation("Failed to start w3svc"); + } + IISConfigUtility.ApppHostTemporaryBackupFileExtention = null; + } + } + 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"); + // First try with release build + outputPath = Path.Combine(solutionRoot, "artifacts", "build", "AspNetCore", "bin", "Release"); - // If debug build does is not available, try with release build + // If release build is not available, try with debug 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!!!"); + outputPath = Path.Combine(solutionRoot, "artifacts", "build", "AspNetCore", "bin", "Debug"); } - // create an extra private copy of the private file on IISExpress directory + 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; check if there is any build issue!!!"); + } + + // create an extra private copy of the private file on IIS directory if (InitializeTestMachine.UsePrivateAspNetCoreFile) { bool updateSuccess = false; @@ -204,10 +269,15 @@ namespace AspNetCoreModule.Test.Framework TestUtility.ResetHelper(ResetHelperMode.KillWorkerProcess); TestUtility.ResetHelper(ResetHelperMode.StopW3svcStartW3svc); Thread.Sleep(1000); - TestUtility.FileCopy(Path.Combine(outputPath, "x64", "aspnetcore.dll"), Aspnetcore_path); + string from = Path.Combine(outputPath, "x64", "aspnetcore.dll"); + TestUtility.FileCopy(from, Aspnetcore_path, overWrite:true, ignoreExceptionWhileDeletingExistingFile:false); + TestUtility.FileCopy(from, IISExpressAspnetcore_path, overWrite: true, ignoreExceptionWhileDeletingExistingFile: false); + if (TestUtility.IsOSAmd64) { - TestUtility.FileCopy(Path.Combine(outputPath, "Win32", "aspnetcore.dll"), Aspnetcore_X86_path); + from = Path.Combine(outputPath, "Win32", "aspnetcore.dll"); + TestUtility.FileCopy(from, Aspnetcore_X86_path, overWrite: true, ignoreExceptionWhileDeletingExistingFile: false); + TestUtility.FileCopy(from, IISExpressAspnetcore_X86_path, overWrite: true, ignoreExceptionWhileDeletingExistingFile: false); } updateSuccess = true; } diff --git a/test/AspNetCoreModule.Test/Framework/TestUtility.cs b/test/AspNetCoreModule.Test/Framework/TestUtility.cs index 9e7d6d67fd..e7373e2299 100644 --- a/test/AspNetCoreModule.Test/Framework/TestUtility.cs +++ b/test/AspNetCoreModule.Test/Framework/TestUtility.cs @@ -215,6 +215,7 @@ namespace AspNetCoreModule.Test.Framework { exceptionBlock?.Invoke(exception); } + LogInformation("ANCMTEST::RetryHelper Retrying " + retry); Thread.Sleep(retryDelayMilliseconds); } return false; @@ -248,21 +249,21 @@ namespace AspNetCoreModule.Test.Framework { if (format != null) { - Logger.LogTrace(format); + Logger.LogTrace(format, parameters); } } public static void LogError(string format, params object[] parameters) { if (format != null) { - Logger.LogError(format); + Logger.LogError(format, parameters); } } public static void LogInformation(string format, params object[] parameters) { if (format != null) { - Logger.LogInformation(format); + Logger.LogInformation(format, parameters); } } @@ -433,6 +434,31 @@ namespace AspNetCoreModule.Test.Framework } } + public static string GetMakeCertPath() + { + string makecertExeFilePath = "makecert.exe"; + var makecertExeFilePaths = new string[] + { + Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"), "Windows Kits", "8.1", "bin", "x64", "makecert.exe"), + Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "Windows Kits", "8.1", "bin", "x86", "makecert.exe"), + Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"), "Windows Kits", "8.0", "bin", "x64", "makecert.exe"), + Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "Windows Kits", "8.0", "bin", "x86", "makecert.exe"), + Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"), "Windows SKDs", "Windows", "v7.1A", "bin", "x64", "makecert.exe"), + Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles%"), "Windows SKDs", "Windows", "v7.1A", "bin", "makecert.exe") + }; + + foreach (string item in makecertExeFilePaths) + { + if (File.Exists(item)) + { + makecertExeFilePath = item; + break; + } + } + + return makecertExeFilePath; + } + public static int GetNumberOfProcess(string processFileName, int expectedNumber = 1, int retry = 0) { int result = 0; @@ -704,7 +730,16 @@ namespace AspNetCoreModule.Test.Framework IPEndPoint a = new IPEndPoint(0, 443); // create Powershell runspace - Runspace runspace = RunspaceFactory.CreateRunspace(); + Runspace runspace = null; + try + { + runspace = RunspaceFactory.CreateRunspace(); + } + catch + { + LogInformation("Failed to instantiate powershell Runspace; if this is Win7, install Powershell 4.0 to fix this problem"); + throw new ApplicationException("Failed to instantiate powershell Runspace"); + } // open it runspace.Open(); @@ -759,7 +794,7 @@ namespace AspNetCoreModule.Test.Framework p.StartInfo.UseShellExecute = false; p.StartInfo.CreateNoWindow = true; - p.Start(); + p.Start(); pid = p.Id; string standardOutput = string.Empty; string standardError = string.Empty; diff --git a/test/AspNetCoreModule.Test/Framework/TestWebApplication.cs b/test/AspNetCoreModule.Test/Framework/TestWebApplication.cs index 3e6498e6dc..83f0b03e32 100644 --- a/test/AspNetCoreModule.Test/Framework/TestWebApplication.cs +++ b/test/AspNetCoreModule.Test/Framework/TestWebApplication.cs @@ -175,7 +175,7 @@ namespace AspNetCoreModule.Test.Framework // read web.config string fileContent = TestUtility.FileReadAllText(filePath); - // get the value of processPath attribute of aspNetCore element + // get the value of arguments attribute of aspNetCore element if (fileContent != null) { result = TestUtility.XmlParser(fileContent, "aspNetCore", "arguments", null); @@ -197,7 +197,7 @@ namespace AspNetCoreModule.Test.Framework { string fromfile = Path.Combine(_physicalPath, from + ".bak"); string tofile = Path.Combine(_physicalPath, from); - if (!File.Exists(tofile)) + if (!File.Exists(fromfile)) { BackupFile(from); } diff --git a/test/AspNetCoreModule.Test/Framework/TestWebSite.cs b/test/AspNetCoreModule.Test/Framework/TestWebSite.cs index a946fc49d3..cf062ef1e9 100644 --- a/test/AspNetCoreModule.Test/Framework/TestWebSite.cs +++ b/test/AspNetCoreModule.Test/Framework/TestWebSite.cs @@ -19,7 +19,7 @@ namespace AspNetCoreModule.Test.Framework public TestUtility testHelper; private ILogger _logger; private int _iisExpressPidBackup = -1; - + private string postfix = string.Empty; public void Dispose() @@ -29,10 +29,17 @@ namespace AspNetCoreModule.Test.Framework if (_iisExpressPidBackup != -1) { var iisExpressProcess = Process.GetProcessById(Convert.ToInt32(_iisExpressPidBackup)); - iisExpressProcess.Kill(); - iisExpressProcess.WaitForExit(); + try + { + iisExpressProcess.Kill(); + iisExpressProcess.WaitForExit(); + iisExpressProcess.Close(); + } + catch + { + TestUtility.RunPowershellScript("stop-process -id " + _iisExpressPidBackup); + } } - TestUtility.LogInformation("TestWebSite::Dispose() End"); } @@ -91,14 +98,30 @@ namespace AspNetCoreModule.Test.Framework _tcpPort = value; } } + + public ServerType IisServerType { get; set; } + public string IisExpressConfigPath { get; set; } + private int _siteId { get; set; } + private IISConfigUtility.AppPoolBitness _appPoolBitness { get; set; } - public TestWebSite(IISConfigUtility.AppPoolBitness appPoolBitness, string loggerPrefix = "ANCMTest", ServerType serverType = ServerType.IIS) + public TestWebSite(IISConfigUtility.AppPoolBitness appPoolBitness, string loggerPrefix = "ANCMTest", bool startIISExpress = true, bool copyAllPublishedFiles = false) { + _appPoolBitness = appPoolBitness; + + // + // Default server type is IISExpress. we, however, should use IIS server instead if IIS server is ready to use. + // + IisServerType = ServerType.IISExpress; + if (IISConfigUtility.IsIISReady) + { + IisServerType = ServerType.IIS; + } + TestUtility.LogInformation("TestWebSite::TestWebSite() Start"); string solutionPath = InitializeTestMachine.GetSolutionDirectory(); - if (serverType == ServerType.IIS) + if (IisServerType == ServerType.IIS) { // check JitDebugger before continuing TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); @@ -134,11 +157,19 @@ namespace AspNetCoreModule.Test.Framework string aspnetCoreAppRootPath = Path.Combine(siteRootPath, "AspNetCoreApp"); string srcPath = TestUtility.GetApplicationPath(); + // copy http.config to the test site root directory and initialize iisExpressConfigPath with the path + if (IisServerType == ServerType.IISExpress) + { + IisExpressConfigPath = Path.Combine(siteRootPath, "http.config"); + TestUtility.FileCopy(Path.Combine(solutionPath, "test", "AspNetCoreModule.Test", "http.config"), IisExpressConfigPath); + } + // // Currently we use only DotnetCore v1.1 // string publishPath = Path.Combine(srcPath, "bin", "Debug", "netcoreapp1.1", "publish"); - + string publishPathOutput = Path.Combine(Environment.ExpandEnvironmentVariables("%SystemDrive%") + @"\", "inetpub", "ANCMTest", "publishPathOutput"); + // // Publish aspnetcore app // @@ -147,44 +178,51 @@ namespace AspNetCoreModule.Test.Framework string argumentForDotNet = "publish " + srcPath; TestUtility.LogInformation("TestWebSite::TestWebSite() StandardTestApp is not published, trying to publish on the fly: dotnet.exe " + argumentForDotNet); TestUtility.RunCommand("dotnet", argumentForDotNet); + TestUtility.DirectoryCopy(publishPath, publishPathOutput); + TestUtility.FileCopy(Path.Combine(publishPathOutput, "web.config"), Path.Combine(publishPathOutput, "web.config.bak")); + + // Adjust the arguments attribute value with IISConfigUtility from a temporary site + using (var iisConfig = new IISConfigUtility(IisServerType, IisExpressConfigPath)) + { + string tempSiteName = "ANCMTest_Temp"; + int tempId = InitializeTestMachine.SiteId - 1; + string argumentFileName = (new TestWebApplication("/", publishPathOutput, null)).GetArgumentFileName(); + iisConfig.CreateSite(tempSiteName, publishPathOutput, tempId, tempId); + iisConfig.SetANCMConfig(tempSiteName, "/", "arguments", Path.Combine(publishPathOutput, argumentFileName)); + iisConfig.DeleteSite(tempSiteName); + } _publishedAspnetCoreApp = true; } - - // check published files - bool checkPublishedFiles = false; - string[] publishedFiles = Directory.GetFiles(publishPath); - foreach (var item in publishedFiles) + + if (copyAllPublishedFiles) { - if (Path.GetFileName(item) == "web.config") - { - checkPublishedFiles = true; - } + // Copy all the files in the pubishpath to the standardAppRootPath + TestUtility.DirectoryCopy(publishPath, aspnetCoreAppRootPath); + TestUtility.FileCopy(Path.Combine(publishPathOutput, "web.config.bak"), Path.Combine(aspnetCoreAppRootPath, "web.config")); } - - if (!checkPublishedFiles) + else { - throw new System.ApplicationException("web.config is not available in " + publishPath); + // Copy only web.config file, which points to the shared publishPathOutput, to the standardAppRootPath + TestUtility.CreateDirectory(aspnetCoreAppRootPath); + TestUtility.FileCopy(Path.Combine(publishPathOutput, "web.config"), Path.Combine(aspnetCoreAppRootPath, "web.config")); } - // Copy the pubishpath to standardAppRootPath - TestUtility.DirectoryCopy(publishPath, aspnetCoreAppRootPath); - int tcpPort = InitializeTestMachine.SiteId++; - int siteId = tcpPort; + _siteId = tcpPort; // // initialize class member variables // string appPoolName = null; - if (serverType == ServerType.IIS) + if (IisServerType == ServerType.IIS) { appPoolName = siteName; } - else if (serverType == ServerType.IISExpress) + else if (IisServerType == ServerType.IISExpress) { appPoolName = "Clr4IntegratedAppPool"; } - + // Initialize member variables _hostName = "localhost"; _siteName = siteName; @@ -211,49 +249,99 @@ namespace AspNetCoreModule.Test.Framework 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)) + using (var iisConfig = new IISConfigUtility(IisServerType, IisExpressConfigPath)) { - if (serverType == ServerType.IIS) + // Create apppool + if (IisServerType == ServerType.IIS) { iisConfig.CreateAppPool(appPoolName); - bool is32bit = (appPoolBitness == IISConfigUtility.AppPoolBitness.enable32Bit); - iisConfig.SetAppPoolSetting(appPoolName, "enable32BitAppOnWin64", is32bit); + + // Switch bitness + if (TestUtility.IsOSAmd64 && appPoolBitness == IISConfigUtility.AppPoolBitness.enable32Bit) + { + iisConfig.SetAppPoolSetting(appPoolName, "enable32BitAppOnWin64", true); + } } - iisConfig.CreateSite(siteName, RootAppContext.PhysicalPath, siteId, this.TcpPort, appPoolName); + + if (InitializeTestMachine.UsePrivateAspNetCoreFile && IisServerType == ServerType.IISExpress) + { + iisConfig.AddModule("AspNetCoreModule", ("%IIS_BIN%\\" + InitializeTestMachine.PrivateFileName), null); + } + + iisConfig.CreateSite(siteName, RootAppContext.PhysicalPath, _siteId, 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); + if (startIISExpress) + { + StartIISExpress(); } TestUtility.LogInformation("TestWebSite::TestWebSite() End"); } + + public void StartIISExpress(string verificationCommand = null) + { + if (IisServerType == ServerType.IIS) + { + return; + } + + 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); + + bool isIISExpressReady = false; + int timeout = 3; + for (int i = 0; i < timeout * 5; i++) + { + string statusCode = string.Empty; + try + { + if (verificationCommand == null) + { + verificationCommand = "( invoke-webrequest http://localhost:" + TcpPort + " ).StatusCode"; + } + statusCode = TestUtility.RunPowershellScript(verificationCommand); + } + catch + { + statusCode = "ExceptionError"; + + } + if ("200" == statusCode) + { + isIISExpressReady = true; + break; + } + else + { + System.Threading.Thread.Sleep(200); + } + } + if (isIISExpressReady) + { + TestUtility.LogInformation("IISExpress is ready to use"); + } + else + { + throw new ApplicationException("IISExpress is not responding within " + timeout + " seconds"); + } + } } } diff --git a/test/AspNetCoreModule.Test/FunctionalTest.cs b/test/AspNetCoreModule.Test/FunctionalTest.cs index 52f5fa72ad..1ef8474b08 100644 --- a/test/AspNetCoreModule.Test/FunctionalTest.cs +++ b/test/AspNetCoreModule.Test/FunctionalTest.cs @@ -3,6 +3,7 @@ using AspNetCoreModule.Test.Framework; using Microsoft.AspNetCore.Testing.xunit; +using System; using System.Threading.Tasks; using Xunit; @@ -10,30 +11,19 @@ namespace AspNetCoreModule.Test { public class FunctionalTest : FunctionalTestHelper, IClassFixture { - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [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) + [InlineData(IISConfigUtility.AppPoolBitness.noChange)] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] + public async void BasicTest(IISConfigUtility.AppPoolBitness appPoolBitness) { - return DoBasicTest(serverType, appPoolBitness); + await DoBasicTest(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] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.noChange, 5)] @@ -44,9 +34,9 @@ namespace AspNetCoreModule.Test { return DoRapidFailsPerMinuteTest(appPoolBitness, valueOfRapidFailsPerMinute); } - - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, 25, 19)] @@ -59,9 +49,9 @@ namespace AspNetCoreModule.Test { return DoShutdownTimeLimitTest(appPoolBitness, valueOfshutdownTimeLimit, expectedClosingTime); } - - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, 10)] @@ -72,20 +62,9 @@ namespace AspNetCoreModule.Test { 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] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] @@ -94,9 +73,9 @@ namespace AspNetCoreModule.Test { return DoRecycleApplicationAfterBackendProcessBeingKilled(appPoolBitness); } - - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] @@ -105,9 +84,9 @@ namespace AspNetCoreModule.Test { return DoRecycleApplicationAfterW3WPProcessBeingKilled(appPoolBitness); } - - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] @@ -117,8 +96,8 @@ namespace AspNetCoreModule.Test return DoRecycleApplicationAfterWebConfigUpdated(appPoolBitness); } - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] @@ -127,9 +106,9 @@ namespace AspNetCoreModule.Test { return DoRecycleApplicationWithURLRewrite(appPoolBitness); } - - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] @@ -138,20 +117,26 @@ namespace AspNetCoreModule.Test { return DoRecycleParentApplicationWithURLRewrite(appPoolBitness); } - - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] - [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] - [InlineData(IISConfigUtility.AppPoolBitness.noChange)] - public Task EnvironmentVariablesTest(IISConfigUtility.AppPoolBitness appPoolBitness) + [InlineData("ANCMTestBar", "bar", "bar", IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData("ASPNETCORE_HOSTINGSTARTUPASSEMBLIES", "NA", "Microsoft.AspNetCore.Server.IISIntegration", IISConfigUtility.AppPoolBitness.noChange)] + [InlineData("ASPNETCORE_HOSTINGSTARTUPASSEMBLIES", "newValue", "newValue", IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData("ASPNETCORE_IIS_HTTPAUTH", "anonymous;", "anonymous;", IISConfigUtility.AppPoolBitness.noChange)] + [InlineData("ASPNETCORE_IIS_HTTPAUTH", "basic;anonymous;", "basic;anonymous;", IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData("ASPNETCORE_IIS_HTTPAUTH", "windows;anonymous;", "windows;anonymous;", IISConfigUtility.AppPoolBitness.noChange)] + [InlineData("ASPNETCORE_IIS_HTTPAUTH", "windows;basic;anonymous;", "windows;basic;anonymous;", IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData("ASPNETCORE_IIS_HTTPAUTH", "ignoredValue", "anonymous;", IISConfigUtility.AppPoolBitness.noChange)] + public Task EnvironmentVariablesTest(string environmentVariableName, string environmentVariableValue, string expectedEnvironmentVariableValue, IISConfigUtility.AppPoolBitness appPoolBitness) { - return DoEnvironmentVariablesTest(appPoolBitness); + return DoEnvironmentVariablesTest(environmentVariableName, environmentVariableValue, expectedEnvironmentVariableValue, appPoolBitness); } - - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] @@ -160,9 +145,9 @@ namespace AspNetCoreModule.Test { return DoAppOfflineTestWithRenaming(appPoolBitness); } - - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] @@ -171,9 +156,9 @@ namespace AspNetCoreModule.Test { return DoAppOfflineTestWithUrlRewriteAndDeleting(appPoolBitness); } - - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789")] @@ -182,9 +167,9 @@ namespace AspNetCoreModule.Test { return DoPostMethodTest(appPoolBitness, testData); } - - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] @@ -194,8 +179,8 @@ namespace AspNetCoreModule.Test return DoDisableStartUpErrorPageTest(appPoolBitness); } - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, 10)] @@ -205,8 +190,8 @@ namespace AspNetCoreModule.Test return DoProcessesPerApplicationTest(appPoolBitness, valueOfProcessesPerApplication); } - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, "00:02:00")] @@ -218,8 +203,8 @@ namespace AspNetCoreModule.Test return DoRequestTimeoutTest(appPoolBitness, requestTimeout); } - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] @@ -229,8 +214,8 @@ namespace AspNetCoreModule.Test return DoStdoutLogEnabledTest(appPoolBitness); } - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, "dotnet.exe", "./")] @@ -241,9 +226,9 @@ namespace AspNetCoreModule.Test { return DoProcessPathAndArgumentsTest(appPoolBitness, processPath, argumentsPrefix); } - - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, true)] @@ -255,19 +240,8 @@ namespace AspNetCoreModule.Test 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] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, true, true)] @@ -278,9 +252,9 @@ namespace AspNetCoreModule.Test { return DoCompressionTest(appPoolBitness, useCompressionMiddleWare, enableIISCompression); } - - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] @@ -289,9 +263,9 @@ namespace AspNetCoreModule.Test { return DoCachingTest(appPoolBitness); } - - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] @@ -300,9 +274,9 @@ namespace AspNetCoreModule.Test { return DoSendHTTPSRequestTest(appPoolBitness); } - - [EnvironmentVariableTestCondition("IIS_VARIATIONS_ENABLED")] + [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] [OSSkipCondition(OperatingSystems.Linux)] [OSSkipCondition(OperatingSystems.MacOSX)] [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, true)] @@ -312,5 +286,32 @@ namespace AspNetCoreModule.Test { return DoClientCertificateMappingTest(appPoolBitness, useHTTPSMiddleWare); } + + ////////////////////////////////////////////////////////// + // NOTE: below test scenarios are not valid for Win7 OS + ////////////////////////////////////////////////////////// + [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, SkipReason = "IIS does not support Websocket on Win7")] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit, "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789")] + [InlineData(IISConfigUtility.AppPoolBitness.noChange, "a")] + public Task WebSocketTest(IISConfigUtility.AppPoolBitness appPoolBitness, string testData) + { + return DoWebSocketTest(appPoolBitness, testData); + } + + [ConditionalTheory] + [ANCMTestSkipCondition("RunAsAdministratorAndX64Bitness")] + [OSSkipCondition(OperatingSystems.Linux)] + [OSSkipCondition(OperatingSystems.MacOSX)] + [OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, SkipReason = "WAS does not handle private memory limitation with Job object on Win7")] + [InlineData(IISConfigUtility.AppPoolBitness.enable32Bit)] + [InlineData(IISConfigUtility.AppPoolBitness.noChange)] + public Task RecylingAppPoolTest(IISConfigUtility.AppPoolBitness appPoolBitness) + { + return DoRecylingAppPoolTest(appPoolBitness); + } } } diff --git a/test/AspNetCoreModule.Test/FunctionalTestHelper.cs b/test/AspNetCoreModule.Test/FunctionalTestHelper.cs index 97bf2d92d8..8c206a7dfd 100644 --- a/test/AspNetCoreModule.Test/FunctionalTestHelper.cs +++ b/test/AspNetCoreModule.Test/FunctionalTestHelper.cs @@ -18,11 +18,71 @@ using System.Text; using System.IO; using System.Security.Principal; using System.IO.Compression; +using Microsoft.AspNetCore.Testing.xunit; namespace AspNetCoreModule.Test { + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public class ANCMTestSkipCondition : Attribute, ITestCondition + { + private readonly string _environmentVariableName; + + public ANCMTestSkipCondition(string environmentVariableName) + { + _environmentVariableName = environmentVariableName; + } + + public bool IsMet + { + get + { + bool result = true; + if (_environmentVariableName == "RunAsAdministratorAndX64Bitness") + { + try + { + if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess) + { + throw new System.InvalidOperationException("this should be started with x64 process mode on 64 bit machine"); + } + + bool isElevated; + WindowsIdentity identity = WindowsIdentity.GetCurrent(); + WindowsPrincipal principal = new WindowsPrincipal(identity); + isElevated = principal.IsInRole(WindowsBuiltInRole.Administrator); + if (!isElevated) + { + throw new System.ApplicationException("this should be started as an administrator"); + } + } + catch (Exception ex) + { + AdditionalInfo = ex.Message; + + result = false; + } + } + return result; + } + } + + public string SkipReason + { + get + { + return $"Skip condition: {_environmentVariableName}: this test case is skipped becauset {AdditionalInfo}."; + } + } + + public string AdditionalInfo { get; set; } + } + public class FunctionalTestHelper { + public FunctionalTestHelper() + { + } + private const int _repeatCount = 3; public enum ReturnValueType @@ -33,18 +93,20 @@ namespace AspNetCoreModule.Test None } - public static async Task DoBasicTest(ServerType serverType, IISConfigUtility.AppPoolBitness appPoolBitness) + public static async Task DoBasicTest(IISConfigUtility.AppPoolBitness appPoolBitness) { - using (var testSite = new TestWebSite(appPoolBitness, "DoBasicTest", serverType)) + using (var testSite = new TestWebSite(appPoolBitness, "DoBasicTest")) { string backendProcessId_old = null; DateTime startTime = DateTime.Now; + Thread.Sleep(3000); string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("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(); @@ -78,6 +140,7 @@ namespace AspNetCoreModule.Test backendProcessId_old = backendProcessId; var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId)); Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", "")); + Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId)); backendProcess.Kill(); Thread.Sleep(500); @@ -89,6 +152,12 @@ namespace AspNetCoreModule.Test { using (var testSite = new TestWebSite(appPoolBitness, "DoRecycleApplicationAfterW3WPProcessBeingKilled")) { + if (testSite.IisServerType == ServerType.IISExpress) + { + TestUtility.LogInformation("This test is not valid for IISExpress server type"); + return; + } + string backendProcessId_old = null; const int repeatCount = 3; for (int i = 0; i < repeatCount; i++) @@ -212,11 +281,15 @@ namespace AspNetCoreModule.Test } } - public static async Task DoEnvironmentVariablesTest(IISConfigUtility.AppPoolBitness appPoolBitness) + public static async Task DoEnvironmentVariablesTest(string environmentVariableName, string environmentVariableValue, string expectedEnvironmentVariableValue, IISConfigUtility.AppPoolBitness appPoolBitness) { + if (environmentVariableName == null) + { + throw new InvalidDataException("envrionmentVarialbeName is null"); + } using (var testSite = new TestWebSite(appPoolBitness, "DoEnvironmentVariablesTest")) { - using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath)) { DateTime startTime = DateTime.Now; Thread.Sleep(500); @@ -237,22 +310,78 @@ namespace AspNetCoreModule.Test TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); int expectedValue = Convert.ToInt32(totalNumber) + 1; + string totalResult = (await GetResponse(testSite.AspNetCoreApp.GetUri("GetEnvironmentVariables"), HttpStatusCode.OK)); Assert.True(expectedValue.ToString() == (await GetResponse(testSite.AspNetCoreApp.GetUri("GetEnvironmentVariables"), HttpStatusCode.OK))); - iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "environmentVariable", new string[] { "ANCMTestBar", "bar" }); - Thread.Sleep(500); + bool setEnvironmentVariableConfiguration = true; + + // Set authentication for ASPNETCORE_IIS_HTTPAUTH test scenarios + if (environmentVariableName == "ASPNETCORE_IIS_HTTPAUTH" && environmentVariableValue != "ignoredValue") + { + setEnvironmentVariableConfiguration = false; + bool windows = false; + bool basic = false; + bool anonymous = false; + if (environmentVariableValue.Contains("windows;")) + { + windows = true; + } + if (environmentVariableValue.Contains("basic;")) + { + basic = true; + } + if (environmentVariableValue.Contains("anonymous;")) + { + anonymous = true; + } + iisConfig.EnableIISAuthentication(testSite.SiteName, windows, basic, anonymous); + } + + if (environmentVariableValue == "NA" || environmentVariableValue == null) + { + setEnvironmentVariableConfiguration = false; + } + + // Add a new environment variable + if (setEnvironmentVariableConfiguration) + { + iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "environmentVariable", new string[] { environmentVariableName, environmentVariableValue }); + + // Adjust the new expected total number of environment variables + if (environmentVariableName != "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES" && + environmentVariableName != "ASPNETCORE_IIS_HTTPAUTH") + { + expectedValue++; + } + } + Thread.Sleep(500); + // check JitDebugger before continuing TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger); - - expectedValue++; + totalResult = (await GetResponse(testSite.AspNetCoreApp.GetUri("GetEnvironmentVariables"), HttpStatusCode.OK)); + Assert.True(expectedValue.ToString() == totalResult); Assert.True("foo" == (await GetResponse(testSite.AspNetCoreApp.GetUri("ExpandEnvironmentVariablesANCMTestFoo"), HttpStatusCode.OK))); - Assert.True("bar" == (await GetResponse(testSite.AspNetCoreApp.GetUri("ExpandEnvironmentVariablesANCMTestBar"), HttpStatusCode.OK))); + Assert.True(expectedEnvironmentVariableValue == (await GetResponse(testSite.AspNetCoreApp.GetUri("ExpandEnvironmentVariables" + environmentVariableName), HttpStatusCode.OK))); + + // Verify other common environment variables + string temp = (await GetResponse(testSite.AspNetCoreApp.GetUri("DumpEnvironmentVariables"), HttpStatusCode.OK)); + Assert.True(temp.Contains("ASPNETCORE_PORT")); + Assert.True(temp.Contains("ASPNETCORE_APPL_PATH")); + Assert.True(temp.Contains("ASPNETCORE_IIS_HTTPAUTH")); + Assert.True(temp.Contains("ASPNETCORE_TOKEN")); + Assert.True(temp.Contains("ASPNETCORE_HOSTINGSTARTUPASSEMBLIES")); + + // Verify other inherited environment variables + Assert.True(temp.Contains("PROCESSOR_ARCHITECTURE")); + Assert.True(temp.Contains("USERNAME")); + Assert.True(temp.Contains("USERDOMAIN")); + Assert.True(temp.Contains("USERPROFILE")); } testSite.AspNetCoreApp.RestoreFile("web.config"); } } - + public static async Task DoAppOfflineTestWithRenaming(IISConfigUtility.AppPoolBitness appPoolBitness) { using (var testSite = new TestWebSite(appPoolBitness, "DoAppOfflineTestWithRenaming")) @@ -350,7 +479,7 @@ namespace AspNetCoreModule.Test Thread.Sleep(500); - using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath)) { DateTime startTime = DateTime.Now; Thread.Sleep(500); @@ -390,7 +519,7 @@ namespace AspNetCoreModule.Test { using (var testSite = new TestWebSite(appPoolBitness, "DoRapidFailsPerMinuteTest")) { - using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath)) { bool rapidFailsTriggered = false; iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "rapidFailsPerMinute", valueOfRapidFailsPerMinute); @@ -446,9 +575,10 @@ namespace AspNetCoreModule.Test { using (var testSite = new TestWebSite(appPoolBitness, "DoProcessesPerApplicationTest")) { - using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath)) { DateTime startTime = DateTime.Now; + Thread.Sleep(3000); iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "processesPerApplication", valueOfProcessesPerApplication); HashSet processIDs = new HashSet(); @@ -469,6 +599,7 @@ namespace AspNetCoreModule.Test } Assert.Equal(valueOfProcessesPerApplication, processIDs.Count); + foreach (var id in processIDs) { var backendProcess = Process.GetProcessById(id); @@ -505,7 +636,7 @@ namespace AspNetCoreModule.Test { using (var testSite = new TestWebSite(appPoolBitness, "DoStartupTimeLimitTest")) { - using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath)) { int startupDelay = 3; //3 seconds iisConfig.SetANCMConfig( @@ -536,7 +667,7 @@ namespace AspNetCoreModule.Test { using (var testSite = new TestWebSite(appPoolBitness, "DoRequestTimeoutTest")) { - using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath)) { iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "requestTimeout", TimeSpan.Parse(requestTimeout)); Thread.Sleep(500); @@ -562,7 +693,7 @@ namespace AspNetCoreModule.Test { using (var testSite = new TestWebSite(appPoolBitness, "DoShutdownTimeLimitTest")) { - using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath)) { // 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); @@ -598,16 +729,18 @@ namespace AspNetCoreModule.Test { testSite.AspNetCoreApp.DeleteDirectory("logs"); - using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath)) { DateTime startTime = DateTime.Now; - Thread.Sleep(500); + Thread.Sleep(3000); + 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.GetUri("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)); @@ -642,9 +775,9 @@ namespace AspNetCoreModule.Test public static async Task DoProcessPathAndArgumentsTest(IISConfigUtility.AppPoolBitness appPoolBitness, string processPath, string argumentsPrefix) { - using (var testSite = new TestWebSite(appPoolBitness, "DoProcessPathAndArgumentsTest")) + using (var testSite = new TestWebSite(appPoolBitness, "DoProcessPathAndArgumentsTest", copyAllPublishedFiles:true)) { - using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath)) { string arguments = argumentsPrefix + testSite.AspNetCoreApp.GetArgumentFileName(); string tempProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK); @@ -681,15 +814,14 @@ namespace AspNetCoreModule.Test { using (var testSite = new TestWebSite(appPoolBitness, "DoForwardWindowsAuthTokenTest")) { - using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath)) { string result = string.Empty; iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "forwardWindowsAuthToken", enabledForwardWindowsAuthToken); string requestHeaders = await GetResponse(testSite.AspNetCoreApp.GetUri("DumpRequestHeaders"), HttpStatusCode.OK); Assert.False(requestHeaders.ToUpper().Contains("MS-ASPNETCORE-WINAUTHTOKEN")); - iisConfig.EnableWindowsAuthentication(testSite.SiteName); - + iisConfig.EnableIISAuthentication(testSite.SiteName, windows:true, basic:false, anonymous:false); Thread.Sleep(500); // check JitDebugger before continuing @@ -736,7 +868,13 @@ namespace AspNetCoreModule.Test { using (var testSite = new TestWebSite(appPoolBitness, "DoRecylingAppPoolTest")) { - using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + if (testSite.IisServerType == ServerType.IISExpress) + { + TestUtility.LogInformation("This test is not valid for IISExpress server type"); + return; + } + + using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath)) { // allocating 1024,000 KB @@ -845,7 +983,7 @@ namespace AspNetCoreModule.Test { using (var testSite = new TestWebSite(appPoolBitness, "DoCompressionTest")) { - using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath)) { string startupClass = "StartupCompressionCaching"; if (!useCompressionMiddleWare) @@ -911,7 +1049,7 @@ namespace AspNetCoreModule.Test { using (var testSite = new TestWebSite(appPoolBitness, "DoCachingTest")) { - using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath)) { string startupClass = "StartupCompressionCaching"; @@ -937,16 +1075,26 @@ namespace AspNetCoreModule.Test string result = string.Empty; - result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("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); + const int retryCount = 3; + string headerValue = string.Empty; + string headerValue2 = string.Empty; + for (int i = 0; i < retryCount; i++) + { + result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK); + headerValue = GetHeaderValue(result, "MyCustomHeader"); + Assert.True(result.Contains("foohtm"), "verify response body"); + Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding")); + Thread.Sleep(1500); - result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("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")); + result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK); + headerValue2 = GetHeaderValue(result, "MyCustomHeader"); + Assert.True(result.Contains("foohtm"), "verify response body"); + Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding")); + if (headerValue == headerValue2) + { + break; + } + } Assert.Equal(headerValue, headerValue2); Thread.Sleep(12000); @@ -962,15 +1110,15 @@ namespace AspNetCoreModule.Test public static async Task DoSendHTTPSRequestTest(IISConfigUtility.AppPoolBitness appPoolBitness) { - using (var testSite = new TestWebSite(appPoolBitness, "DoSendHTTPSRequestTest")) + using (var testSite = new TestWebSite(appPoolBitness, "DoSendHTTPSRequestTest", startIISExpress:false)) { - using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath)) { string hostName = ""; string subjectName = "localhost"; string ipAddress = "*"; string hexIPAddress = "0x00"; - int sslPort = 46300; + int sslPort = InitializeTestMachine.SiteId + 6300; // Add https binding and get https uri information iisConfig.AddBindingToSite(testSite.SiteName, ipAddress, sslPort, hostName, "https"); @@ -984,6 +1132,9 @@ namespace AspNetCoreModule.Test // Configure http.sys ssl certificate mapping to IP:Port endpoint with the newly created self signed certificage iisConfig.SetSSLCertificate(sslPort, hexIPAddress, thumbPrint); + // starting IISExpress was deffered after creating test applications and now it is ready to start it + testSite.StartIISExpress(); + // Verify http request string result = string.Empty; result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri(), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK); @@ -1009,9 +1160,9 @@ namespace AspNetCoreModule.Test public static async Task DoClientCertificateMappingTest(IISConfigUtility.AppPoolBitness appPoolBitness, bool useHTTPSMiddleWare) { - using (var testSite = new TestWebSite(appPoolBitness, "DoClientCertificateMappingTest")) + using (var testSite = new TestWebSite(appPoolBitness, "DoClientCertificateMappingTest", startIISExpress: false)) { - using (var iisConfig = new IISConfigUtility(ServerType.IIS)) + using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath)) { string hostName = ""; string rootCN = "ANCMTest" + testSite.PostFix; @@ -1021,7 +1172,7 @@ namespace AspNetCoreModule.Test string ipAddress = "*"; string hexIPAddress = "0x00"; - int sslPort = 46300; + int sslPort = InitializeTestMachine.SiteId + 6300; // Add https binding and get https uri information iisConfig.AddBindingToSite(testSite.SiteName, ipAddress, sslPort, hostName, "https"); @@ -1050,7 +1201,27 @@ namespace AspNetCoreModule.Test // Get public key of the client certificate and Configure OnetToOneClientCertificateMapping the public key and disable anonymous authentication and set SSL flags for Client certificate authentication string publicKey = iisConfig.GetCertificatePublicKey(thumbPrintForClientAuthentication, @"Cert:\CurrentUser\My"); - iisConfig.EnableOneToOneClientCertificateMapping(testSite.SiteName, ".\\" + userName, password, publicKey); + + bool setPasswordSeperately = false; + if (testSite.IisServerType == ServerType.IISExpress && IISConfigUtility.IsIISInstalled == true) + { + setPasswordSeperately = true; + iisConfig.EnableOneToOneClientCertificateMapping(testSite.SiteName, ".\\" + userName, null, publicKey); + } + else + { + iisConfig.EnableOneToOneClientCertificateMapping(testSite.SiteName, ".\\" + userName, password, publicKey); + } + + // IISExpress uses a differnt encryption from full IIS version's and it is not easy to override the encryption methong with MWA. + // As a work-around, password is set with updating the config file directly. + if (setPasswordSeperately) + { + // Search userName property and replace it with userName + password + string text = File.ReadAllText(testSite.IisExpressConfigPath); + text = text.Replace(userName + "\"", userName + "\"" + " " + "password=" + "\"" + password + "\""); + File.WriteAllText(testSite.IisExpressConfigPath, text); + } // Configure kestrel SSL test environment if (useHTTPSMiddleWare) @@ -1073,9 +1244,13 @@ namespace AspNetCoreModule.Test Assert.True(File.Exists(pfxFilePath)); } + // starting IISExpress was deffered after creating test applications and now it is ready to start it + Uri rootHttpsUri = testSite.RootAppContext.GetUri(null, sslPort, protocol: "https"); + testSite.StartIISExpress("( invoke-webrequest " + rootHttpsUri.OriginalString + " -CertificateThumbprint " + thumbPrintForClientAuthentication + ").StatusCode"); + // Verify http request with using client certificate Uri targetHttpsUri = testSite.AspNetCoreApp.GetUri(null, sslPort, protocol: "https"); - string statusCode = TestUtility.RunPowershellScript("( invoke-webrequest " + targetHttpsUri.OriginalString + " -CertificateThumbprint " + thumbPrintForClientAuthentication + ").StatusCode"); + string statusCode = TestUtility.RunPowershellScript("( invoke-webrequest " + targetHttpsUri.OriginalString + " -CertificateThumbprint " + thumbPrintForClientAuthentication + ").StatusCode"); Assert.Equal("200", statusCode); // Verify https request with client certificate includes the certificate header "MS-ASPNETCORE-CLIENTCERT" @@ -1134,10 +1309,7 @@ namespace AspNetCoreModule.Test // Verify WebSocket subprotocol await VerifyResponseBodyContain(testSite.WebSocketApp.GetUri("echoSubProtocol.aspx"), new string[] { "Socket Open", "mywebsocketsubprotocol" }, HttpStatusCode.OK); // echoSubProtocol.aspx has hard coded path for the websocket server - - // Verify process creation ANCM event log - Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId)); - + // Verify websocket using (WebSocketClientHelper websocketClient = new WebSocketClientHelper()) { @@ -1386,10 +1558,30 @@ namespace AspNetCoreModule.Test if (unZipContent) { var inputStream = await response.Content.ReadAsStreamAsync(); - var outputStream = new MemoryStream(); + + // for debugging purpose + //byte[] temp = new byte[inputStream.Length]; + //inputStream.Read(temp, 0, (int) inputStream.Length); + //inputStream.Position = 0; + using (var gzip = new GZipStream(inputStream, CompressionMode.Decompress)) { - await gzip.CopyToAsync(outputStream); + var outputStream = new MemoryStream(); + try + { + await gzip.CopyToAsync(outputStream); + } + catch (Exception ex) + { + // Even though "Vary" response header exists, the content is not actually compressed. + // We should ignore this execption until we find a proper way to determine if the body is compressed or not. + if (ex.Message.IndexOf("gzip", StringComparison.InvariantCultureIgnoreCase) >= 0) + { + result = await response.Content.ReadAsStringAsync(); + return result; + } + throw ex; + } gzip.Close(); inputStream.Close(); outputStream.Position = 0; @@ -1414,7 +1606,8 @@ namespace AspNetCoreModule.Test string responseStatus = "NotInitialized"; var httpClientHandler = new HttpClientHandler(); - httpClientHandler.UseDefaultCredentials = true; + httpClientHandler.UseDefaultCredentials = true; + httpClientHandler.AutomaticDecompression = DecompressionMethods.None; var httpClient = new HttpClient(httpClientHandler) { diff --git a/test/AspNetCoreModule.Test/app.config b/test/AspNetCoreModule.Test/app.config index d36d43e498..49e0f8825c 100644 --- a/test/AspNetCoreModule.Test/app.config +++ b/test/AspNetCoreModule.Test/app.config @@ -26,6 +26,14 @@ + + + + + + + + - + diff --git a/test/AspNetCoreModule.Test/aspnetcoremodule.test.csproj b/test/AspNetCoreModule.Test/aspnetcoremodule.test.csproj index 9da583a52b..4545c18afb 100644 --- a/test/AspNetCoreModule.Test/aspnetcoremodule.test.csproj +++ b/test/AspNetCoreModule.Test/aspnetcoremodule.test.csproj @@ -6,12 +6,15 @@ true true + + + + PreserveNewest - @@ -32,7 +35,6 @@ - @@ -41,6 +43,10 @@ + + + + diff --git a/test/stresstestwebroot/app_offline.htm b/test/stresstestwebroot/app_offline.htm new file mode 100644 index 0000000000..30d74d2584 --- /dev/null +++ b/test/stresstestwebroot/app_offline.htm @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/test/stresstestwebroot/web.config b/test/stresstestwebroot/web.config new file mode 100644 index 0000000000..a6fc3cdcf5 --- /dev/null +++ b/test/stresstestwebroot/web.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/tools/certificate.ps1 b/tools/certificate.ps1 index 1a19029ad9..c5a6fce639 100644 --- a/tools/certificate.ps1 +++ b/tools/certificate.ps1 @@ -26,7 +26,7 @@ ("Result: $thumbPrint2") .\certificate.ps1 -Command Export-CertificateTo -TargetThumbPrint $thumbPrint2 -TargetSSLStore "Cert:\LocalMachine\My" -ExportToSSLStore "Cert:\CurrentUser\My" - .\certificate.ps1 -Command Export-CertificateTo -TargetThumbPrint F97AB75DCF1C62547E4B5E7025D60001892A6A60 -ExportToSSLStore C:\gitroot\AspNetCoreModule\tools\test.pfx -PfxPassword test + .\certificate.ps1 -Command Export-CertificateTo -TargetThumbPrint $thumbPrint2 -TargetSSLStore "Cert:\LocalMachine\My" -ExportToSSLStore C:\gitroot\AspNetCoreModule\tools\test.pfx -PfxPassword test # Clean up @@ -238,6 +238,17 @@ function Export-CertificateTo($_targetThumbPrint, $_exportToSSLStore, $_password Remove-Item $tempExportFile -Force -Confirm:$false } + $isThisWin7 = $false + $exportToSSLStoreName = $null + $exportToSSLStoreLocation = $null + $targetSSLStoreName = $null + $targetSSLStoreLocation = $null + + if ((Get-Command Export-Certificate 2> out-null) -eq $null) + { + $isThisWin7 = $true + } + # if _exportToSSLStore points to a .pfx file if ($exportToSSLStore.ToLower().EndsWith(".pfx")) { @@ -246,33 +257,123 @@ function Export-CertificateTo($_targetThumbPrint, $_exportToSSLStore, $_password return ("Error!!! _password is required") } - $securedPassword = ConvertTo-SecureString -String $_password -Force –AsPlainText - $exportedPfxFile = Export-PfxCertificate -FilePath $_exportToSSLStore -Cert $TargetSSLStore\$_targetThumbPrint -Password $securedPassword - if ( ($exportedPfxFile -ne $null) -and (Test-Path $exportedPfxFile.FullName) ) - { - # Succeeded to export to .pfx file - return + if ($isThisWin7) + { + if ($TargetSSLStore.ToLower().Contains("my")) + { + $targetSSLStoreName = "My" + } + elseif ($_exportToSSLStore.ToLower().Contains("root")) + { + $targetSSLStoreName = "Root" + } + else + { + throw ("Unsupported store name " + $TargetSSLStore) + } + if ($TargetSSLStore.ToLower().Contains("localmachine")) + { + $targetSSLStoreLocation = "LocalMachine" + } + else + { + throw ("Unsupported store location name " + $TargetSSLStore) + } + + &certutil.exe @('-exportpfx', '-p', $_password, $targetSSLStoreName, $_targetThumbPrint, $_exportToSSLStore) | out-null + + if ( Test-Path $_exportToSSLStore ) + { + # Succeeded to export to .pfx file + return + } + else + { + return ("Error!!! Can't export $TargetSSLStore\$_targetThumbPrint to $tempExportFile") + } } else + { + $securedPassword = ConvertTo-SecureString -String $_password -Force –AsPlainText + $exportedPfxFile = Export-PfxCertificate -FilePath $_exportToSSLStore -Cert $TargetSSLStore\$_targetThumbPrint -Password $securedPassword + if ( ($exportedPfxFile -ne $null) -and (Test-Path $exportedPfxFile.FullName) ) + { + # Succeeded to export to .pfx file + return + } + else + { + return ("Error!!! Can't export $TargetSSLStore\$_targetThumbPrint to $tempExportFile") + } + } + } + + if ($isThisWin7) + { + # Initialize variables for Win7 + if ($_exportToSSLStore.ToLower().Contains("my")) + { + $exportToSSLStoreName = [System.Security.Cryptography.X509Certificates.StoreName]::My + } + elseif ($_exportToSSLStore.ToLower().Contains("root")) + { + $exportToSSLStoreName = [System.Security.Cryptography.X509Certificates.StoreName]::Root + } + else + { + throw ("Unsupported store name " + $_exportToSSLStore) + } + if ($_exportToSSLStore.ToLower().Contains("localmachine")) + { + $exportToSSLStoreLocation = [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine + } + elseif ($_exportToSSLStore.ToLower().Contains("currentuser")) + { + $exportToSSLStoreLocation = [System.Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser + } + else + { + throw ("Unsupported store location name " + $_exportToSSLStore) + } + + # Export-Certificate is not available. + $isThisWin7 = $true + $certificate = Get-Item "$TargetSSLStore\$_targetThumbPrint" + $base64certificate = @" +-----BEGIN CERTIFICATE----- +$([Convert]::ToBase64String($certificate.Export('Cert'), [System.Base64FormattingOptions]::InsertLineBreaks))) +-----END CERTIFICATE----- +"@ + Set-Content -Path $tempExportFile -Value $base64certificate | Out-Null + } + else + { + Export-Certificate -Cert $cert -FilePath $tempExportFile | Out-Null + if (-not (Test-Path $tempExportFile)) { return ("Error!!! Can't export $TargetSSLStore\$_targetThumbPrint to $tempExportFile") } } - - Export-Certificate -Cert $cert -FilePath $tempExportFile | Out-Null - if (-not (Test-Path $tempExportFile)) + + if ($isThisWin7) { - return ("Error!!! Can't export $TargetSSLStore\$_targetThumbPrint to $tempExportFile") + [Reflection.Assembly]::Load("System.Security, Version=2.0.0.0, Culture=Neutral, PublicKeyToken=b03f5f7f11d50a3a") | Out-Null + $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($tempExportFile) + $store = New-Object System.Security.Cryptography.X509Certificates.X509Store($exportToSSLStoreName,$exportToSSLStoreLocation) + $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite) | Out-Null + $store.Add($cert) | Out-Null + } + else + { + # clean up destination SSL store + Delete-Certificate $_targetThumbPrint $_exportToSSLStore + if (Test-Path "$_exportToSSLStore\$_targetThumbPrint") + { + return ("Error!!! Can't delete already existing one $_exportToSSLStore\$_targetThumbPrint") + } + Import-Certificate -CertStoreLocation $_exportToSSLStore -FilePath $tempExportFile | Out-Null } - # clean up destination SSL store - Delete-Certificate $_targetThumbPrint $_exportToSSLStore - if (Test-Path "$_exportToSSLStore\$_targetThumbPrint") - { - return ("Error!!! Can't delete already existing one $_exportToSSLStore\$_targetThumbPrint") - } - - Import-Certificate -CertStoreLocation $_exportToSSLStore -FilePath $tempExportFile | Out-Null if (-not (Test-Path "$_exportToSSLStore\$_targetThumbPrint")) { return ("Error!!! Can't copy $TargetSSLStore\$_targetThumbPrint to $_exportToSSLStore") diff --git a/tools/stresstest.ps1 b/tools/stresstest.ps1 new file mode 100644 index 0000000000..981c6fcf44 --- /dev/null +++ b/tools/stresstest.ps1 @@ -0,0 +1,96 @@ +########################################################## +# NOTE: +# For running test automation, following prerequisite required: +# +# 1. On Win7, powershell should be upgraded to 4.0 +# https://social.technet.microsoft.com/wiki/contents/articles/21016.how-to-install-windows-powershell-4-0.aspx +# 2. url-rewrite should be installed +# 3. makecert.exe tools should be available +########################################################## + +# Replace aspnetcore.dll with the latest version +copy C:\gitroot\AspNetCoreModule\artifacts\build\AspNetCore\bin\Release\x64\aspnetcore.dll "C:\Program Files\IIS Express" +copy C:\gitroot\AspNetCoreModule\artifacts\build\AspNetCore\bin\Release\x64\aspnetcore.pdb "C:\Program Files\IIS Express" + + +# Enable appverif for IISExpress.exe +appverif /verify iisexpress.exe + +# Set the AspNetCoreModuleTest environment variable with the following command +cd C:\gitroot\AspNetCoreModule\test\AspNetCoreModule.Test +dotnet restore +dotnet build +$aspNetCoreModuleTest="C:\gitroot\AspNetCoreModule\test\AspNetCoreModule.Test\bin\Debug\net46" + +if (Test-Path (Join-Path $aspNetCoreModuleTest aspnetcoremodule.test.dll)) +{ + # Clean up applicationhost.config of IISExpress + del $env:userprofile\documents\iisexpress\config\applicationhost.config -Confirm:$false -Force + Start-Process "C:\Program Files\IIS Express\iisexpress.exe" + Sleep 3 + Stop-Process -Name iisexpress + + # Create sites + (1..50) | foreach { md ("C:\inetpub\wwwroot\AspnetCoreHandler_HelloWeb\foo" + $_ ) 2> out-null } + (1..50) | foreach { copy C:\gitroot\AspNetCoreModule\test\StressTestWebRoot\web.config ("C:\inetpub\wwwroot\AspnetCoreHandler_HelloWeb\foo" + $_ ) } + (1..50) | foreach { + $path = ("C:\inetpub\wwwroot\AspnetCoreHandler_HelloWeb\foo" + $_ ) + $appPath = "/foo"+$_ + & "C:\Program Files\IIS Express\appcmd.exe" add app /site.name:"WebSite1" /path:$appPath /physicalPath:$path + } + + <#(1..50) | foreach { + $configpath = ("WebSite1/foo" + $_) + $value = "C:\inetpub\wwwroot\AspnetCoreHandler_HelloWeb\foo" + $_ + ".exe" + & "C:\Program Files\IIS Express\appcmd.exe" set config $configpath -section:system.webServer/aspNetCore /processPath:$value + } + (1..50) | foreach { copy C:\inetpub\wwwroot\AspnetCoreHandler_HelloWeb\foo.exe ("C:\inetpub\wwwroot\AspnetCoreHandler_HelloWeb\foo" + $_ +".exe") } + (1..50) | foreach { + $configpath = ("WebSite1/foo" + $_) + $value = "%AspNetCoreModuleTest%\AspnetCoreApp_HelloWeb\foo" + $_ + ".exe" + & "C:\Program Files\IIS Express\appcmd.exe" set config $configpath -section:system.webServer/aspNetCore /processPath:$value /apphostconfig:%AspNetCoreModuleTest%\config\applicationhost.config + + $value = "%AspNetCoreModuleTest%\AspnetCoreApp_HelloWeb\AutobahnTestServer.dll" + & "C:\Program Files\IIS Express\appcmd.exe" set config $configpath -section:system.webServer/aspNetCore /arguments:$value /apphostconfig:%AspNetCoreModuleTest%\config\applicationhost.config + } + #> + + # Start IISExpress with running the below command + &"C:\Program Files\Debugging Tools for Windows (x64)\windbg.exe" /g /G "C:\Program Files\IIS Express\iisexpress.exe" + + + # 6. Start stress testing + (1..10000) | foreach { + if ($_ % 2 -eq 0) + { + ("Recycling backend only") + stop-process -name dotnet + (1..50) | foreach { del ("C:\inetpub\wwwroot\AspnetCoreHandler_HelloWeb\foo" + $_ + "\app_offline.htm") -confirm:$false -Force 2> out-null } + stop-process -name dotnet + } + else + { + ("Recycling backedn + enabling appoffline ....") + stop-process -name dotnet + (1..50) | foreach { copy C:\gitroot\AspNetCoreModule\test\StressTestWebRoot\app_offline.htm ("C:\inetpub\wwwroot\AspnetCoreHandler_HelloWeb\foo" + $_ ) } + } + Sleep 1 + + (1..10) | foreach { + (1..50) | foreach { + invoke-webrequest ("http://localhost:8080/foo"+$_) > $null + } + } + } + + + # Stress test idea + # 1. Use Web Stress Tester + # 2. Run stop-process -name dotnet + # 3. Hit Q command to IISExpress console window + # 4. Use app_offline.htm + # 5. Save dummy web.config +} + +// bp aspnetcore!FORWARDING_HANDLER::FORWARDING_HANDLER +// bp aspnetcore!FORWARDING_HANDLER::~FORWARDING_HANDLER \ No newline at end of file