1727 lines
91 KiB
C#
1727 lines
91 KiB
C#
// Copyright (c) .NET Foundation. All rights reserved.
|
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
|
|
|
using AspNetCoreModule.Test.Framework;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net.Http;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.Net.Http.Headers;
|
|
using Xunit;
|
|
using Xunit.Sdk;
|
|
using System.Diagnostics;
|
|
using System.Net;
|
|
using System.Threading;
|
|
using AspNetCoreModule.Test.WebSocketClient;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Security.Principal;
|
|
using System.IO.Compression;
|
|
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
|
|
{
|
|
ResponseBody,
|
|
ResponseBodyAndHeaders,
|
|
ResponseStatus,
|
|
None
|
|
}
|
|
|
|
public static async Task DoBasicTest(IISConfigUtility.AppPoolBitness appPoolBitness)
|
|
{
|
|
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();
|
|
var httpClient = new HttpClient(httpClientHandler)
|
|
{
|
|
BaseAddress = testSite.AspNetCoreApp.GetUri(),
|
|
Timeout = TimeSpan.FromSeconds(5),
|
|
};
|
|
|
|
// Invoke given test scenario function
|
|
await CheckChunkedAsync(httpClient, testSite.AspNetCoreApp);
|
|
}
|
|
}
|
|
|
|
public static async Task DoRecycleApplicationAfterBackendProcessBeingKilled(IISConfigUtility.AppPoolBitness appPoolBitness)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoRecycleApplicationAfterBackendProcessBeingKilled"))
|
|
{
|
|
string backendProcessId_old = null;
|
|
const int repeatCount = 3;
|
|
for (int i = 0; i < repeatCount; i++)
|
|
{
|
|
// check JitDebugger before continuing
|
|
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
|
|
DateTime startTime = DateTime.Now;
|
|
Thread.Sleep(1000);
|
|
|
|
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
|
|
Assert.NotEqual(backendProcessId_old, backendProcessId);
|
|
backendProcessId_old = backendProcessId;
|
|
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
|
|
Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", ""));
|
|
|
|
Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId));
|
|
backendProcess.Kill();
|
|
Thread.Sleep(500);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static async Task DoRecycleApplicationAfterW3WPProcessBeingKilled(IISConfigUtility.AppPoolBitness appPoolBitness)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoRecycleApplicationAfterW3WPProcessBeingKilled"))
|
|
{
|
|
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++)
|
|
{
|
|
// check JitDebugger before continuing
|
|
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
|
|
DateTime startTime = DateTime.Now;
|
|
Thread.Sleep(1000);
|
|
|
|
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
|
|
Assert.NotEqual(backendProcessId_old, backendProcessId);
|
|
backendProcessId_old = backendProcessId;
|
|
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
|
|
Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", ""));
|
|
Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId));
|
|
|
|
// get process id of IIS worker process (w3wp.exe)
|
|
string userName = testSite.SiteName;
|
|
int processIdOfWorkerProcess = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("w3wp.exe", "Handle", userName));
|
|
var workerProcess = Process.GetProcessById(Convert.ToInt32(processIdOfWorkerProcess));
|
|
workerProcess.Kill();
|
|
|
|
Thread.Sleep(500);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static async Task DoRecycleApplicationAfterWebConfigUpdated(IISConfigUtility.AppPoolBitness appPoolBitness)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoRecycleApplicationAfterWebConfigUpdated"))
|
|
{
|
|
string backendProcessId_old = null;
|
|
const int repeatCount = 3;
|
|
for (int i = 0; i < repeatCount; i++)
|
|
{
|
|
// check JitDebugger before continuing
|
|
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
|
|
DateTime startTime = DateTime.Now;
|
|
Thread.Sleep(1000);
|
|
|
|
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
|
|
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
|
|
Assert.NotEqual(backendProcessId_old, backendProcessId);
|
|
backendProcessId_old = backendProcessId;
|
|
Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", ""));
|
|
Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId));
|
|
testSite.AspNetCoreApp.MoveFile("web.config", "_web.config");
|
|
Thread.Sleep(500);
|
|
testSite.AspNetCoreApp.MoveFile("_web.config", "web.config");
|
|
}
|
|
|
|
// restore web.config
|
|
testSite.AspNetCoreApp.RestoreFile("web.config");
|
|
|
|
}
|
|
}
|
|
|
|
public static async Task DoRecycleApplicationWithURLRewrite(IISConfigUtility.AppPoolBitness appPoolBitness)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoRecycleApplicationWithURLRewrite"))
|
|
{
|
|
string backendProcessId_old = null;
|
|
const int repeatCount = 3;
|
|
for (int i = 0; i < repeatCount; i++)
|
|
{
|
|
// check JitDebugger before continuing
|
|
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
|
|
DateTime startTime = DateTime.Now;
|
|
Thread.Sleep(500);
|
|
|
|
string urlForUrlRewrite = testSite.URLRewriteApp.URL + "/Rewrite2/" + testSite.AspNetCoreApp.URL + "/GetProcessId";
|
|
string backendProcessId = await GetResponse(testSite.RootAppContext.GetUri(urlForUrlRewrite), HttpStatusCode.OK);
|
|
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
|
|
Assert.NotEqual(backendProcessId_old, backendProcessId);
|
|
backendProcessId_old = backendProcessId;
|
|
Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", ""));
|
|
Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId));
|
|
|
|
testSite.AspNetCoreApp.MoveFile("web.config", "_web.config");
|
|
Thread.Sleep(500);
|
|
testSite.AspNetCoreApp.MoveFile("_web.config", "web.config");
|
|
}
|
|
|
|
// restore web.config
|
|
testSite.AspNetCoreApp.RestoreFile("web.config");
|
|
|
|
}
|
|
}
|
|
|
|
public static async Task DoRecycleParentApplicationWithURLRewrite(IISConfigUtility.AppPoolBitness appPoolBitness)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoRecycleParentApplicationWithURLRewrite"))
|
|
{
|
|
string backendProcessId_old = null;
|
|
const int repeatCount = 3;
|
|
for (int i = 0; i < repeatCount; i++)
|
|
{
|
|
// check JitDebugger before continuing
|
|
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
|
|
DateTime startTime = DateTime.Now;
|
|
Thread.Sleep(1000);
|
|
|
|
string urlForUrlRewrite = testSite.URLRewriteApp.URL + "/Rewrite2/" + testSite.AspNetCoreApp.URL + "/GetProcessId";
|
|
string backendProcessId = await GetResponse(testSite.RootAppContext.GetUri(urlForUrlRewrite), HttpStatusCode.OK);
|
|
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
|
|
Assert.NotEqual(backendProcessId_old, backendProcessId);
|
|
backendProcessId_old = backendProcessId;
|
|
Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", ""));
|
|
Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId));
|
|
testSite.RootAppContext.MoveFile("web.config", "_web.config");
|
|
Thread.Sleep(500);
|
|
testSite.RootAppContext.MoveFile("_web.config", "web.config");
|
|
}
|
|
|
|
// restore web.config
|
|
testSite.RootAppContext.RestoreFile("web.config");
|
|
}
|
|
}
|
|
|
|
public static async Task DoEnvironmentVariablesTest(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(testSite.IisServerType, testSite.IisExpressConfigPath))
|
|
{
|
|
DateTime startTime = DateTime.Now;
|
|
Thread.Sleep(500);
|
|
|
|
string totalNumber = await GetResponse(testSite.AspNetCoreApp.GetUri("GetEnvironmentVariables"), HttpStatusCode.OK);
|
|
Assert.True(totalNumber == (await GetResponse(testSite.AspNetCoreApp.GetUri("GetEnvironmentVariables"), HttpStatusCode.OK)));
|
|
|
|
iisConfig.SetANCMConfig(
|
|
testSite.SiteName,
|
|
testSite.AspNetCoreApp.Name,
|
|
"environmentVariable",
|
|
new string[] { "ANCMTestFoo", "foo" }
|
|
);
|
|
|
|
Thread.Sleep(500);
|
|
|
|
// check JitDebugger before continuing
|
|
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
|
|
int expectedValue = Convert.ToInt32(totalNumber) + 1;
|
|
string totalResult = (await GetResponse(testSite.AspNetCoreApp.GetUri("GetEnvironmentVariables"), HttpStatusCode.OK));
|
|
Assert.True(expectedValue.ToString() == (await GetResponse(testSite.AspNetCoreApp.GetUri("GetEnvironmentVariables"), HttpStatusCode.OK)));
|
|
|
|
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);
|
|
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(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"))
|
|
{
|
|
string backendProcessId_old = null;
|
|
string fileContent = "BackEndAppOffline";
|
|
testSite.AspNetCoreApp.CreateFile(new string[] { fileContent }, "App_Offline.Htm");
|
|
|
|
for (int i = 0; i < _repeatCount; i++)
|
|
{
|
|
// check JitDebugger before continuing
|
|
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
|
|
DateTime startTime = DateTime.Now;
|
|
Thread.Sleep(1100);
|
|
|
|
// verify 503
|
|
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), fileContent + "\r\n", HttpStatusCode.ServiceUnavailable);
|
|
|
|
// rename app_offline.htm to _app_offline.htm and verify 200
|
|
testSite.AspNetCoreApp.MoveFile("App_Offline.Htm", "_App_Offline.Htm");
|
|
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
|
|
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
|
|
Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", ""));
|
|
Assert.NotEqual(backendProcessId_old, backendProcessId);
|
|
backendProcessId_old = backendProcessId;
|
|
Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId));
|
|
|
|
// rename back to app_offline.htm
|
|
testSite.AspNetCoreApp.MoveFile("_App_Offline.Htm", "App_Offline.Htm");
|
|
}
|
|
}
|
|
}
|
|
|
|
public static async Task DoAppOfflineTestWithUrlRewriteAndDeleting(IISConfigUtility.AppPoolBitness appPoolBitness)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoAppOfflineTestWithUrlRewriteAndDeleting"))
|
|
{
|
|
string backendProcessId_old = null;
|
|
string fileContent = "BackEndAppOffline2";
|
|
testSite.AspNetCoreApp.CreateFile(new string[] { fileContent }, "App_Offline.Htm");
|
|
|
|
for (int i = 0; i < _repeatCount; i++)
|
|
{
|
|
// check JitDebugger before continuing
|
|
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
|
|
DateTime startTime = DateTime.Now;
|
|
Thread.Sleep(500);
|
|
|
|
// verify 503
|
|
string urlForUrlRewrite = testSite.URLRewriteApp.URL + "/Rewrite2/" + testSite.AspNetCoreApp.URL + "/GetProcessId";
|
|
await VerifyResponseBody(testSite.RootAppContext.GetUri(urlForUrlRewrite), fileContent + "\r\n", HttpStatusCode.ServiceUnavailable);
|
|
|
|
// delete app_offline.htm and verify 200
|
|
testSite.AspNetCoreApp.DeleteFile("App_Offline.Htm");
|
|
string backendProcessId = await GetResponse(testSite.RootAppContext.GetUri(urlForUrlRewrite), HttpStatusCode.OK);
|
|
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
|
|
Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", ""));
|
|
Assert.NotEqual(backendProcessId_old, backendProcessId);
|
|
backendProcessId_old = backendProcessId;
|
|
Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId));
|
|
|
|
// create app_offline.htm again
|
|
testSite.AspNetCoreApp.CreateFile(new string[] { fileContent }, "App_Offline.Htm");
|
|
}
|
|
}
|
|
}
|
|
|
|
public static async Task DoPostMethodTest(IISConfigUtility.AppPoolBitness appPoolBitness, string testData)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoPostMethodTest"))
|
|
{
|
|
var postFormData = new[]
|
|
{
|
|
new KeyValuePair<string, string>("FirstName", "Mickey"),
|
|
new KeyValuePair<string, string>("LastName", "Mouse"),
|
|
new KeyValuePair<string, string>("TestData", testData),
|
|
};
|
|
var expectedResponseBody = "FirstName=Mickey&LastName=Mouse&TestData=" + testData;
|
|
await VerifyPostResponseBody(testSite.AspNetCoreApp.GetUri("EchoPostData"), postFormData, expectedResponseBody, HttpStatusCode.OK);
|
|
}
|
|
}
|
|
|
|
public static async Task DoDisableStartUpErrorPageTest(IISConfigUtility.AppPoolBitness appPoolBitness)
|
|
{
|
|
int errorEventId = 1000;
|
|
string errorMessageContainThis = "bogus"; // bogus path value to cause 502.3 error
|
|
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoDisableStartUpErrorPageTest"))
|
|
{
|
|
testSite.AspNetCoreApp.DeleteFile("custom502-3.htm");
|
|
string curstomErrorMessage = "ANCMTest502-3";
|
|
testSite.AspNetCoreApp.CreateFile(new string[] { curstomErrorMessage }, "custom502-3.htm");
|
|
|
|
Thread.Sleep(500);
|
|
|
|
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
|
|
{
|
|
DateTime startTime = DateTime.Now;
|
|
Thread.Sleep(500);
|
|
|
|
iisConfig.ConfigureCustomLogging(testSite.SiteName, testSite.AspNetCoreApp.Name, 502, 3, "custom502-3.htm");
|
|
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "disableStartUpErrorPage", true);
|
|
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "processPath", errorMessageContainThis);
|
|
|
|
var responseBody = await GetResponse(testSite.AspNetCoreApp.GetUri(), HttpStatusCode.BadGateway);
|
|
responseBody = responseBody.Replace("\r", "").Replace("\n", "").Trim();
|
|
Assert.True(responseBody == curstomErrorMessage);
|
|
|
|
// verify event error log
|
|
Assert.True(TestUtility.RetryHelper((arg1, arg2, arg3) => VerifyApplicationEventLog(arg1, arg2, arg3), errorEventId, startTime, errorMessageContainThis));
|
|
|
|
// try again after setting "false" value
|
|
startTime = DateTime.Now;
|
|
Thread.Sleep(500);
|
|
|
|
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "disableStartUpErrorPage", false);
|
|
Thread.Sleep(500);
|
|
|
|
// check JitDebugger before continuing
|
|
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
|
|
responseBody = await GetResponse(testSite.AspNetCoreApp.GetUri(), HttpStatusCode.BadGateway);
|
|
Assert.True(responseBody.Contains("808681"));
|
|
|
|
// verify event error log
|
|
Assert.True(TestUtility.RetryHelper((arg1, arg2, arg3) => VerifyApplicationEventLog(arg1, arg2, arg3), errorEventId, startTime, errorMessageContainThis));
|
|
}
|
|
testSite.AspNetCoreApp.RestoreFile("web.config");
|
|
}
|
|
}
|
|
|
|
public static async Task DoRapidFailsPerMinuteTest(IISConfigUtility.AppPoolBitness appPoolBitness, int valueOfRapidFailsPerMinute)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoRapidFailsPerMinuteTest"))
|
|
{
|
|
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
|
|
{
|
|
bool rapidFailsTriggered = false;
|
|
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "rapidFailsPerMinute", valueOfRapidFailsPerMinute);
|
|
|
|
string backendProcessId_old = null;
|
|
const int repeatCount = 10;
|
|
|
|
DateTime startTime = DateTime.Now;
|
|
Thread.Sleep(50);
|
|
|
|
for (int i = 0; i < repeatCount; i++)
|
|
{
|
|
// check JitDebugger before continuing
|
|
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
|
|
DateTime startTimeInsideLooping = DateTime.Now;
|
|
Thread.Sleep(50);
|
|
|
|
var statusCode = await GetResponseStatusCode(testSite.AspNetCoreApp.GetUri("GetProcessId"));
|
|
if (statusCode != HttpStatusCode.OK.ToString())
|
|
{
|
|
Assert.True(i >= valueOfRapidFailsPerMinute, i.ToString() + "is greater than or equals to " + valueOfRapidFailsPerMinute.ToString());
|
|
Assert.True(i < valueOfRapidFailsPerMinute + 3, i.ToString() + "is less than " + (valueOfRapidFailsPerMinute + 3).ToString());
|
|
rapidFailsTriggered = true;
|
|
break;
|
|
}
|
|
|
|
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
|
|
Assert.NotEqual(backendProcessId_old, backendProcessId);
|
|
backendProcessId_old = backendProcessId;
|
|
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
|
|
Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", ""));
|
|
|
|
//Verifying EventID of new backend process is not necesssary and removed in order to fix some test reliablity issues
|
|
//Thread.Sleep(3000);
|
|
//Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTimeInsideLooping, backendProcessId), "Verifying event log of new backend process id " + backendProcessId);
|
|
|
|
backendProcess.Kill();
|
|
Thread.Sleep(3000);
|
|
}
|
|
Assert.True(rapidFailsTriggered, "Verify 503 error");
|
|
|
|
// verify event error log
|
|
int errorEventId = 1003;
|
|
string errorMessageContainThis = "'" + valueOfRapidFailsPerMinute + "'"; // part of error message
|
|
Assert.True(TestUtility.RetryHelper((arg1, arg2, arg3) => VerifyApplicationEventLog(arg1, arg2, arg3), errorEventId, startTime, errorMessageContainThis));
|
|
}
|
|
testSite.AspNetCoreApp.RestoreFile("web.config");
|
|
}
|
|
}
|
|
|
|
public static async Task DoProcessesPerApplicationTest(IISConfigUtility.AppPoolBitness appPoolBitness, int valueOfProcessesPerApplication)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoProcessesPerApplicationTest"))
|
|
{
|
|
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
|
|
{
|
|
DateTime startTime = DateTime.Now;
|
|
Thread.Sleep(3000);
|
|
|
|
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "processesPerApplication", valueOfProcessesPerApplication);
|
|
HashSet<int> processIDs = new HashSet<int>();
|
|
|
|
for (int i = 0; i < 20; i++)
|
|
{
|
|
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
|
|
int id = Convert.ToInt32(backendProcessId);
|
|
if (!processIDs.Contains(id))
|
|
{
|
|
processIDs.Add(id);
|
|
}
|
|
|
|
if (i == (valueOfProcessesPerApplication - 1))
|
|
{
|
|
Assert.Equal(valueOfProcessesPerApplication, processIDs.Count);
|
|
}
|
|
}
|
|
|
|
Assert.Equal(valueOfProcessesPerApplication, processIDs.Count);
|
|
|
|
foreach (var id in processIDs)
|
|
{
|
|
var backendProcess = Process.GetProcessById(id);
|
|
Assert.Equal(backendProcess.ProcessName.ToLower().Replace(".exe", ""), testSite.AspNetCoreApp.GetProcessFileName().ToLower().Replace(".exe", ""));
|
|
Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, id.ToString()));
|
|
}
|
|
|
|
// reset the value with 1 again
|
|
processIDs = new HashSet<int>();
|
|
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "processesPerApplication", 1);
|
|
Thread.Sleep(3000);
|
|
|
|
// check JitDebugger before continuing
|
|
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
Thread.Sleep(500);
|
|
|
|
for (int i = 0; i < 20; i++)
|
|
{
|
|
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
|
|
int id = Convert.ToInt32(backendProcessId);
|
|
if (!processIDs.Contains(id))
|
|
{
|
|
processIDs.Add(id);
|
|
}
|
|
}
|
|
Assert.Equal(1, processIDs.Count);
|
|
}
|
|
|
|
testSite.AspNetCoreApp.RestoreFile("web.config");
|
|
}
|
|
}
|
|
|
|
public static async Task DoStartupTimeLimitTest(IISConfigUtility.AppPoolBitness appPoolBitness, int startupTimeLimit)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoStartupTimeLimitTest"))
|
|
{
|
|
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
|
|
{
|
|
int startupDelay = 3; //3 seconds
|
|
iisConfig.SetANCMConfig(
|
|
testSite.SiteName,
|
|
testSite.AspNetCoreApp.Name,
|
|
"environmentVariable",
|
|
new string[] { "ANCMTestStartUpDelay", (startupDelay * 1000).ToString() }
|
|
);
|
|
|
|
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "requestTimeout", TimeSpan.Parse("00:01:00")); // 1 minute
|
|
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "startupTimeLimit", startupTimeLimit);
|
|
|
|
Thread.Sleep(500);
|
|
if (startupTimeLimit < startupDelay)
|
|
{
|
|
await VerifyResponseStatus(testSite.AspNetCoreApp.GetUri("DoSleep3000"), HttpStatusCode.BadGateway);
|
|
}
|
|
else
|
|
{
|
|
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri("DoSleep3000"), "Running", HttpStatusCode.OK);
|
|
}
|
|
}
|
|
testSite.AspNetCoreApp.RestoreFile("web.config");
|
|
}
|
|
}
|
|
|
|
public static async Task DoRequestTimeoutTest(IISConfigUtility.AppPoolBitness appPoolBitness, string requestTimeout)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoRequestTimeoutTest"))
|
|
{
|
|
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
|
|
{
|
|
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "requestTimeout", TimeSpan.Parse(requestTimeout));
|
|
Thread.Sleep(500);
|
|
|
|
if (requestTimeout.ToString() == "00:02:00")
|
|
{
|
|
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri("DoSleep65000"), "Running", HttpStatusCode.OK, timeout:70);
|
|
}
|
|
else if (requestTimeout.ToString() == "00:01:00")
|
|
{
|
|
await VerifyResponseStatus(testSite.AspNetCoreApp.GetUri("DoSleep65000"), HttpStatusCode.BadGateway, 70);
|
|
}
|
|
else
|
|
{
|
|
throw new System.ApplicationException("wrong data");
|
|
}
|
|
}
|
|
testSite.AspNetCoreApp.RestoreFile("web.config");
|
|
}
|
|
}
|
|
|
|
public static async Task DoShutdownTimeLimitTest(IISConfigUtility.AppPoolBitness appPoolBitness, int valueOfshutdownTimeLimit, int expectedClosingTime)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoShutdownTimeLimitTest"))
|
|
{
|
|
using (var iisConfig = new IISConfigUtility(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);
|
|
iisConfig.SetANCMConfig(
|
|
testSite.SiteName,
|
|
testSite.AspNetCoreApp.Name,
|
|
"environmentVariable",
|
|
new string[] { "ANCMTestShutdownDelay", "20000" }
|
|
);
|
|
|
|
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
|
|
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
|
|
var backendProcess = Process.GetProcessById(Convert.ToInt32(backendProcessId));
|
|
|
|
// Set a new value such as 100 to make the backend process being recycled
|
|
DateTime startTime = DateTime.Now;
|
|
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "shutdownTimeLimit", 100);
|
|
backendProcess.WaitForExit(30000);
|
|
DateTime endTime = DateTime.Now;
|
|
var difference = endTime - startTime;
|
|
Assert.True(difference.Seconds >= expectedClosingTime);
|
|
Assert.True(difference.Seconds < expectedClosingTime + 3);
|
|
Assert.True(backendProcessId != await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK));
|
|
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
|
|
}
|
|
|
|
testSite.AspNetCoreApp.RestoreFile("web.config");
|
|
}
|
|
}
|
|
public static async Task DoStdoutLogEnabledTest(IISConfigUtility.AppPoolBitness appPoolBitness)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoStdoutLogEnabledTest"))
|
|
{
|
|
testSite.AspNetCoreApp.DeleteDirectory("logs");
|
|
|
|
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
|
|
{
|
|
DateTime startTime = DateTime.Now;
|
|
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));
|
|
|
|
testSite.AspNetCoreApp.CreateDirectory("logs");
|
|
|
|
// verify the log file is not created because backend process is not recycled
|
|
Assert.True(Directory.GetFiles(logPath).Length == 0);
|
|
Assert.True(backendProcessId == (await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK)));
|
|
|
|
// reset web.config to recycle backend process and give write permission to the Users local group to which IIS workerprocess identity belongs
|
|
SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);
|
|
TestUtility.GiveWritePermissionTo(logPath, sid);
|
|
|
|
startTime = DateTime.Now;
|
|
Thread.Sleep(500);
|
|
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "stdoutLogEnabled", false);
|
|
|
|
// check JitDebugger before continuing
|
|
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
|
|
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "stdoutLogEnabled", true);
|
|
|
|
Assert.True(backendProcessId != (await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK)));
|
|
|
|
// Verify log file is created now after backend process is recycled
|
|
Assert.True(TestUtility.RetryHelper(p => { return Directory.GetFiles(p).Length > 0 ? true : false; }, logPath));
|
|
}
|
|
|
|
testSite.AspNetCoreApp.RestoreFile("web.config");
|
|
}
|
|
}
|
|
|
|
public static async Task DoProcessPathAndArgumentsTest(IISConfigUtility.AppPoolBitness appPoolBitness, string processPath, string argumentsPrefix)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoProcessPathAndArgumentsTest", copyAllPublishedFiles:true))
|
|
{
|
|
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);
|
|
var tempBackendProcess = Process.GetProcessById(Convert.ToInt32(tempProcessId));
|
|
|
|
// replace $env with the actual test value
|
|
if (processPath == "$env")
|
|
{
|
|
string tempString = Environment.ExpandEnvironmentVariables("%systemdrive%").ToLower();
|
|
processPath = Path.Combine(tempBackendProcess.MainModule.FileName).ToLower().Replace(tempString, "%systemdrive%");
|
|
arguments = testSite.AspNetCoreApp.GetDirectoryPathWith(arguments).ToLower().Replace(tempString, "%systemdrive%");
|
|
}
|
|
|
|
DateTime startTime = DateTime.Now;
|
|
Thread.Sleep(500);
|
|
|
|
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "processPath", processPath);
|
|
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "arguments", arguments);
|
|
Thread.Sleep(500);
|
|
|
|
// check JitDebugger before continuing
|
|
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
Thread.Sleep(500);
|
|
|
|
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
|
|
Assert.True(TestUtility.RetryHelper((arg1, arg2) => VerifyANCMStartEvent(arg1, arg2), startTime, backendProcessId));
|
|
}
|
|
|
|
testSite.AspNetCoreApp.RestoreFile("web.config");
|
|
}
|
|
}
|
|
|
|
public static async Task DoForwardWindowsAuthTokenTest(IISConfigUtility.AppPoolBitness appPoolBitness, bool enabledForwardWindowsAuthToken)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoForwardWindowsAuthTokenTest"))
|
|
{
|
|
using (var iisConfig = new IISConfigUtility(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.EnableIISAuthentication(testSite.SiteName, windows:true, basic:false, anonymous:false);
|
|
Thread.Sleep(500);
|
|
|
|
// check JitDebugger before continuing
|
|
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
Thread.Sleep(500);
|
|
|
|
requestHeaders = await GetResponse(testSite.AspNetCoreApp.GetUri("DumpRequestHeaders"), HttpStatusCode.OK);
|
|
if (enabledForwardWindowsAuthToken)
|
|
{
|
|
string expectedHeaderName = "MS-ASPNETCORE-WINAUTHTOKEN";
|
|
Assert.True(requestHeaders.ToUpper().Contains(expectedHeaderName));
|
|
|
|
result = await GetResponse(testSite.AspNetCoreApp.GetUri("ImpersonateMiddleware"), HttpStatusCode.OK);
|
|
bool compare = false;
|
|
|
|
string expectedValue1 = "ImpersonateMiddleware-UserName = " + Environment.ExpandEnvironmentVariables("%USERDOMAIN%") + "\\" + Environment.ExpandEnvironmentVariables("%USERNAME%");
|
|
if (result.ToLower().Contains(expectedValue1.ToLower()))
|
|
{
|
|
compare = true;
|
|
}
|
|
|
|
string expectedValue2 = "ImpersonateMiddleware-UserName = " + Environment.ExpandEnvironmentVariables("%USERNAME%");
|
|
if (result.ToLower().Contains(expectedValue2.ToLower()))
|
|
{
|
|
compare = true;
|
|
}
|
|
|
|
Assert.True(compare);
|
|
}
|
|
else
|
|
{
|
|
Assert.False(requestHeaders.ToUpper().Contains("MS-ASPNETCORE-WINAUTHTOKEN"));
|
|
|
|
result = await GetResponse(testSite.AspNetCoreApp.GetUri("ImpersonateMiddleware"), HttpStatusCode.OK);
|
|
Assert.True(result.Contains("ImpersonateMiddleware-UserName = NoAuthentication"));
|
|
}
|
|
}
|
|
|
|
testSite.AspNetCoreApp.RestoreFile("web.config");
|
|
}
|
|
}
|
|
|
|
public static async Task DoRecylingAppPoolTest(IISConfigUtility.AppPoolBitness appPoolBitness)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoRecylingAppPoolTest"))
|
|
{
|
|
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
|
|
await VerifyResponseStatus(testSite.AspNetCoreApp.GetUri("MemoryLeak1024000"), HttpStatusCode.OK);
|
|
|
|
// get backend process id
|
|
string pocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
|
|
|
|
// get process id of IIS worker process (w3wp.exe)
|
|
string userName = testSite.SiteName;
|
|
int processIdOfWorkerProcess = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("w3wp.exe", "Handle", userName));
|
|
var workerProcess = Process.GetProcessById(Convert.ToInt32(processIdOfWorkerProcess));
|
|
var backendProcess = Process.GetProcessById(Convert.ToInt32(pocessIdBackendProcess));
|
|
|
|
var privateMemoryKB = workerProcess.PrivateMemorySize64 / 1024;
|
|
var virtualMemoryKB = workerProcess.VirtualMemorySize64 / 1024;
|
|
var privateMemoryKBBackend = backendProcess.PrivateMemorySize64 / 1024;
|
|
var virtualMemoryKBBackend = backendProcess.VirtualMemorySize64 / 1024;
|
|
var totalPrivateMemoryKB = privateMemoryKB + privateMemoryKBBackend;
|
|
var totalVirtualMemoryKB = virtualMemoryKB + virtualMemoryKBBackend;
|
|
|
|
// terminate backend process
|
|
backendProcess.Kill();
|
|
backendProcess.Dispose();
|
|
|
|
// terminate IIS worker process
|
|
workerProcess.Kill();
|
|
workerProcess.Dispose();
|
|
Thread.Sleep(3000);
|
|
|
|
// check JitDebugger before continuing
|
|
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
|
|
iisConfig.SetAppPoolSetting(testSite.AspNetCoreApp.AppPoolName, "privateMemory", totalPrivateMemoryKB);
|
|
|
|
// set 100 for rapidFailProtection counter for both IIS worker process and aspnetcore backend process
|
|
iisConfig.SetAppPoolSetting(testSite.AspNetCoreApp.AppPoolName, "rapidFailProtectionMaxCrashes", 100);
|
|
iisConfig.SetANCMConfig(testSite.SiteName, testSite.AspNetCoreApp.Name, "rapidFailsPerMinute", 100);
|
|
Thread.Sleep(3000);
|
|
|
|
await VerifyResponseStatus(testSite.RootAppContext.GetUri("small.htm"), HttpStatusCode.OK);
|
|
Thread.Sleep(1000);
|
|
int x = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("w3wp.exe", "Handle", userName));
|
|
|
|
// Verify that IIS recycling does not happen while there is no memory leak
|
|
bool foundVSJit = false;
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
// check JitDebugger before continuing
|
|
foundVSJit = TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
|
|
await VerifyResponseStatus(testSite.RootAppContext.GetUri("small.htm"), HttpStatusCode.OK);
|
|
Thread.Sleep(3000);
|
|
}
|
|
|
|
int y = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("w3wp.exe", "Handle", userName));
|
|
Assert.True(x == y && foundVSJit == false, "worker process is not recycled after 30 seconds");
|
|
|
|
string backupPocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
|
|
string newPocessIdBackendProcess = backupPocessIdBackendProcess;
|
|
|
|
// Verify IIS recycling happens while there is memory leak
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
// check JitDebugger before continuing
|
|
foundVSJit = TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
|
|
// allocating 2048,000 KB
|
|
await VerifyResponseStatus(testSite.AspNetCoreApp.GetUri("MemoryLeak2048000"), HttpStatusCode.OK);
|
|
|
|
newPocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
|
|
if (foundVSJit || backupPocessIdBackendProcess != newPocessIdBackendProcess)
|
|
{
|
|
// worker process is recycled expectedly and backend process is recycled together
|
|
break;
|
|
}
|
|
Thread.Sleep(3000);
|
|
}
|
|
// check JitDebugger before continuing
|
|
TestUtility.ResetHelper(ResetHelperMode.KillVSJitDebugger);
|
|
|
|
int z = 0;
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
z = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("w3wp.exe", "Handle", userName));
|
|
if (x != z)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
Thread.Sleep(1000);
|
|
}
|
|
}
|
|
z = Convert.ToInt32(TestUtility.GetProcessWMIAttributeValue("w3wp.exe", "Handle", userName));
|
|
Assert.True(x != z, "worker process is recycled");
|
|
|
|
newPocessIdBackendProcess = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
|
|
Assert.True(backupPocessIdBackendProcess != newPocessIdBackendProcess, "backend process is recycled");
|
|
}
|
|
testSite.AspNetCoreApp.RestoreFile("web.config");
|
|
}
|
|
}
|
|
|
|
public static async Task DoCompressionTest(IISConfigUtility.AppPoolBitness appPoolBitness, bool useCompressionMiddleWare, bool enableIISCompression)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoCompressionTest"))
|
|
{
|
|
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
|
|
{
|
|
string startupClass = "StartupCompressionCaching";
|
|
if (!useCompressionMiddleWare)
|
|
{
|
|
startupClass = "StartupNoCompressionCaching";
|
|
}
|
|
|
|
// set startup class
|
|
iisConfig.SetANCMConfig(
|
|
testSite.SiteName,
|
|
testSite.AspNetCoreApp.Name,
|
|
"environmentVariable",
|
|
new string[] { "ANCMTestStartupClassName", startupClass }
|
|
);
|
|
|
|
// enable or IIS compression
|
|
// Note: IIS compression, however, will be ignored if AspnetCore compression middleware is enabled.
|
|
iisConfig.SetCompression(testSite.SiteName, enableIISCompression);
|
|
|
|
// prepare static contents
|
|
testSite.AspNetCoreApp.CreateDirectory("wwwroot");
|
|
testSite.AspNetCoreApp.CreateDirectory(@"wwwroot\pdir");
|
|
|
|
testSite.AspNetCoreApp.CreateFile(new string[] { "foohtm" }, @"wwwroot\foo.htm");
|
|
testSite.AspNetCoreApp.CreateFile(new string[] { "barhtm" }, @"wwwroot\pdir\bar.htm");
|
|
testSite.AspNetCoreApp.CreateFile(new string[] { "defaulthtm" }, @"wwwroot\default.htm");
|
|
|
|
string result = string.Empty;
|
|
if (!useCompressionMiddleWare && !enableIISCompression)
|
|
{
|
|
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
|
|
Assert.True(result.Contains("foohtm"), "verify response body");
|
|
Assert.False(result.Contains("Content-Encoding"), "verify response header");
|
|
|
|
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("pdir/bar.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
|
|
Assert.True(result.Contains("barhtm"), "verify response body");
|
|
Assert.False(result.Contains("Content-Encoding"), "verify response header");
|
|
|
|
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri(), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
|
|
Assert.True(result.Contains("defaulthtm"), "verify response body");
|
|
Assert.False(result.Contains("Content-Encoding"), "verify response header");
|
|
}
|
|
else
|
|
{
|
|
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
|
|
Assert.True(result.Contains("foohtm"), "verify response body");
|
|
Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding"));
|
|
|
|
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("pdir/bar.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
|
|
Assert.True(result.Contains("barhtm"), "verify response body");
|
|
Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding"));
|
|
|
|
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri(), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
|
|
Assert.True(result.Contains("defaulthtm"), "verify response body");
|
|
Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding"));
|
|
}
|
|
}
|
|
testSite.AspNetCoreApp.RestoreFile("web.config");
|
|
}
|
|
}
|
|
|
|
public static async Task DoCachingTest(IISConfigUtility.AppPoolBitness appPoolBitness)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoCachingTest"))
|
|
{
|
|
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
|
|
{
|
|
string startupClass = "StartupCompressionCaching";
|
|
|
|
// set startup class
|
|
iisConfig.SetANCMConfig(
|
|
testSite.SiteName,
|
|
testSite.AspNetCoreApp.Name,
|
|
"environmentVariable",
|
|
new string[] { "ANCMTestStartupClassName", startupClass }
|
|
);
|
|
|
|
// enable IIS compression
|
|
// Note: IIS compression, however, will be ignored if AspnetCore compression middleware is enabled.
|
|
iisConfig.SetCompression(testSite.SiteName, true);
|
|
|
|
// prepare static contents
|
|
testSite.AspNetCoreApp.CreateDirectory("wwwroot");
|
|
testSite.AspNetCoreApp.CreateDirectory(@"wwwroot\pdir");
|
|
|
|
testSite.AspNetCoreApp.CreateFile(new string[] { "foohtm" }, @"wwwroot\foo.htm");
|
|
testSite.AspNetCoreApp.CreateFile(new string[] { "barhtm" }, @"wwwroot\pdir\bar.htm");
|
|
testSite.AspNetCoreApp.CreateFile(new string[] { "defaulthtm" }, @"wwwroot\default.htm");
|
|
|
|
string result = string.Empty;
|
|
|
|
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);
|
|
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);
|
|
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri("foo.htm"), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
|
|
Assert.True(result.Contains("foohtm"), "verify response body");
|
|
Assert.Equal("gzip", GetHeaderValue(result, "Content-Encoding"));
|
|
string headerValue3 = GetHeaderValue(result, "MyCustomHeader");
|
|
Assert.NotEqual(headerValue2, headerValue3);
|
|
}
|
|
testSite.AspNetCoreApp.RestoreFile("web.config");
|
|
}
|
|
}
|
|
|
|
public static async Task DoSendHTTPSRequestTest(IISConfigUtility.AppPoolBitness appPoolBitness)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoSendHTTPSRequestTest", startIISExpress:false))
|
|
{
|
|
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
|
|
{
|
|
string hostName = "";
|
|
string subjectName = "localhost";
|
|
string ipAddress = "*";
|
|
string hexIPAddress = "0x00";
|
|
int sslPort = InitializeTestMachine.SiteId + 6300;
|
|
|
|
// Add https binding and get https uri information
|
|
iisConfig.AddBindingToSite(testSite.SiteName, ipAddress, sslPort, hostName, "https");
|
|
|
|
// Create a self signed certificate
|
|
string thumbPrint = iisConfig.CreateSelfSignedCertificate(subjectName);
|
|
|
|
// Export the self signed certificate to rootCA
|
|
iisConfig.ExportCertificateTo(thumbPrint, sslStoreTo:@"Cert:\LocalMachine\Root");
|
|
|
|
// Configure http.sys ssl certificate mapping to IP:Port endpoint with the newly created self signed certificage
|
|
iisConfig.SetSSLCertificate(sslPort, hexIPAddress, thumbPrint);
|
|
|
|
// 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);
|
|
Assert.True(result.Contains("Running"), "verify response body");
|
|
|
|
// Verify https request
|
|
Uri targetHttpsUri = testSite.AspNetCoreApp.GetUri(null, sslPort, protocol: "https");
|
|
result = await GetResponseAndHeaders(targetHttpsUri, new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.OK);
|
|
Assert.True(result.Contains("Running"), "verify response body");
|
|
|
|
// Remove the SSL Certificate mapping
|
|
iisConfig.RemoveSSLCertificate(sslPort, hexIPAddress);
|
|
|
|
// Remove the newly created self signed certificate
|
|
iisConfig.DeleteCertificate(thumbPrint);
|
|
|
|
// Remove the exported self signed certificate on rootCA
|
|
iisConfig.DeleteCertificate(thumbPrint, @"Cert:\LocalMachine\Root");
|
|
}
|
|
testSite.AspNetCoreApp.RestoreFile("web.config");
|
|
}
|
|
}
|
|
|
|
public static async Task DoClientCertificateMappingTest(IISConfigUtility.AppPoolBitness appPoolBitness, bool useHTTPSMiddleWare)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoClientCertificateMappingTest", startIISExpress: false))
|
|
{
|
|
using (var iisConfig = new IISConfigUtility(testSite.IisServerType, testSite.IisExpressConfigPath))
|
|
{
|
|
string hostName = "";
|
|
string rootCN = "ANCMTest" + testSite.PostFix;
|
|
string webServerCN = "localhost";
|
|
string kestrelServerCN = "localhost";
|
|
string clientCN = "ANCMClient-" + testSite.PostFix;
|
|
|
|
string ipAddress = "*";
|
|
string hexIPAddress = "0x00";
|
|
int sslPort = InitializeTestMachine.SiteId + 6300;
|
|
|
|
// Add https binding and get https uri information
|
|
iisConfig.AddBindingToSite(testSite.SiteName, ipAddress, sslPort, hostName, "https");
|
|
|
|
// Create a root certificate
|
|
string thumbPrintForRoot = iisConfig.CreateSelfSignedCertificateWithMakeCert(rootCN);
|
|
|
|
// Create a certificate for web server setting its issuer with the root certificate subject name
|
|
string thumbPrintForWebServer = iisConfig.CreateSelfSignedCertificateWithMakeCert(webServerCN, rootCN, extendedKeyUsage: "1.3.6.1.5.5.7.3.1");
|
|
string thumbPrintForKestrel = null;
|
|
|
|
// Create a certificate for client authentication setting its issuer with the root certificate subject name
|
|
string thumbPrintForClientAuthentication = iisConfig.CreateSelfSignedCertificateWithMakeCert(clientCN, rootCN, extendedKeyUsage: "1.3.6.1.5.5.7.3.2");
|
|
|
|
// Configure http.sys ssl certificate mapping to IP:Port endpoint with the newly created self signed certificage
|
|
iisConfig.SetSSLCertificate(sslPort, hexIPAddress, thumbPrintForWebServer);
|
|
|
|
// Create a new local administrator user
|
|
string userName = "tempuser" + TestUtility.RandomString(5);
|
|
string password = "AncmTest123!";
|
|
string temp;
|
|
temp = TestUtility.RunPowershellScript("net localgroup IIS_IUSRS /Delete " + userName);
|
|
temp = TestUtility.RunPowershellScript("net user " + userName + " /Delete");
|
|
temp = TestUtility.RunPowershellScript("net user " + userName + " " + password + " /ADD");
|
|
temp = TestUtility.RunPowershellScript("net localgroup IIS_IUSRS /Add " + userName);
|
|
|
|
// 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");
|
|
|
|
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)
|
|
{
|
|
// set startup class
|
|
string startupClass = "StartupHTTPS";
|
|
iisConfig.SetANCMConfig(
|
|
testSite.SiteName,
|
|
testSite.AspNetCoreApp.Name,
|
|
"environmentVariable",
|
|
new string[] { "ANCMTestStartupClassName", startupClass }
|
|
);
|
|
|
|
// Create a certificate for Kestrel web server and export to TestResources\testcert.pfx
|
|
// NOTE: directory name "TestResources", file name "testcert.pfx" and password "testPassword" should be matched to AspnetCoreModule.TestSites.Standard web application
|
|
thumbPrintForKestrel = iisConfig.CreateSelfSignedCertificateWithMakeCert(kestrelServerCN, rootCN, extendedKeyUsage: "1.3.6.1.5.5.7.3.1");
|
|
testSite.AspNetCoreApp.CreateDirectory("TestResources");
|
|
string pfxFilePath = Path.Combine(testSite.AspNetCoreApp.GetDirectoryPathWith("TestResources"), "testcert.pfx");
|
|
iisConfig.ExportCertificateTo(thumbPrintForKestrel, sslStoreFrom: "Cert:\\LocalMachine\\My", sslStoreTo: pfxFilePath, pfxPassword: "testPassword");
|
|
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");
|
|
Assert.Equal("200", statusCode);
|
|
|
|
// Verify https request with client certificate includes the certificate header "MS-ASPNETCORE-CLIENTCERT"
|
|
Uri targetHttpsUriForDumpRequestHeaders = testSite.AspNetCoreApp.GetUri("DumpRequestHeaders", sslPort, protocol: "https");
|
|
string outputRawContent = TestUtility.RunPowershellScript("( invoke-webrequest " + targetHttpsUriForDumpRequestHeaders.OriginalString + " -CertificateThumbprint " + thumbPrintForClientAuthentication + ").RawContent.ToString()");
|
|
string expectedHeaderName = "MS-ASPNETCORE-CLIENTCERT";
|
|
Assert.True(outputRawContent.Contains(expectedHeaderName));
|
|
|
|
// Get the value of MS-ASPNETCORE-CLIENTCERT request header again and verify it is matched to its configured public key
|
|
Uri targetHttpsUriForCLIENTCERTRequestHeader = testSite.AspNetCoreApp.GetUri("GetRequestHeaderValueMS-ASPNETCORE-CLIENTCERT", sslPort, protocol: "https");
|
|
outputRawContent = TestUtility.RunPowershellScript("( invoke-webrequest " + targetHttpsUriForCLIENTCERTRequestHeader.OriginalString + " -CertificateThumbprint " + thumbPrintForClientAuthentication + ").RawContent.ToString()");
|
|
Assert.True(outputRawContent.Contains(publicKey));
|
|
|
|
// Verify non-https request returns 403.4 error
|
|
string result = string.Empty;
|
|
result = await GetResponseAndHeaders(testSite.AspNetCoreApp.GetUri(), new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.Forbidden);
|
|
Assert.True(result.Contains("403.4"));
|
|
|
|
// Verify https request without using client certificate returns 403.7
|
|
result = await GetResponseAndHeaders(targetHttpsUri, new string[] { "Accept-Encoding", "gzip" }, HttpStatusCode.Forbidden);
|
|
Assert.True(result.Contains("403.7"));
|
|
|
|
// Clean up user
|
|
temp = TestUtility.RunPowershellScript("net localgroup IIS_IUSRS /Delete " + userName);
|
|
temp = TestUtility.RunPowershellScript("net user " + userName + " /Delete");
|
|
|
|
// Remove the SSL Certificate mapping
|
|
iisConfig.RemoveSSLCertificate(sslPort, hexIPAddress);
|
|
|
|
// Clean up certificates
|
|
iisConfig.DeleteCertificate(thumbPrintForRoot, @"Cert:\LocalMachine\Root");
|
|
iisConfig.DeleteCertificate(thumbPrintForWebServer, @"Cert:\LocalMachine\My");
|
|
if (useHTTPSMiddleWare)
|
|
{
|
|
iisConfig.DeleteCertificate(thumbPrintForKestrel, @"Cert:\LocalMachine\My");
|
|
}
|
|
iisConfig.DeleteCertificate(thumbPrintForClientAuthentication, @"Cert:\CurrentUser\My");
|
|
}
|
|
testSite.AspNetCoreApp.RestoreFile("web.config");
|
|
}
|
|
}
|
|
|
|
public static async Task DoWebSocketTest(IISConfigUtility.AppPoolBitness appPoolBitness, string testData)
|
|
{
|
|
using (var testSite = new TestWebSite(appPoolBitness, "DoWebSocketTest"))
|
|
{
|
|
DateTime startTime = DateTime.Now;
|
|
|
|
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
|
|
|
|
// Get Process ID
|
|
string backendProcessId = await GetResponse(testSite.AspNetCoreApp.GetUri("GetProcessId"), HttpStatusCode.OK);
|
|
|
|
// Verify WebSocket without setting subprotocol
|
|
await VerifyResponseBodyContain(testSite.WebSocketApp.GetUri("echo.aspx"), new string[] { "Socket Open" }, HttpStatusCode.OK); // echo.aspx has hard coded path for the websocket server
|
|
|
|
// Verify WebSocket subprotocol
|
|
await VerifyResponseBodyContain(testSite.WebSocketApp.GetUri("echoSubProtocol.aspx"), new string[] { "Socket Open", "mywebsocketsubprotocol" }, HttpStatusCode.OK); // echoSubProtocol.aspx has hard coded path for the websocket server
|
|
|
|
// Verify websocket
|
|
using (WebSocketClientHelper websocketClient = new WebSocketClientHelper())
|
|
{
|
|
var frameReturned = websocketClient.Connect(testSite.AspNetCoreApp.GetUri("websocket"), true, true);
|
|
Assert.True(frameReturned.Content.Contains("Connection: Upgrade"));
|
|
Assert.True(frameReturned.Content.Contains("HTTP/1.1 101 Switching Protocols"));
|
|
Thread.Sleep(500);
|
|
|
|
VerifySendingWebSocketData(websocketClient, testData);
|
|
|
|
Thread.Sleep(500);
|
|
frameReturned = websocketClient.Close();
|
|
Assert.True(frameReturned.FrameType == FrameType.Close, "Closing Handshake");
|
|
}
|
|
|
|
// send a simple request again and verify the response body
|
|
await VerifyResponseBody(testSite.AspNetCoreApp.GetUri(), "Running", HttpStatusCode.OK);
|
|
}
|
|
}
|
|
|
|
private static string GetHeaderValue(string inputData, string headerName)
|
|
{
|
|
string result = string.Empty;
|
|
foreach (string item in inputData.Split(new char[] { ',', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries))
|
|
{
|
|
if (item.Contains(headerName))
|
|
{
|
|
var tokens = item.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
|
|
if (tokens.Length == 2)
|
|
{
|
|
result = tokens[1].Trim();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static bool VerifySendingWebSocketData(WebSocketClientHelper websocketClient, string testData)
|
|
{
|
|
bool result = false;
|
|
|
|
//
|
|
// send complete or partial text data and ping multiple times
|
|
//
|
|
websocketClient.SendTextData(testData);
|
|
websocketClient.SendPing();
|
|
websocketClient.SendTextData(testData);
|
|
websocketClient.SendPing();
|
|
websocketClient.SendPing();
|
|
websocketClient.SendTextData(testData, 0x01); // 0x01: start of sending partial data
|
|
websocketClient.SendPing();
|
|
websocketClient.SendTextData(testData, 0x80); // 0x80: end of sending partial data
|
|
websocketClient.SendPing();
|
|
websocketClient.SendPing();
|
|
websocketClient.SendTextData(testData);
|
|
websocketClient.SendTextData(testData);
|
|
websocketClient.SendTextData(testData);
|
|
websocketClient.SendPing();
|
|
Thread.Sleep(3000);
|
|
|
|
// Verify test result
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
if (DoVerifyDataSentAndReceived(websocketClient) == false)
|
|
{
|
|
// retrying after 1 second sleeping
|
|
Thread.Sleep(1000);
|
|
}
|
|
else
|
|
{
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static bool DoVerifyDataSentAndReceived(WebSocketClientHelper websocketClient)
|
|
{
|
|
var result = true;
|
|
var sentString = new StringBuilder();
|
|
var recString = new StringBuilder();
|
|
var pingString = new StringBuilder();
|
|
var pongString = new StringBuilder();
|
|
|
|
foreach (Frame frame in websocketClient.Connection.DataSent.ToArray())
|
|
{
|
|
if (frame.FrameType == FrameType.Continuation
|
|
|| frame.FrameType == FrameType.SegmentedText
|
|
|| frame.FrameType == FrameType.Text
|
|
|| frame.FrameType == FrameType.ContinuationFrameEnd)
|
|
{
|
|
sentString.Append(frame.Content);
|
|
}
|
|
|
|
if (frame.FrameType == FrameType.Ping)
|
|
{
|
|
pingString.Append(frame.Content);
|
|
}
|
|
}
|
|
|
|
foreach (Frame frame in websocketClient.Connection.DataReceived.ToArray())
|
|
{
|
|
if (frame.FrameType == FrameType.Continuation
|
|
|| frame.FrameType == FrameType.SegmentedText
|
|
|| frame.FrameType == FrameType.Text
|
|
|| frame.FrameType == FrameType.ContinuationFrameEnd)
|
|
{
|
|
recString.Append(frame.Content);
|
|
}
|
|
|
|
if (frame.FrameType == FrameType.Pong)
|
|
{
|
|
pongString.Append(frame.Content);
|
|
}
|
|
}
|
|
|
|
if (sentString.Length == recString.Length && pongString.Length == pingString.Length)
|
|
{
|
|
if (sentString.Length != recString.Length)
|
|
{
|
|
result = false;
|
|
TestUtility.LogInformation("Same size of data sent(" + sentString.Length + ") and received(" + recString.Length + ")");
|
|
}
|
|
|
|
if (sentString.ToString() != recString.ToString())
|
|
{
|
|
result = false;
|
|
TestUtility.LogInformation("Not matched string in sent and received");
|
|
}
|
|
if (pongString.Length != pingString.Length)
|
|
{
|
|
result = false;
|
|
TestUtility.LogInformation("Ping received; Ping (" + pingString.Length + ") and Pong (" + pongString.Length + ")");
|
|
}
|
|
websocketClient.Connection.DataSent.Clear();
|
|
websocketClient.Connection.DataReceived.Clear();
|
|
}
|
|
else
|
|
{
|
|
TestUtility.LogInformation("Retrying... so far data sent(" + sentString.Length + ") and received(" + recString.Length + ")");
|
|
result = false;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static async Task CheckChunkedAsync(HttpClient client, TestWebApplication webApp)
|
|
{
|
|
var response = await client.GetAsync(webApp.GetUri("chunked"));
|
|
var responseText = await response.Content.ReadAsStringAsync();
|
|
try
|
|
{
|
|
Assert.Equal("Chunked", responseText);
|
|
Assert.True(response.Headers.TransferEncodingChunked, "/chunked, chunked?");
|
|
Assert.Null(response.Headers.ConnectionClose);
|
|
Assert.Null(GetContentLength(response));
|
|
}
|
|
catch (XunitException)
|
|
{
|
|
TestUtility.LogInformation(response.ToString());
|
|
TestUtility.LogInformation(responseText);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private static string GetContentLength(HttpResponseMessage response)
|
|
{
|
|
// Don't use response.Content.Headers.ContentLength, it will dynamically calculate the value if it can.
|
|
IEnumerable<string> values;
|
|
return response.Content.Headers.TryGetValues(HeaderNames.ContentLength, out values) ? values.FirstOrDefault() : null;
|
|
}
|
|
|
|
private static bool VerifyANCMStartEvent(DateTime startFrom, string includeThis)
|
|
{
|
|
return VerifyEventLog(1001, startFrom, includeThis);
|
|
}
|
|
|
|
private static bool VerifyApplicationEventLog(int eventID, DateTime startFrom, string includeThis)
|
|
{
|
|
return VerifyEventLog(eventID, startFrom, includeThis);
|
|
}
|
|
|
|
private static bool VerifyEventLog(int eventId, DateTime startFrom, string includeThis = null)
|
|
{
|
|
var events = TestUtility.GetApplicationEvent(eventId, startFrom);
|
|
Assert.True(events.Count > 0, "Verfiy expected event logs");
|
|
bool findEvent = false;
|
|
foreach (string item in events)
|
|
{
|
|
if (item.Contains(includeThis))
|
|
{
|
|
findEvent = true;
|
|
break;
|
|
}
|
|
}
|
|
return findEvent;
|
|
}
|
|
|
|
private static async Task VerifyResponseStatus(Uri uri, HttpStatusCode expectedResponseStatus, int timeout = 5, int numberOfRetryCount = 2, bool verifyResponseFlag = true)
|
|
{
|
|
await SendReceive(uri, null, null, null, expectedResponseStatus, ReturnValueType.None, numberOfRetryCount, verifyResponseFlag, postData: null, timeout: timeout);
|
|
}
|
|
|
|
private static async Task VerifyResponseBody(Uri uri, string expectedResponseBody, HttpStatusCode expectedResponseStatus, int timeout = 5, int numberOfRetryCount = 2, bool verifyResponseFlag = true)
|
|
{
|
|
await SendReceive(uri, null, expectedResponseBody, null, expectedResponseStatus, ReturnValueType.None, numberOfRetryCount, verifyResponseFlag, postData:null, timeout:timeout);
|
|
}
|
|
|
|
private static async Task VerifyPostResponseBody(Uri uri, KeyValuePair<string, string>[] postData, string expectedResponseBody, HttpStatusCode expectedResponseStatus, int timeout = 5, int numberOfRetryCount = 2, bool verifyResponseFlag = true)
|
|
{
|
|
await SendReceive(uri, null, expectedResponseBody, null, expectedResponseStatus, ReturnValueType.None, numberOfRetryCount, verifyResponseFlag, postData, timeout);
|
|
}
|
|
|
|
private static async Task VerifyResponseBodyContain(Uri uri, string[] expectedStrings, HttpStatusCode expectedResponseStatus, int timeout = 5, int numberOfRetryCount = 2, bool verifyResponseFlag = true)
|
|
{
|
|
await SendReceive(uri, null, null, expectedStrings, expectedResponseStatus, ReturnValueType.None, numberOfRetryCount, verifyResponseFlag, postData: null, timeout: timeout);
|
|
}
|
|
|
|
private static async Task<string> GetResponse(Uri uri, HttpStatusCode expectedResponseStatus, ReturnValueType returnValueType = ReturnValueType.ResponseBody, int timeout = 5, int numberOfRetryCount = 1, bool verifyResponseFlag = true)
|
|
{
|
|
return await SendReceive(uri, null, null, null, expectedResponseStatus, returnValueType, numberOfRetryCount, verifyResponseFlag, postData:null, timeout:timeout);
|
|
}
|
|
|
|
private static async Task<string> GetResponseAndHeaders(Uri uri, string[] requestHeaders, HttpStatusCode expectedResponseStatus, ReturnValueType returnValueType = ReturnValueType.ResponseBodyAndHeaders, int timeout = 5, int numberOfRetryCount = 1, bool verifyResponseFlag = true)
|
|
{
|
|
return await SendReceive(uri, requestHeaders, null, null, expectedResponseStatus, returnValueType, numberOfRetryCount, verifyResponseFlag, postData: null, timeout: timeout);
|
|
}
|
|
|
|
private static async Task<string> GetResponseStatusCode(Uri uri)
|
|
{
|
|
return await SendReceive(uri, null, null, null, HttpStatusCode.OK, ReturnValueType.ResponseStatus, numberOfRetryCount:1, verifyResponseFlag:false, postData:null, timeout:5);
|
|
}
|
|
|
|
private static async Task<string> ReadContent(HttpResponseMessage response)
|
|
{
|
|
bool unZipContent = false;
|
|
string result = String.Empty;
|
|
|
|
IEnumerable<string> values;
|
|
if (response.Headers.TryGetValues("Vary", out values))
|
|
{
|
|
unZipContent = true;
|
|
}
|
|
|
|
if (unZipContent)
|
|
{
|
|
var inputStream = await response.Content.ReadAsStreamAsync();
|
|
|
|
// 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))
|
|
{
|
|
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;
|
|
using (StreamReader reader = new StreamReader(outputStream, Encoding.UTF8))
|
|
{
|
|
result = reader.ReadToEnd();
|
|
outputStream.Close();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = await response.Content.ReadAsStringAsync();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static async Task<string> SendReceive(Uri uri, string[] requestHeaders, string expectedResponseBody, string[] expectedStringsInResponseBody, HttpStatusCode expectedResponseStatus, ReturnValueType returnValueType, int numberOfRetryCount, bool verifyResponseFlag, KeyValuePair < string, string>[] postData, int timeout)
|
|
{
|
|
string result = null;
|
|
string responseText = "NotInitialized";
|
|
string responseStatus = "NotInitialized";
|
|
|
|
var httpClientHandler = new HttpClientHandler();
|
|
httpClientHandler.UseDefaultCredentials = true;
|
|
httpClientHandler.AutomaticDecompression = DecompressionMethods.None;
|
|
|
|
var httpClient = new HttpClient(httpClientHandler)
|
|
{
|
|
BaseAddress = uri,
|
|
Timeout = TimeSpan.FromSeconds(timeout),
|
|
};
|
|
|
|
if (requestHeaders != null)
|
|
{
|
|
for (int i = 0; i < requestHeaders.Length; i=i+2)
|
|
{
|
|
httpClient.DefaultRequestHeaders.Add(requestHeaders[i], requestHeaders[i+1]);
|
|
}
|
|
}
|
|
|
|
HttpResponseMessage response = null;
|
|
try
|
|
{
|
|
FormUrlEncodedContent postHttpContent = null;
|
|
if (postData != null)
|
|
{
|
|
postHttpContent = new FormUrlEncodedContent(postData);
|
|
}
|
|
|
|
if (numberOfRetryCount > 1 && expectedResponseStatus == HttpStatusCode.OK)
|
|
{
|
|
if (postData == null)
|
|
{
|
|
response = await TestUtility.RetryRequest(() =>
|
|
{
|
|
return httpClient.GetAsync(string.Empty);
|
|
}, TestUtility.Logger, retryCount: numberOfRetryCount);
|
|
}
|
|
else
|
|
{
|
|
response = await TestUtility.RetryRequest(() =>
|
|
{
|
|
return httpClient.PostAsync(string.Empty, postHttpContent);
|
|
}, TestUtility.Logger, retryCount: numberOfRetryCount);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (postData == null)
|
|
{
|
|
response = await httpClient.GetAsync(string.Empty);
|
|
}
|
|
else
|
|
{
|
|
response = await httpClient.PostAsync(string.Empty, postHttpContent);
|
|
}
|
|
}
|
|
|
|
if (response != null)
|
|
{
|
|
responseStatus = response.StatusCode.ToString();
|
|
if (verifyResponseFlag)
|
|
{
|
|
if (expectedResponseBody != null)
|
|
{
|
|
if (responseText == "NotInitialized")
|
|
{
|
|
responseText = await ReadContent(response);
|
|
}
|
|
Assert.Equal(expectedResponseBody, responseText);
|
|
}
|
|
|
|
if (expectedStringsInResponseBody != null)
|
|
{
|
|
if (responseText == "NotInitialized")
|
|
{
|
|
responseText = await ReadContent(response);
|
|
}
|
|
foreach (string item in expectedStringsInResponseBody)
|
|
{
|
|
Assert.True(responseText.Contains(item));
|
|
}
|
|
}
|
|
Assert.Equal(expectedResponseStatus, response.StatusCode);
|
|
}
|
|
|
|
switch (returnValueType)
|
|
{
|
|
case ReturnValueType.ResponseBody:
|
|
case ReturnValueType.ResponseBodyAndHeaders:
|
|
if (responseText == "NotInitialized")
|
|
{
|
|
responseText = await ReadContent(response);
|
|
}
|
|
result = responseText;
|
|
if (returnValueType == ReturnValueType.ResponseBodyAndHeaders)
|
|
{
|
|
result += ", " + response.ToString();
|
|
}
|
|
break;
|
|
case ReturnValueType.ResponseStatus:
|
|
result = response.StatusCode.ToString();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch (XunitException)
|
|
{
|
|
if (response != null)
|
|
{
|
|
TestUtility.LogInformation(response.ToString());
|
|
}
|
|
TestUtility.LogInformation(responseText);
|
|
TestUtility.LogInformation(responseStatus);
|
|
throw;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
}
|