Adds websocket feature detection, test cleanup. (#624)

This commit is contained in:
Justin Kotalik 2018-03-08 09:52:45 -08:00 committed by GitHub
parent ad4b172660
commit 00b1948937
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 772 additions and 467 deletions

View File

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.27130.2026 VisualStudioVersion = 15.0.27130.2036
MinimumVisualStudioVersion = 15.0.26730.03 MinimumVisualStudioVersion = 15.0.26730.03
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{04B1EDB6-E967-4D25-89B9-E6F8304038CD}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{04B1EDB6-E967-4D25-89B9-E6F8304038CD}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
@ -44,8 +44,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeIISSample", "samples\
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IISTestSite", "test\IISTestSite\IISTestSite.csproj", "{679FA2A2-898B-4320-884E-C2D294A97CE1}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IISTestSite", "test\IISTestSite\IISTestSite.csproj", "{679FA2A2-898B-4320-884E-C2D294A97CE1}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IISIntegration.IISServerFunctionalTests", "test\IISIntegration.IISServerFunctionalTests\IISIntegration.IISServerFunctionalTests.csproj", "{C745BBFD-7888-4038-B41B-6D1832D13878}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AspNetCore", "src\AspNetCore\AspNetCore.vcxproj", "{439824F9-1455-4CC4-BD79-B44FA0A16552}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AspNetCore", "src\AspNetCore\AspNetCore.vcxproj", "{439824F9-1455-4CC4-BD79-B44FA0A16552}"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IISLib", "src\IISLib\IISLib.vcxproj", "{4787A64F-9A3E-4867-A55A-70CB4B2B2FFE}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IISLib", "src\IISLib\IISLib.vcxproj", "{4787A64F-9A3E-4867-A55A-70CB4B2B2FFE}"
@ -150,17 +148,6 @@ Global
{679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|x64.Build.0 = Release|Any CPU {679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|x64.Build.0 = Release|Any CPU
{679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|x86.ActiveCfg = Release|Any CPU {679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|x86.ActiveCfg = Release|Any CPU
{679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|x86.Build.0 = Release|Any CPU {679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|x86.Build.0 = Release|Any CPU
{C745BBFD-7888-4038-B41B-6D1832D13878}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C745BBFD-7888-4038-B41B-6D1832D13878}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C745BBFD-7888-4038-B41B-6D1832D13878}.Debug|x64.ActiveCfg = Debug|Any CPU
{C745BBFD-7888-4038-B41B-6D1832D13878}.Debug|x64.Build.0 = Debug|Any CPU
{C745BBFD-7888-4038-B41B-6D1832D13878}.Debug|x86.ActiveCfg = Debug|Any CPU
{C745BBFD-7888-4038-B41B-6D1832D13878}.Debug|x86.Build.0 = Debug|Any CPU
{C745BBFD-7888-4038-B41B-6D1832D13878}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C745BBFD-7888-4038-B41B-6D1832D13878}.Release|Any CPU.Build.0 = Release|Any CPU
{C745BBFD-7888-4038-B41B-6D1832D13878}.Release|x64.ActiveCfg = Release|Any CPU
{C745BBFD-7888-4038-B41B-6D1832D13878}.Release|x64.Build.0 = Release|Any CPU
{C745BBFD-7888-4038-B41B-6D1832D13878}.Release|x86.ActiveCfg = Release|Any CPU
{439824F9-1455-4CC4-BD79-B44FA0A16552}.Debug|Any CPU.ActiveCfg = Debug|Win32 {439824F9-1455-4CC4-BD79-B44FA0A16552}.Debug|Any CPU.ActiveCfg = Debug|Win32
{439824F9-1455-4CC4-BD79-B44FA0A16552}.Debug|x64.ActiveCfg = Debug|x64 {439824F9-1455-4CC4-BD79-B44FA0A16552}.Debug|x64.ActiveCfg = Debug|x64
{439824F9-1455-4CC4-BD79-B44FA0A16552}.Debug|x64.Build.0 = Debug|x64 {439824F9-1455-4CC4-BD79-B44FA0A16552}.Debug|x64.Build.0 = Debug|x64
@ -225,7 +212,6 @@ Global
{4E3E1F5C-CD52-4CC0-A35F-D1FA1685D2FA} = {EF30B533-D715-421A-92B7-92FEF460AC9C} {4E3E1F5C-CD52-4CC0-A35F-D1FA1685D2FA} = {EF30B533-D715-421A-92B7-92FEF460AC9C}
{9BC4AFCB-325D-4C81-8228-8CF301CE2F97} = {C74B8F36-FD2F-45C9-9B8A-00E7CF0126A9} {9BC4AFCB-325D-4C81-8228-8CF301CE2F97} = {C74B8F36-FD2F-45C9-9B8A-00E7CF0126A9}
{679FA2A2-898B-4320-884E-C2D294A97CE1} = {EF30B533-D715-421A-92B7-92FEF460AC9C} {679FA2A2-898B-4320-884E-C2D294A97CE1} = {EF30B533-D715-421A-92B7-92FEF460AC9C}
{C745BBFD-7888-4038-B41B-6D1832D13878} = {EF30B533-D715-421A-92B7-92FEF460AC9C}
{439824F9-1455-4CC4-BD79-B44FA0A16552} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD} {439824F9-1455-4CC4-BD79-B44FA0A16552} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD}
{4787A64F-9A3E-4867-A55A-70CB4B2B2FFE} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD} {4787A64F-9A3E-4867-A55A-70CB4B2B2FFE} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD}
{55494E58-E061-4C4C-A0A8-837008E72F85} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD} {55494E58-E061-4C4C-A0A8-837008E72F85} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD}

View File

@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.IISIntegration; using Microsoft.AspNetCore.Server.IISIntegration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -71,7 +72,16 @@ namespace IISSample
var value = vars[key]; var value = vars[key];
await context.Response.WriteAsync(key + ": " + value + Environment.NewLine); await context.Response.WriteAsync(key + ": " + value + Environment.NewLine);
} }
await context.Response.WriteAsync(Environment.NewLine); await context.Response.WriteAsync(Environment.NewLine);
if (context.Features.Get<IHttpUpgradeFeature>() != null)
{
await context.Response.WriteAsync("Websocket feature is enabled.");
}
else
{
await context.Response.WriteAsync("Websocket feature is disabled.");
}
}); });
} }

View File

@ -18,4 +18,4 @@
<aspNetCore processPath="dotnet" arguments=".\IISSample.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/> <aspNetCore processPath="dotnet" arguments=".\IISSample.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
--> -->
</system.webServer> </system.webServer>
</configuration> </configuration>

View File

@ -70,6 +70,16 @@ namespace NativeIISSample
{ {
await context.Response.WriteAsync(varName + ": " + context.GetIISServerVariable(varName) + Environment.NewLine); await context.Response.WriteAsync(varName + ": " + context.GetIISServerVariable(varName) + Environment.NewLine);
} }
await context.Response.WriteAsync(Environment.NewLine);
if (context.Features.Get<IHttpUpgradeFeature>() != null)
{
await context.Response.WriteAsync("Websocket feature is enabled.");
}
else
{
await context.Response.WriteAsync("Websocket feature is disabled.");
}
}); });
} }
@ -83,6 +93,7 @@ namespace NativeIISSample
"REMOTE_PORT", "REMOTE_PORT",
"REMOTE_USER", "REMOTE_USER",
"REQUEST_METHOD", "REQUEST_METHOD",
"WEBSOCKET_VERSION"
}; };
public static void Main(string[] args) public static void Main(string[] args)

View File

@ -7,6 +7,7 @@
#define HOSTING_STARTUP_ASSEMBLIES_NAME L"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES=" #define HOSTING_STARTUP_ASSEMBLIES_NAME L"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES="
#define HOSTING_STARTUP_ASSEMBLIES_VALUE L"Microsoft.AspNetCore.Server.IISIntegration" #define HOSTING_STARTUP_ASSEMBLIES_VALUE L"Microsoft.AspNetCore.Server.IISIntegration"
#define ASPNETCORE_IIS_AUTH_ENV_STR L"ASPNETCORE_IIS_HTTPAUTH=" #define ASPNETCORE_IIS_AUTH_ENV_STR L"ASPNETCORE_IIS_HTTPAUTH="
#define ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED_ENV_STR L"ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED="
#define ASPNETCORE_IIS_AUTH_WINDOWS L"windows;" #define ASPNETCORE_IIS_AUTH_WINDOWS L"windows;"
#define ASPNETCORE_IIS_AUTH_BASIC L"basic;" #define ASPNETCORE_IIS_AUTH_BASIC L"basic;"
#define ASPNETCORE_IIS_AUTH_ANONYMOUS L"anonymous;" #define ASPNETCORE_IIS_AUTH_ANONYMOUS L"anonymous;"
@ -123,349 +124,6 @@ public:
pEntry->Dereference(); pEntry->Dereference();
} }
static
VOID
CopyToMultiSz(
ENVIRONMENT_VAR_ENTRY * pEntry,
PVOID pvData
)
{
STRU strTemp;
MULTISZ *pMultiSz = static_cast<MULTISZ *>(pvData);
DBG_ASSERT(pMultiSz);
DBG_ASSERT(pEntry);
strTemp.Copy(pEntry->QueryName());
strTemp.Append(pEntry->QueryValue());
pMultiSz->Append(strTemp.QueryStr());
}
static
VOID
CopyToTable(
ENVIRONMENT_VAR_ENTRY * pEntry,
PVOID pvData
)
{
// best effort copy, ignore the failure
ENVIRONMENT_VAR_ENTRY * pNewEntry = new ENVIRONMENT_VAR_ENTRY();
if (pNewEntry != NULL)
{
pNewEntry->Initialize(pEntry->QueryName(), pEntry->QueryValue());
ENVIRONMENT_VAR_HASH *pHash = static_cast<ENVIRONMENT_VAR_HASH *>(pvData);
DBG_ASSERT(pHash);
pHash->InsertRecord(pNewEntry);
// Need to dereference as InsertRecord references it now
pNewEntry->Dereference();
}
}
static
VOID
AppendEnvironmentVariables
(
ENVIRONMENT_VAR_ENTRY * pEntry,
PVOID pvData
)
{
UNREFERENCED_PARAMETER(pvData);
HRESULT hr = S_OK;
DWORD dwResult = 0;
DWORD dwError = 0;
STRU struNameBuffer;
STACK_STRU(struValueBuffer, 300);
BOOL fFound = FALSE;
// pEntry->QueryName includes the trailing =, remove it before calling stru
if (FAILED(hr = struNameBuffer.Copy(pEntry->QueryName())))
{
goto Finished;
}
dwResult = struNameBuffer.LastIndexOf(L'=');
if (dwResult != -1)
{
struNameBuffer.QueryStr()[dwResult] = L'\0';
if (FAILED(hr = struNameBuffer.SyncWithBuffer()))
{
goto Finished;
}
}
dwResult = GetEnvironmentVariable(struNameBuffer.QueryStr(), struValueBuffer.QueryStr(), struValueBuffer.QuerySizeCCH());
if (dwResult == 0)
{
dwError = GetLastError();
// Windows API (e.g., CreateProcess) allows variable with empty string value
// in such case dwResult will be 0 and dwError will also be 0
// As UI and CMD does not allow empty value, ignore this environment var
if (dwError != ERROR_ENVVAR_NOT_FOUND && dwError != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32(dwError);
goto Finished;
}
}
else if (dwResult > struValueBuffer.QuerySizeCCH())
{
// have to increase the buffer and try get environment var again
struValueBuffer.Reset();
struValueBuffer.Resize(dwResult + (DWORD)wcslen(pEntry->QueryValue()) + 2); // for null char and semicolon
dwResult = GetEnvironmentVariable(struNameBuffer.QueryStr(),
struValueBuffer.QueryStr(),
struValueBuffer.QuerySizeCCH());
if (struValueBuffer.IsEmpty())
{
hr = E_UNEXPECTED;
goto Finished;
}
fFound = TRUE;
}
else
{
fFound = TRUE;
}
if (FAILED(hr = struValueBuffer.SyncWithBuffer()))
{
goto Finished;
}
if (fFound)
{
if (FAILED(hr = struValueBuffer.Append(L";")))
{
goto Finished;
}
}
if (FAILED(hr = struValueBuffer.Append(pEntry->QueryValue())))
{
goto Finished;
}
if (FAILED(hr = pEntry->Initialize(pEntry->QueryName(), struValueBuffer.QueryStr())))
{
goto Finished;
}
Finished:
// TODO besides calling SetLastError, is there anyway to propagate failures to set the environment variable?
return;
}
static
VOID
SetEnvironmentVariables
(
ENVIRONMENT_VAR_ENTRY * pEntry,
PVOID pvData
)
{
UNREFERENCED_PARAMETER(pvData);
HRESULT hr = S_OK;
DWORD dwResult = 0;
STRU struNameBuffer;
// pEntry->QueryName includes the trailing =, remove it before calling SetEnvironmentVariable.
if (FAILED(hr = struNameBuffer.Copy(pEntry->QueryName())))
{
goto Finished;
}
dwResult = struNameBuffer.LastIndexOf(L'=');
if (dwResult != -1)
{
struNameBuffer.QueryStr()[dwResult] = L'\0';
if (FAILED(hr = struNameBuffer.SyncWithBuffer()))
{
goto Finished;
}
}
dwResult = SetEnvironmentVariable(struNameBuffer.QueryStr(), pEntry->QueryValue());
if (dwResult == 0)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
Finished:
return;
}
static
HRESULT
InitEnvironmentVariablesTable
(
_In_ ENVIRONMENT_VAR_HASH* pInEnvironmentVarTable,
BOOL fWindowsAuthEnabled,
BOOL fBasicAuthEnabled,
BOOL fAnonymousAuthEnabled,
ENVIRONMENT_VAR_HASH** ppEnvironmentVarTable
)
{
HRESULT hr = S_OK;
BOOL fFound = FALSE;
DWORD dwResult, dwError;
STRU strIisAuthEnvValue;
STACK_STRU(strStartupAssemblyEnv, 1024);
ENVIRONMENT_VAR_ENTRY* pHostingEntry = NULL;
ENVIRONMENT_VAR_ENTRY* pIISAuthEntry = NULL;
ENVIRONMENT_VAR_HASH* pEnvironmentVarTable = NULL;
pEnvironmentVarTable = new ENVIRONMENT_VAR_HASH();
if (pEnvironmentVarTable == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
//
// few environment variables expected, small bucket size for hash table
//
if (FAILED(hr = pEnvironmentVarTable->Initialize(37 /*prime*/)))
{
goto Finished;
}
// copy the envirable hash table (from configuration) to a temp one as we may need to remove elements
pInEnvironmentVarTable->Apply(ENVIRONMENT_VAR_HASH::CopyToTable, pEnvironmentVarTable);
if (pEnvironmentVarTable->Count() != pInEnvironmentVarTable->Count())
{
// hash table copy failed
hr = E_UNEXPECTED;
goto Finished;
}
pEnvironmentVarTable->FindKey((PWSTR)ASPNETCORE_IIS_AUTH_ENV_STR, &pIISAuthEntry);
if (pIISAuthEntry != NULL)
{
// user defined ASPNETCORE_IIS_HTTPAUTH in configuration, wipe it off
pIISAuthEntry->Dereference();
pEnvironmentVarTable->DeleteKey((PWSTR)ASPNETCORE_IIS_AUTH_ENV_STR);
}
if (fWindowsAuthEnabled)
{
strIisAuthEnvValue.Copy(ASPNETCORE_IIS_AUTH_WINDOWS);
}
if (fBasicAuthEnabled)
{
strIisAuthEnvValue.Append(ASPNETCORE_IIS_AUTH_BASIC);
}
if (fAnonymousAuthEnabled)
{
strIisAuthEnvValue.Append(ASPNETCORE_IIS_AUTH_ANONYMOUS);
}
if (strIisAuthEnvValue.IsEmpty())
{
strIisAuthEnvValue.Copy(ASPNETCORE_IIS_AUTH_NONE);
}
pIISAuthEntry = new ENVIRONMENT_VAR_ENTRY();
if (pIISAuthEntry == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
if (FAILED(hr = pIISAuthEntry->Initialize(ASPNETCORE_IIS_AUTH_ENV_STR, strIisAuthEnvValue.QueryStr())) ||
FAILED(hr = pEnvironmentVarTable->InsertRecord(pIISAuthEntry)))
{
goto Finished;
}
// Compiler is complaining about conversion between PCWSTR and PWSTR here.
// Explictly casting.
pEnvironmentVarTable->FindKey((PWSTR)HOSTING_STARTUP_ASSEMBLIES_NAME, &pHostingEntry);
if (pHostingEntry != NULL)
{
// user defined ASPNETCORE_HOSTINGSTARTUPASSEMBLIES in configuration
// the value will be used in OutputEnvironmentVariables. Do nothing here
pHostingEntry->Dereference();
pHostingEntry = NULL;
goto Skipped;
}
//check whether ASPNETCORE_HOSTINGSTARTUPASSEMBLIES is defined in system
dwResult = GetEnvironmentVariable(HOSTING_STARTUP_ASSEMBLIES_ENV_STR,
strStartupAssemblyEnv.QueryStr(),
strStartupAssemblyEnv.QuerySizeCCH());
if (dwResult == 0)
{
dwError = GetLastError();
// Windows API (e.g., CreateProcess) allows variable with empty string value
// in such case dwResult will be 0 and dwError will also be 0
// As UI and CMD does not allow empty value, ignore this environment var
if (dwError != ERROR_ENVVAR_NOT_FOUND && dwError != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32(dwError);
goto Finished;
}
}
else if (dwResult > strStartupAssemblyEnv.QuerySizeCCH())
{
// have to increase the buffer and try get environment var again
strStartupAssemblyEnv.Reset();
strStartupAssemblyEnv.Resize(dwResult + (DWORD)wcslen(HOSTING_STARTUP_ASSEMBLIES_VALUE) + 1);
dwResult = GetEnvironmentVariable(HOSTING_STARTUP_ASSEMBLIES_ENV_STR,
strStartupAssemblyEnv.QueryStr(),
strStartupAssemblyEnv.QuerySizeCCH());
if (strStartupAssemblyEnv.IsEmpty())
{
hr = E_UNEXPECTED;
goto Finished;
}
fFound = TRUE;
}
else
{
fFound = TRUE;
}
strStartupAssemblyEnv.SyncWithBuffer();
if (fFound)
{
strStartupAssemblyEnv.Append(L";");
}
strStartupAssemblyEnv.Append(HOSTING_STARTUP_ASSEMBLIES_VALUE);
// the environment variable was not defined, create it and add to hashtable
pHostingEntry = new ENVIRONMENT_VAR_ENTRY();
if (pHostingEntry == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
if (FAILED(hr = pHostingEntry->Initialize(HOSTING_STARTUP_ASSEMBLIES_NAME, strStartupAssemblyEnv.QueryStr())) ||
FAILED(hr = pEnvironmentVarTable->InsertRecord(pHostingEntry)))
{
goto Finished;
}
Skipped:
*ppEnvironmentVarTable = pEnvironmentVarTable;
pEnvironmentVarTable = NULL;
Finished:
if (pHostingEntry != NULL)
{
pHostingEntry->Dereference();
pHostingEntry = NULL;
}
if (pIISAuthEntry != NULL)
{
pIISAuthEntry->Dereference();
pIISAuthEntry = NULL;
}
if (pEnvironmentVarTable != NULL)
{
pEnvironmentVarTable->Clear();
delete pEnvironmentVarTable;
pEnvironmentVarTable = NULL;
}
return hr;
}
private: private:
ENVIRONMENT_VAR_HASH(const ENVIRONMENT_VAR_HASH &); ENVIRONMENT_VAR_HASH(const ENVIRONMENT_VAR_HASH &);

View File

@ -28,8 +28,26 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly string _pairingToken; private readonly string _pairingToken;
private readonly IApplicationLifetime _applicationLifetime; private readonly IApplicationLifetime _applicationLifetime;
private readonly bool _isWebsocketsSupported;
public IISMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IOptions<IISOptions> options, string pairingToken, IAuthenticationSchemeProvider authentication, IApplicationLifetime applicationLifetime) // Can't break public API, so creating a second constructor to propagate the isWebsocketsSupported flag.
public IISMiddleware(RequestDelegate next,
ILoggerFactory loggerFactory,
IOptions<IISOptions> options,
string pairingToken,
IAuthenticationSchemeProvider authentication,
IApplicationLifetime applicationLifetime)
: this(next, loggerFactory, options, pairingToken, isWebsocketsSupported: true, authentication, applicationLifetime)
{
}
public IISMiddleware(RequestDelegate next,
ILoggerFactory loggerFactory,
IOptions<IISOptions> options,
string pairingToken,
bool isWebsocketsSupported,
IAuthenticationSchemeProvider authentication,
IApplicationLifetime applicationLifetime)
{ {
if (next == null) if (next == null)
{ {
@ -63,6 +81,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
_pairingToken = pairingToken; _pairingToken = pairingToken;
_applicationLifetime = applicationLifetime; _applicationLifetime = applicationLifetime;
_logger = loggerFactory.CreateLogger<IISMiddleware>(); _logger = loggerFactory.CreateLogger<IISMiddleware>();
_isWebsocketsSupported = isWebsocketsSupported;
} }
public async Task Invoke(HttpContext httpContext) public async Task Invoke(HttpContext httpContext)
@ -118,6 +137,13 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
} }
} }
// Remove the upgrade feature if websockets are not supported by ANCM.
// The feature must be removed on a per request basis as the Upgrade feature exists per request.
if (!_isWebsocketsSupported)
{
httpContext.Features.Set<IHttpUpgradeFeature>(null);
}
await _next(httpContext); await _next(httpContext);
} }
} }

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
@ -12,11 +12,13 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
{ {
private readonly string _pairingToken; private readonly string _pairingToken;
private readonly PathString _pathBase; private readonly PathString _pathBase;
private readonly bool _isWebsocketsSupported;
internal IISSetupFilter(string pairingToken, PathString pathBase) internal IISSetupFilter(string pairingToken, PathString pathBase, bool isWebsocketsSupported)
{ {
_pairingToken = pairingToken; _pairingToken = pairingToken;
_pathBase = pathBase; _pathBase = pathBase;
_isWebsocketsSupported = isWebsocketsSupported;
} }
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next) public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
@ -25,7 +27,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
{ {
app.UsePathBase(_pathBase); app.UsePathBase(_pathBase);
app.UseForwardedHeaders(); app.UseForwardedHeaders();
app.UseMiddleware<IISMiddleware>(_pairingToken); app.UseMiddleware<IISMiddleware>(_pairingToken, _isWebsocketsSupported);
next(app); next(app);
}; };
} }

View File

@ -183,7 +183,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
bool IHttpResponseFeature.HasStarted => HasResponseStarted; bool IHttpResponseFeature.HasStarted => HasResponseStarted;
bool IHttpUpgradeFeature.IsUpgradableRequest => UpgradeAvailable; // The UpgradeAvailable Feature is set on the first request to the server.
bool IHttpUpgradeFeature.IsUpgradableRequest => _websocketAvailability == WebsocketAvailabilityStatus.Available;
bool IFeatureCollection.IsReadOnly => false; bool IFeatureCollection.IsReadOnly => false;

View File

@ -28,7 +28,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
private const int PauseWriterThreshold = 65536; private const int PauseWriterThreshold = 65536;
private const int ResumeWriterTheshold = PauseWriterThreshold / 2; private const int ResumeWriterTheshold = PauseWriterThreshold / 2;
private static bool UpgradeAvailable = (Environment.OSVersion.Version >= new Version(6, 2)); // TODO make this static again.
private static WebsocketAvailabilityStatus _websocketAvailability = WebsocketAvailabilityStatus.Uninitialized;
protected readonly IntPtr _pInProcessHandler; protected readonly IntPtr _pInProcessHandler;
@ -59,6 +60,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
private const string NtlmString = "NTLM"; private const string NtlmString = "NTLM";
private const string NegotiateString = "Negotiate"; private const string NegotiateString = "Negotiate";
private const string BasicString = "Basic"; private const string BasicString = "Basic";
private const string WebSocketVersionString = "WEBSOCKET_VERSION";
internal unsafe IISHttpContext(MemoryPool<byte> memoryPool, IntPtr pInProcessHandler, IISOptions options, IISHttpServer server) internal unsafe IISHttpContext(MemoryPool<byte> memoryPool, IntPtr pInProcessHandler, IISOptions options, IISHttpServer server)
: base((HttpApiTypes.HTTP_REQUEST*)NativeMethods.http_get_raw_request(pInProcessHandler)) : base((HttpApiTypes.HTTP_REQUEST*)NativeMethods.http_get_raw_request(pInProcessHandler))
@ -121,7 +123,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
StatusCode = 200; StatusCode = 200;
RequestHeaders = new RequestHeaders(this); RequestHeaders = new RequestHeaders(this);
HttpResponseHeaders = new HeaderCollection(); // TODO Optimize for known headers HttpResponseHeaders = new HeaderCollection();
ResponseHeaders = HttpResponseHeaders; ResponseHeaders = HttpResponseHeaders;
if (options.ForwardWindowsAuthentication) if (options.ForwardWindowsAuthentication)
@ -134,6 +136,26 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
} }
ResetFeatureCollection(); ResetFeatureCollection();
// Check if the Http upgrade feature is available in IIS.
// To check this, we can look at the server variable WEBSOCKET_VERSION
// And see if there is a version. Same check that Katana did:
// https://github.com/aspnet/AspNetKatana/blob/9f6e09af6bf203744feb5347121fe25f6eec06d8/src/Microsoft.Owin.Host.SystemWeb/OwinAppContext.cs#L125
// Actively not locking here as acquiring a lock on every request will hurt perf more than checking the
// server variables a few extra times if a bunch of requests hit the server at the same time.
if (_websocketAvailability == WebsocketAvailabilityStatus.Uninitialized)
{
NativeMethods.http_get_server_variable(pInProcessHandler, WebSocketVersionString, out var webSocketsSupported);
var webSocketsAvailable = !string.IsNullOrEmpty(webSocketsSupported);
_websocketAvailability = webSocketsAvailable ?
WebsocketAvailabilityStatus.Available :
WebsocketAvailabilityStatus.NotAvailable;
}
if (_websocketAvailability == WebsocketAvailabilityStatus.NotAvailable)
{
_currentIHttpUpgradeFeature = null;
}
} }
RequestBody = new IISHttpRequestBody(this); RequestBody = new IISHttpRequestBody(this);
@ -534,5 +556,12 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
} }
return null; return null;
} }
private enum WebsocketAvailabilityStatus
{
Uninitialized = 1,
Available,
NotAvailable
}
} }
} }

View File

@ -19,6 +19,7 @@ namespace Microsoft.AspNetCore.Hosting
private static readonly string ServerPath = "APPL_PATH"; private static readonly string ServerPath = "APPL_PATH";
private static readonly string PairingToken = "TOKEN"; private static readonly string PairingToken = "TOKEN";
private static readonly string IISAuth = "IIS_HTTPAUTH"; private static readonly string IISAuth = "IIS_HTTPAUTH";
private static readonly string IISWebSockets = "IIS_WEBSOCKETS_SUPPORTED";
/// <summary> /// <summary>
/// Configures the port and base path the server should listen on when running behind AspNetCoreModule. /// Configures the port and base path the server should listen on when running behind AspNetCoreModule.
@ -74,6 +75,14 @@ namespace Microsoft.AspNetCore.Hosting
var path = hostBuilder.GetSetting(ServerPath) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{ServerPath}"); var path = hostBuilder.GetSetting(ServerPath) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{ServerPath}");
var pairingToken = hostBuilder.GetSetting(PairingToken) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{PairingToken}"); var pairingToken = hostBuilder.GetSetting(PairingToken) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{PairingToken}");
var iisAuth = hostBuilder.GetSetting(IISAuth) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{IISAuth}"); var iisAuth = hostBuilder.GetSetting(IISAuth) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{IISAuth}");
var websocketsSupported = hostBuilder.GetSetting(IISWebSockets) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{IISWebSockets}");
bool isWebSocketsSupported;
if (!bool.TryParse(websocketsSupported, out isWebSocketsSupported))
{
// If the websocket support variable is not set, we will always fallback to assuming websockets are enabled.
isWebSocketsSupported = (Environment.OSVersion.Version >= new Version(6, 2));
}
if (!string.IsNullOrEmpty(port) && !string.IsNullOrEmpty(path) && !string.IsNullOrEmpty(pairingToken)) if (!string.IsNullOrEmpty(port) && !string.IsNullOrEmpty(path) && !string.IsNullOrEmpty(pairingToken))
{ {
@ -107,7 +116,7 @@ namespace Microsoft.AspNetCore.Hosting
// Delay register the url so users don't accidently overwrite it. // Delay register the url so users don't accidently overwrite it.
hostBuilder.UseSetting(WebHostDefaults.ServerUrlsKey, address); hostBuilder.UseSetting(WebHostDefaults.ServerUrlsKey, address);
hostBuilder.PreferHostingUrls(true); hostBuilder.PreferHostingUrls(true);
services.AddSingleton<IStartupFilter>(new IISSetupFilter(pairingToken, new PathString(path))); services.AddSingleton<IStartupFilter>(new IISSetupFilter(pairingToken, new PathString(path), isWebSocketsSupported));
services.Configure<ForwardedHeadersOptions>(options => services.Configure<ForwardedHeadersOptions>(options =>
{ {
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;

View File

@ -208,6 +208,7 @@
<ItemGroup> <ItemGroup>
<ClInclude Include="aspnetcore_event.h" /> <ClInclude Include="aspnetcore_event.h" />
<ClInclude Include="disconnectcontext.h" /> <ClInclude Include="disconnectcontext.h" />
<ClInclude Include="environmentvariablehelpers.h" />
<ClInclude Include="sttimer.h" /> <ClInclude Include="sttimer.h" />
<ClInclude Include="outofprocess\forwarderconnection.h" /> <ClInclude Include="outofprocess\forwarderconnection.h" />
<ClInclude Include="outofprocess\processmanager.h" /> <ClInclude Include="outofprocess\processmanager.h" />

View File

@ -80,6 +80,7 @@
<ClInclude Include="aspnetcore_event.h" /> <ClInclude Include="aspnetcore_event.h" />
<ClInclude Include="sttimer.h" /> <ClInclude Include="sttimer.h" />
<ClInclude Include="disconnectcontext.h" /> <ClInclude Include="disconnectcontext.h" />
<ClInclude Include="environmentvariablehelpers.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Source.def" /> <None Include="Source.def" />

View File

@ -112,8 +112,6 @@ InitializeGlobalConfiguration(
g_fNsiApiNotSupported = TRUE; g_fNsiApiNotSupported = TRUE;
} }
// WebSocket is supported on Win8 and above only
// todo: test on win7
g_fWebSocketSupported = IsWindows8OrGreater(); g_fWebSocketSupported = IsWindows8OrGreater();
g_fGlobalInitialize = TRUE; g_fGlobalInitialize = TRUE;

View File

@ -0,0 +1,421 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
#include "precomp.hxx"
class ENVIRONMENT_VAR_HELPERS
{
public:
static
VOID
CopyToMultiSz(
ENVIRONMENT_VAR_ENTRY * pEntry,
PVOID pvData
)
{
STRU strTemp;
MULTISZ *pMultiSz = static_cast<MULTISZ *>(pvData);
DBG_ASSERT(pMultiSz);
DBG_ASSERT(pEntry);
strTemp.Copy(pEntry->QueryName());
strTemp.Append(pEntry->QueryValue());
pMultiSz->Append(strTemp.QueryStr());
}
static
VOID
CopyToTable(
ENVIRONMENT_VAR_ENTRY * pEntry,
PVOID pvData
)
{
// best effort copy, ignore the failure
ENVIRONMENT_VAR_ENTRY * pNewEntry = new ENVIRONMENT_VAR_ENTRY();
if (pNewEntry != NULL)
{
pNewEntry->Initialize(pEntry->QueryName(), pEntry->QueryValue());
ENVIRONMENT_VAR_HASH *pHash = static_cast<ENVIRONMENT_VAR_HASH *>(pvData);
DBG_ASSERT(pHash);
pHash->InsertRecord(pNewEntry);
// Need to dereference as InsertRecord references it now
pNewEntry->Dereference();
}
}
static
VOID
AppendEnvironmentVariables
(
ENVIRONMENT_VAR_ENTRY * pEntry,
PVOID pvData
)
{
HRESULT hr = S_OK;
DWORD dwResult = 0;
DWORD dwError = 0;
STRU struNameBuffer;
STACK_STRU(struValueBuffer, 300);
BOOL fFound = FALSE;
HRESULT* pHr = static_cast<HRESULT*>(pvData);
// pEntry->QueryName includes the trailing =, remove it before calling stru
if (FAILED(hr = struNameBuffer.Copy(pEntry->QueryName())))
{
goto Finished;
}
dwResult = struNameBuffer.LastIndexOf(L'=');
if (dwResult != -1)
{
struNameBuffer.QueryStr()[dwResult] = L'\0';
if (FAILED(hr = struNameBuffer.SyncWithBuffer()))
{
goto Finished;
}
}
dwResult = GetEnvironmentVariable(struNameBuffer.QueryStr(), struValueBuffer.QueryStr(), struValueBuffer.QuerySizeCCH());
if (dwResult == 0)
{
dwError = GetLastError();
// Windows API (e.g., CreateProcess) allows variable with empty string value
// in such case dwResult will be 0 and dwError will also be 0
// As UI and CMD does not allow empty value, ignore this environment var
if (dwError != ERROR_ENVVAR_NOT_FOUND && dwError != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32(dwError);
goto Finished;
}
}
else if (dwResult > struValueBuffer.QuerySizeCCH())
{
// have to increase the buffer and try get environment var again
struValueBuffer.Reset();
struValueBuffer.Resize(dwResult + (DWORD)wcslen(pEntry->QueryValue()) + 2); // for null char and semicolon
dwResult = GetEnvironmentVariable(struNameBuffer.QueryStr(),
struValueBuffer.QueryStr(),
struValueBuffer.QuerySizeCCH());
if (struValueBuffer.IsEmpty())
{
hr = E_UNEXPECTED;
goto Finished;
}
fFound = TRUE;
}
else
{
fFound = TRUE;
}
if (FAILED(hr = struValueBuffer.SyncWithBuffer()))
{
goto Finished;
}
if (fFound)
{
if (FAILED(hr = struValueBuffer.Append(L";")))
{
goto Finished;
}
}
if (FAILED(hr = struValueBuffer.Append(pEntry->QueryValue())))
{
goto Finished;
}
if (FAILED(hr = pEntry->Initialize(pEntry->QueryName(), struValueBuffer.QueryStr())))
{
goto Finished;
}
Finished:
if (FAILED(hr))
{
*pHr = hr;
}
return;
}
static
VOID
SetEnvironmentVariables
(
ENVIRONMENT_VAR_ENTRY * pEntry,
PVOID pvData
)
{
UNREFERENCED_PARAMETER(pvData);
HRESULT hr = S_OK;
DWORD dwResult = 0;
STRU struNameBuffer;
HRESULT* pHr = static_cast<HRESULT*>(pvData);
// pEntry->QueryName includes the trailing =, remove it before calling SetEnvironmentVariable.
if (FAILED(hr = struNameBuffer.Copy(pEntry->QueryName())))
{
goto Finished;
}
dwResult = struNameBuffer.LastIndexOf(L'=');
if (dwResult != -1)
{
struNameBuffer.QueryStr()[dwResult] = L'\0';
if (FAILED(hr = struNameBuffer.SyncWithBuffer()))
{
goto Finished;
}
}
dwResult = SetEnvironmentVariable(struNameBuffer.QueryStr(), pEntry->QueryValue());
if (dwResult == 0)
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
Finished:
if (FAILED(hr))
{
*pHr = hr;
}
return;
}
static
HRESULT
InitEnvironmentVariablesTable
(
_In_ ENVIRONMENT_VAR_HASH* pInEnvironmentVarTable,
_In_ BOOL fWindowsAuthEnabled,
_In_ BOOL fBasicAuthEnabled,
_In_ BOOL fAnonymousAuthEnabled,
_Out_ ENVIRONMENT_VAR_HASH** ppEnvironmentVarTable
)
{
HRESULT hr = S_OK;
BOOL fFound = FALSE;
DWORD dwResult, dwError;
STRU strIisAuthEnvValue;
STACK_STRU(strStartupAssemblyEnv, 1024);
ENVIRONMENT_VAR_ENTRY* pHostingEntry = NULL;
ENVIRONMENT_VAR_ENTRY* pIISAuthEntry = NULL;
ENVIRONMENT_VAR_HASH* pEnvironmentVarTable = NULL;
pEnvironmentVarTable = new ENVIRONMENT_VAR_HASH();
if (pEnvironmentVarTable == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
//
// few environment variables expected, small bucket size for hash table
//
if (FAILED(hr = pEnvironmentVarTable->Initialize(37 /*prime*/)))
{
goto Finished;
}
// copy the envirable hash table (from configuration) to a temp one as we may need to remove elements
pInEnvironmentVarTable->Apply(ENVIRONMENT_VAR_HELPERS::CopyToTable, pEnvironmentVarTable);
if (pEnvironmentVarTable->Count() != pInEnvironmentVarTable->Count())
{
// hash table copy failed
hr = E_UNEXPECTED;
goto Finished;
}
pEnvironmentVarTable->FindKey((PWSTR)ASPNETCORE_IIS_AUTH_ENV_STR, &pIISAuthEntry);
if (pIISAuthEntry != NULL)
{
// user defined ASPNETCORE_IIS_HTTPAUTH in configuration, wipe it off
pIISAuthEntry->Dereference();
pEnvironmentVarTable->DeleteKey((PWSTR)ASPNETCORE_IIS_AUTH_ENV_STR);
}
if (fWindowsAuthEnabled)
{
strIisAuthEnvValue.Copy(ASPNETCORE_IIS_AUTH_WINDOWS);
}
if (fBasicAuthEnabled)
{
strIisAuthEnvValue.Append(ASPNETCORE_IIS_AUTH_BASIC);
}
if (fAnonymousAuthEnabled)
{
strIisAuthEnvValue.Append(ASPNETCORE_IIS_AUTH_ANONYMOUS);
}
if (strIisAuthEnvValue.IsEmpty())
{
strIisAuthEnvValue.Copy(ASPNETCORE_IIS_AUTH_NONE);
}
pIISAuthEntry = new ENVIRONMENT_VAR_ENTRY();
if (pIISAuthEntry == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
if (FAILED(hr = pIISAuthEntry->Initialize(ASPNETCORE_IIS_AUTH_ENV_STR, strIisAuthEnvValue.QueryStr())) ||
FAILED(hr = pEnvironmentVarTable->InsertRecord(pIISAuthEntry)))
{
goto Finished;
}
// Compiler is complaining about conversion between PCWSTR and PWSTR here.
// Explictly casting.
pEnvironmentVarTable->FindKey((PWSTR)HOSTING_STARTUP_ASSEMBLIES_NAME, &pHostingEntry);
if (pHostingEntry != NULL)
{
// user defined ASPNETCORE_HOSTINGSTARTUPASSEMBLIES in configuration
// the value will be used in OutputEnvironmentVariables. Do nothing here
pHostingEntry->Dereference();
pHostingEntry = NULL;
goto Skipped;
}
//check whether ASPNETCORE_HOSTINGSTARTUPASSEMBLIES is defined in system
dwResult = GetEnvironmentVariable(HOSTING_STARTUP_ASSEMBLIES_ENV_STR,
strStartupAssemblyEnv.QueryStr(),
strStartupAssemblyEnv.QuerySizeCCH());
if (dwResult == 0)
{
dwError = GetLastError();
// Windows API (e.g., CreateProcess) allows variable with empty string value
// in such case dwResult will be 0 and dwError will also be 0
// As UI and CMD does not allow empty value, ignore this environment var
if (dwError != ERROR_ENVVAR_NOT_FOUND && dwError != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32(dwError);
goto Finished;
}
}
else if (dwResult > strStartupAssemblyEnv.QuerySizeCCH())
{
// have to increase the buffer and try get environment var again
strStartupAssemblyEnv.Reset();
strStartupAssemblyEnv.Resize(dwResult + (DWORD)wcslen(HOSTING_STARTUP_ASSEMBLIES_VALUE) + 1);
dwResult = GetEnvironmentVariable(HOSTING_STARTUP_ASSEMBLIES_ENV_STR,
strStartupAssemblyEnv.QueryStr(),
strStartupAssemblyEnv.QuerySizeCCH());
if (strStartupAssemblyEnv.IsEmpty())
{
hr = E_UNEXPECTED;
goto Finished;
}
fFound = TRUE;
}
else
{
fFound = TRUE;
}
strStartupAssemblyEnv.SyncWithBuffer();
if (fFound)
{
strStartupAssemblyEnv.Append(L";");
}
strStartupAssemblyEnv.Append(HOSTING_STARTUP_ASSEMBLIES_VALUE);
// the environment variable was not defined, create it and add to hashtable
pHostingEntry = new ENVIRONMENT_VAR_ENTRY();
if (pHostingEntry == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
if (FAILED(hr = pHostingEntry->Initialize(HOSTING_STARTUP_ASSEMBLIES_NAME, strStartupAssemblyEnv.QueryStr())) ||
FAILED(hr = pEnvironmentVarTable->InsertRecord(pHostingEntry)))
{
goto Finished;
}
Skipped:
*ppEnvironmentVarTable = pEnvironmentVarTable;
pEnvironmentVarTable = NULL;
Finished:
if (pHostingEntry != NULL)
{
pHostingEntry->Dereference();
pHostingEntry = NULL;
}
if (pIISAuthEntry != NULL)
{
pIISAuthEntry->Dereference();
pIISAuthEntry = NULL;
}
if (pEnvironmentVarTable != NULL)
{
pEnvironmentVarTable->Clear();
delete pEnvironmentVarTable;
pEnvironmentVarTable = NULL;
}
return hr;
}
static
HRESULT
AddWebsocketEnabledToEnvironmentVariables
(
_Inout_ ENVIRONMENT_VAR_HASH* pInEnvironmentVarTable,
_In_ BOOL fWebsocketsEnabled
)
{
HRESULT hr = S_OK;
ENVIRONMENT_VAR_ENTRY* pIISWebsocketEntry = NULL;
STACK_STRU(strIISWebsocketEnvValue, 40);
// We only need to set the WEBSOCKET_SUPPORTED environment variable for out of process
pInEnvironmentVarTable->FindKey((PWSTR)ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED_ENV_STR, &pIISWebsocketEntry);
if (pIISWebsocketEntry != NULL)
{
// user defined ASPNETCORE_IIS_WEBSOCKETS in configuration, wipe it off
pIISWebsocketEntry->Dereference();
pInEnvironmentVarTable->DeleteKey((PWSTR)ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED_ENV_STR);
}
// Set either true or false for the WebsocketEnvValue.
if (fWebsocketsEnabled)
{
if (FAILED(hr = strIISWebsocketEnvValue.Copy(L"true")))
{
goto Finished;
}
}
else
{
if (FAILED(hr = strIISWebsocketEnvValue.Copy(L"false")))
{
goto Finished;
}
}
pIISWebsocketEntry = new ENVIRONMENT_VAR_ENTRY();
if (pIISWebsocketEntry == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
if (FAILED(hr = pIISWebsocketEntry->Initialize(ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED_ENV_STR, strIISWebsocketEnvValue.QueryStr())) ||
FAILED(hr = pInEnvironmentVarTable->InsertRecord(pIISWebsocketEntry)))
{
goto Finished;
}
Finished:
return hr;
}
public:
ENVIRONMENT_VAR_HELPERS();
~ENVIRONMENT_VAR_HELPERS();
};

View File

@ -726,7 +726,7 @@ IN_PROCESS_APPLICATION::SetEnvironementVariablesOnWorkerProcess(
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
ENVIRONMENT_VAR_HASH* pHashTable = NULL; ENVIRONMENT_VAR_HASH* pHashTable = NULL;
if (FAILED(hr = ENVIRONMENT_VAR_HASH::InitEnvironmentVariablesTable( if (FAILED(hr = ENVIRONMENT_VAR_HELPERS::InitEnvironmentVariablesTable(
m_pConfig->QueryEnvironmentVariables(), m_pConfig->QueryEnvironmentVariables(),
m_pConfig->QueryWindowsAuthEnabled(), m_pConfig->QueryWindowsAuthEnabled(),
m_pConfig->QueryBasicAuthEnabled(), m_pConfig->QueryBasicAuthEnabled(),
@ -736,8 +736,16 @@ IN_PROCESS_APPLICATION::SetEnvironementVariablesOnWorkerProcess(
goto Finished; goto Finished;
} }
pHashTable->Apply(ENVIRONMENT_VAR_HASH::AppendEnvironmentVariables, NULL); pHashTable->Apply(ENVIRONMENT_VAR_HELPERS::AppendEnvironmentVariables, &hr);
pHashTable->Apply(ENVIRONMENT_VAR_HASH::SetEnvironmentVariables, NULL); if (FAILED(hr))
{
goto Finished;
}
pHashTable->Apply(ENVIRONMENT_VAR_HELPERS::SetEnvironmentVariables, &hr);
if (FAILED(hr))
{
goto Finished;
}
Finished: Finished:
return hr; return hr;
} }
@ -770,15 +778,15 @@ IN_PROCESS_APPLICATION::ExecuteApplication(
goto Finished; goto Finished;
} }
// There can only ever be a single instance of .NET Core
// loaded in the process but we need to get config information to boot it up in the
// first place. This is happening in an execute request handler and everyone waits
// until this initialization is done.
if (FAILED(hr = SetEnvironementVariablesOnWorkerProcess())) if (FAILED(hr = SetEnvironementVariablesOnWorkerProcess()))
{ {
goto Finished; goto Finished;
} }
// There can only ever be a single instance of .NET Core
// loaded in the process but we need to get config information to boot it up in the
// first place. This is happening in an execute request handler and everyone waits
// until this initialization is done.
// We set a static so that managed code can call back into this instance and // We set a static so that managed code can call back into this instance and
// set the callbacks // set the callbacks
s_Application = this; s_Application = this;

View File

@ -225,6 +225,7 @@ PROCESS_MANAGER::GetProcess(
pConfig->QueryAnonymousAuthEnabled(), pConfig->QueryAnonymousAuthEnabled(),
pConfig->QueryEnvironmentVariables(), pConfig->QueryEnvironmentVariables(),
pConfig->QueryStdoutLogEnabled(), pConfig->QueryStdoutLogEnabled(),
pConfig->QueryWebSocketEnabled(),
pConfig->QueryStdoutLogFile(), pConfig->QueryStdoutLogFile(),
pConfig->QueryApplicationPhysicalPath(), // physical path pConfig->QueryApplicationPhysicalPath(), // physical path
pConfig->QueryApplicationPath(), // app path pConfig->QueryApplicationPath(), // app path

View File

@ -22,6 +22,7 @@ SERVER_PROCESS::Initialize(
BOOL fAnonymousAuthEnabled, BOOL fAnonymousAuthEnabled,
ENVIRONMENT_VAR_HASH *pEnvironmentVariables, ENVIRONMENT_VAR_HASH *pEnvironmentVariables,
BOOL fStdoutLogEnabled, BOOL fStdoutLogEnabled,
BOOL fWebsocketsEnabled,
STRU *pstruStdoutLogFile, STRU *pstruStdoutLogFile,
STRU *pszAppPhysicalPath, STRU *pszAppPhysicalPath,
STRU *pszAppPath, STRU *pszAppPath,
@ -38,6 +39,7 @@ SERVER_PROCESS::Initialize(
m_fWindowsAuthEnabled = fWindowsAuthEnabled; m_fWindowsAuthEnabled = fWindowsAuthEnabled;
m_fBasicAuthEnabled = fBasicAuthEnabled; m_fBasicAuthEnabled = fBasicAuthEnabled;
m_fAnonymousAuthEnabled = fAnonymousAuthEnabled; m_fAnonymousAuthEnabled = fAnonymousAuthEnabled;
m_fWebsocketsEnabled = fWebsocketsEnabled;
m_pProcessManager->ReferenceProcessManager(); m_pProcessManager->ReferenceProcessManager();
if (FAILED(hr = m_ProcessPath.Copy(*pszProcessExePath)) || if (FAILED(hr = m_ProcessPath.Copy(*pszProcessExePath)) ||
@ -402,7 +404,7 @@ SERVER_PROCESS::OutputEnvironmentVariables
pszCurrentVariable = pszNextVariable; pszCurrentVariable = pszNextVariable;
} }
// append the remaining env variable in hash table // append the remaining env variable in hash table
pEnvironmentVarTable->Apply(ENVIRONMENT_VAR_HASH::CopyToMultiSz, pmszOutput); pEnvironmentVarTable->Apply(ENVIRONMENT_VAR_HELPERS::CopyToMultiSz, pmszOutput);
Finished: Finished:
if (pszEnvironmentVariables != NULL) if (pszEnvironmentVariables != NULL)
@ -755,7 +757,7 @@ SERVER_PROCESS::StartProcess(
goto Failure; goto Failure;
} }
if (FAILED(hr = ENVIRONMENT_VAR_HASH::InitEnvironmentVariablesTable( if (FAILED(hr = ENVIRONMENT_VAR_HELPERS::InitEnvironmentVariablesTable(
m_pEnvironmentVarTable, m_pEnvironmentVarTable,
m_fWindowsAuthEnabled, m_fWindowsAuthEnabled,
m_fBasicAuthEnabled, m_fBasicAuthEnabled,
@ -766,6 +768,16 @@ SERVER_PROCESS::StartProcess(
goto Failure; goto Failure;
} }
if (FAILED(hr = ENVIRONMENT_VAR_HELPERS::AddWebsocketEnabledToEnvironmentVariables(
pHashTable,
m_fWebsocketsEnabled
)))
{
pStrStage = L"AddWebsocketEnabledToEnvironmentVariables";
goto Failure;
}
// //
// setup the the port that the backend process will listen on // setup the the port that the backend process will listen on
// //

View File

@ -33,6 +33,7 @@ public:
_In_ BOOL fAnonymousAuthEnabled, _In_ BOOL fAnonymousAuthEnabled,
_In_ ENVIRONMENT_VAR_HASH* pEnvironmentVariables, _In_ ENVIRONMENT_VAR_HASH* pEnvironmentVariables,
_In_ BOOL fStdoutLogEnabled, _In_ BOOL fStdoutLogEnabled,
_In_ BOOL fWebsocketsEnabled,
_In_ STRU *pstruStdoutLogFile, _In_ STRU *pstruStdoutLogFile,
_In_ STRU *pszAppPhysicalPath, _In_ STRU *pszAppPhysicalPath,
_In_ STRU *pszAppPath, _In_ STRU *pszAppPath,
@ -222,6 +223,7 @@ private:
BOOL m_fWindowsAuthEnabled; BOOL m_fWindowsAuthEnabled;
BOOL m_fBasicAuthEnabled; BOOL m_fBasicAuthEnabled;
BOOL m_fAnonymousAuthEnabled; BOOL m_fAnonymousAuthEnabled;
BOOL m_fWebsocketsEnabled;
STTIMER m_Timer; STTIMER m_Timer;
SOCKET m_socket; SOCKET m_socket;

View File

@ -59,6 +59,7 @@
#include "aspnetcore_event.h" #include "aspnetcore_event.h"
#include "aspnetcore_msg.h" #include "aspnetcore_msg.h"
#include "disconnectcontext.h" #include "disconnectcontext.h"
#include "environmentvariablehelpers.h"
#include "sttimer.h" #include "sttimer.h"
#include ".\inprocess\InProcessHandler.h" #include ".\inprocess\InProcessHandler.h"
#include ".\inprocess\inprocessapplication.h" #include ".\inprocess\inprocessapplication.h"

View File

@ -212,9 +212,9 @@
--> -->
<globalModules> <globalModules>
<add name="UriCacheModule" image="%IIS_BIN%\cachuri.dll" /> <add name="UriCacheModule" image="%IIS_BIN%\cachuri.dll" />
<!-- <add name="FileCacheModule" image="%IIS_BIN%\cachfile.dll" /> --> <!-- <add name="FileCacheModule" image="%IIS_BIN%\cachfile.dll" /> -->
<add name="TokenCacheModule" image="%IIS_BIN%\cachtokn.dll" /> <add name="TokenCacheModule" image="%IIS_BIN%\cachtokn.dll" />
<!-- <add name="HttpCacheModule" image="%IIS_BIN%\cachhttp.dll" /> --> <!-- <add name="HttpCacheModule" image="%IIS_BIN%\cachhttp.dll" /> -->
<add name="DynamicCompressionModule" image="%IIS_BIN%\compdyn.dll" /> <add name="DynamicCompressionModule" image="%IIS_BIN%\compdyn.dll" />
<add name="StaticCompressionModule" image="%IIS_BIN%\compstat.dll" /> <add name="StaticCompressionModule" image="%IIS_BIN%\compstat.dll" />
<add name="DefaultDocumentModule" image="%IIS_BIN%\defdoc.dll" /> <add name="DefaultDocumentModule" image="%IIS_BIN%\defdoc.dll" />
@ -228,7 +228,7 @@
<add name="UrlAuthorizationModule" image="%IIS_BIN%\urlauthz.dll" /> <add name="UrlAuthorizationModule" image="%IIS_BIN%\urlauthz.dll" />
<add name="BasicAuthenticationModule" image="%IIS_BIN%\authbas.dll" /> <add name="BasicAuthenticationModule" image="%IIS_BIN%\authbas.dll" />
<add name="WindowsAuthenticationModule" image="%IIS_BIN%\authsspi.dll" /> <add name="WindowsAuthenticationModule" image="%IIS_BIN%\authsspi.dll" />
<!-- <add name="DigestAuthenticationModule" image="%IIS_BIN%\authmd5.dll" /> --> <!-- <add name="DigestAuthenticationModule" image="%IIS_BIN%\authmd5.dll" /> -->
<add name="IISCertificateMappingAuthenticationModule" image="%IIS_BIN%\authmap.dll" /> <add name="IISCertificateMappingAuthenticationModule" image="%IIS_BIN%\authmap.dll" />
<add name="IpRestrictionModule" image="%IIS_BIN%\iprestr.dll" /> <add name="IpRestrictionModule" image="%IIS_BIN%\iprestr.dll" />
<add name="DynamicIpRestrictionModule" image="%IIS_BIN%\diprestr.dll" /> <add name="DynamicIpRestrictionModule" image="%IIS_BIN%\diprestr.dll" />
@ -236,14 +236,14 @@
<add name="CustomLoggingModule" image="%IIS_BIN%\logcust.dll" /> <add name="CustomLoggingModule" image="%IIS_BIN%\logcust.dll" />
<add name="CustomErrorModule" image="%IIS_BIN%\custerr.dll" /> <add name="CustomErrorModule" image="%IIS_BIN%\custerr.dll" />
<add name="HttpLoggingModule" image="%IIS_BIN%\loghttp.dll" /> <add name="HttpLoggingModule" image="%IIS_BIN%\loghttp.dll" />
<!-- <add name="TracingModule" image="%IIS_BIN%\iisetw.dll" /> --> <!-- <add name="TracingModule" image="%IIS_BIN%\iisetw.dll" /> -->
<add name="FailedRequestsTracingModule" image="%IIS_BIN%\iisfreb.dll" /> <add name="FailedRequestsTracingModule" image="%IIS_BIN%\iisfreb.dll" />
<add name="RequestMonitorModule" image="%IIS_BIN%\iisreqs.dll" /> <add name="RequestMonitorModule" image="%IIS_BIN%\iisreqs.dll" />
<add name="IsapiModule" image="%IIS_BIN%\isapi.dll" /> <add name="IsapiModule" image="%IIS_BIN%\isapi.dll" />
<add name="IsapiFilterModule" image="%IIS_BIN%\filter.dll" /> <add name="IsapiFilterModule" image="%IIS_BIN%\filter.dll" />
<add name="CgiModule" image="%IIS_BIN%\cgi.dll" /> <add name="CgiModule" image="%IIS_BIN%\cgi.dll" />
<add name="FastCgiModule" image="%IIS_BIN%\iisfcgi.dll" /> <add name="FastCgiModule" image="%IIS_BIN%\iisfcgi.dll" />
<!-- <add name="WebDAVModule" image="%IIS_BIN%\webdav.dll" /> --> <!-- <add name="WebDAVModule" image="%IIS_BIN%\webdav.dll" /> -->
<add name="RewriteModule" image="%IIS_BIN%\rewrite.dll" /> <add name="RewriteModule" image="%IIS_BIN%\rewrite.dll" />
<add name="ConfigurationValidationModule" image="%IIS_BIN%\validcfg.dll" /> <add name="ConfigurationValidationModule" image="%IIS_BIN%\validcfg.dll" />
<add name="ApplicationInitializationModule" image="%IIS_BIN%\warmup.dll" /> <add name="ApplicationInitializationModule" image="%IIS_BIN%\warmup.dll" />
@ -799,7 +799,7 @@
<tracing> <tracing>
<traceProviderDefinitions> <traceProviderDefinitions>
<add name="WWW Server" guid="{3a2a4e84-4c21-4981-ae10-3fda0d9b0f83}"> <add name="WWW Server" guid="{3a2a4e84-4c21-4981-ae10-3fda0d9b0f83}">
<areas> <areas>
<clear /> <clear />
@ -870,7 +870,7 @@
<authoringRules /> <authoringRules />
</webdav> </webdav>
<applicationInitialization /> <applicationInitialization />
<webSocket /> <webSocket enabled="false" />
</system.webServer> </system.webServer>
<location path="" overrideMode="Allow"> <location path="" overrideMode="Allow">

View File

@ -5,15 +5,19 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Content Include="*.config" CopyToOutputDirectory="PreserveNewest" /> <Content Include="AppHostConfig\*.config" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.IISIntegration\Microsoft.AspNetCore.Server.IISIntegration.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Server.IntegrationTesting" Version="$(MicrosoftAspNetCoreServerIntegrationTestingPackageVersion)" /> <PackageReference Include="Microsoft.AspNetCore.Server.IntegrationTesting" Version="$(MicrosoftAspNetCoreServerIntegrationTestingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="$(MicrosoftExtensionsLoggingDebugPackageVersion)" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="$(MicrosoftExtensionsLoggingDebugPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" /> <PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
<PackageReference Include="xunit" Version="$(XunitPackageVersion)" /> <PackageReference Include="xunit" Version="$(XunitPackageVersion)" />
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitRunnerVisualStudioPackageVersion)" /> <PackageReference Include="xunit.runner.visualstudio" Version="$(XunitRunnerVisualStudioPackageVersion)" />

View File

@ -6,11 +6,10 @@
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
using Xunit; using Xunit;
namespace IISIntegration.IISServerFunctionalTests namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
[Collection(IISTestSiteCollection.Name)] [Collection(IISTestSiteCollection.Name)]
public class AuthenticationTests public class AuthenticationTests

View File

@ -5,7 +5,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
[Collection(IISTestSiteCollection.Name)] [Collection(IISTestSiteCollection.Name)]
public class EnvironmentVariableTests public class EnvironmentVariableTests

View File

@ -6,7 +6,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
[Collection(IISTestSiteCollection.Name)] [Collection(IISTestSiteCollection.Name)]
public class FeatureCollectionTest public class FeatureCollectionTest

View File

@ -6,14 +6,14 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
[Collection(IISTestSiteCollection.Name)] [Collection(IISTestSiteCollection.Name)]
public class HelloWorldTests public class HelloWorldInProcessTests
{ {
private readonly IISTestSiteFixture _fixture; private readonly IISTestSiteFixture _fixture;
public HelloWorldTests(IISTestSiteFixture fixture) public HelloWorldInProcessTests(IISTestSiteFixture fixture)
{ {
_fixture = fixture; _fixture = fixture;
} }

View File

@ -5,7 +5,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
[Collection(IISTestSiteCollection.Name)] [Collection(IISTestSiteCollection.Name)]
public class LargeResponseBodyTests public class LargeResponseBodyTests

View File

@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
[Collection(IISTestSiteCollection.Name)] [Collection(IISTestSiteCollection.Name)]

View File

@ -5,7 +5,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
[Collection(IISTestSiteCollection.Name)] [Collection(IISTestSiteCollection.Name)]
public class ResponseInvalidOrderingTest public class ResponseInvalidOrderingTest

View File

@ -5,7 +5,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
[Collection(IISTestSiteCollection.Name)] [Collection(IISTestSiteCollection.Name)]
public class ServerVariablesTest public class ServerVariablesTest

View File

@ -41,10 +41,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
var logger = loggerFactory.CreateLogger("HelloWorldTest"); var logger = loggerFactory.CreateLogger("HelloWorldTest");
var deploymentParameters = new DeploymentParameters(Helpers.GetTestSitesPath(), serverType, runtimeFlavor, architecture) var deploymentParameters = new DeploymentParameters(Helpers.GetOutOfProcessTestSitesPath(), serverType, runtimeFlavor, architecture)
{ {
EnvironmentName = "HelloWorld", // Will pick the Start class named 'StartupHelloWorld', EnvironmentName = "HelloWorld", // Will pick the Start class named 'StartupHelloWorld',
ServerConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("Http.config") : null, ServerConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("AppHostConfig/Http.config") : null,
SiteName = "HttpTestSite", // This is configured in the Http.config SiteName = "HttpTestSite", // This is configured in the Http.config
TargetFramework = runtimeFlavor == RuntimeFlavor.Clr ? "net461" : "netcoreapp2.0", TargetFramework = runtimeFlavor == RuntimeFlavor.Clr ? "net461" : "netcoreapp2.0",
ApplicationType = applicationType, ApplicationType = applicationType,

View File

@ -48,11 +48,11 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
var logger = loggerFactory.CreateLogger("HttpsHelloWorldTest"); var logger = loggerFactory.CreateLogger("HttpsHelloWorldTest");
var deploymentParameters = new DeploymentParameters(Helpers.GetTestSitesPath(), serverType, runtimeFlavor, architecture) var deploymentParameters = new DeploymentParameters(Helpers.GetOutOfProcessTestSitesPath(), serverType, runtimeFlavor, architecture)
{ {
ApplicationBaseUriHint = applicationBaseUrl, ApplicationBaseUriHint = applicationBaseUrl,
EnvironmentName = "HttpsHelloWorld", // Will pick the Start class named 'StartupHttpsHelloWorld', EnvironmentName = "HttpsHelloWorld", // Will pick the Start class named 'StartupHttpsHelloWorld',
ServerConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("Https.config") : null, ServerConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("AppHostConfig/Https.config") : null,
SiteName = "HttpsTestSite", // This is configured in the Https.config SiteName = "HttpsTestSite", // This is configured in the Https.config
TargetFramework = runtimeFlavor == RuntimeFlavor.Clr ? "net461" : "netcoreapp2.0", TargetFramework = runtimeFlavor == RuntimeFlavor.Clr ? "net461" : "netcoreapp2.0",
ApplicationType = applicationType, ApplicationType = applicationType,
@ -131,7 +131,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
var logger = loggerFactory.CreateLogger("HttpsHelloWorldTest"); var logger = loggerFactory.CreateLogger("HttpsHelloWorldTest");
var deploymentParameters = new DeploymentParameters(Helpers.GetTestSitesPath(), serverType, runtimeFlavor, architecture) var deploymentParameters = new DeploymentParameters(Helpers.GetOutOfProcessTestSitesPath(), serverType, runtimeFlavor, architecture)
{ {
ApplicationBaseUriHint = applicationBaseUrl, ApplicationBaseUriHint = applicationBaseUrl,
EnvironmentName = "HttpsHelloWorld", // Will pick the Start class named 'StartupHttpsHelloWorld', EnvironmentName = "HttpsHelloWorld", // Will pick the Start class named 'StartupHttpsHelloWorld',

View File

@ -50,11 +50,11 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
? "win7-x64" ? "win7-x64"
: "win7-x86"; : "win7-x86";
var deploymentParameters = new DeploymentParameters(Helpers.GetTestSitesPath(), serverType, runtimeFlavor, architecture) var deploymentParameters = new DeploymentParameters(Helpers.GetOutOfProcessTestSitesPath(), serverType, runtimeFlavor, architecture)
{ {
ApplicationBaseUriHint = $"http://localhost:{port}", ApplicationBaseUriHint = $"http://localhost:{port}",
EnvironmentName = "NtlmAuthentication", // Will pick the Start class named 'StartupNtlmAuthentication' EnvironmentName = "NtlmAuthentication", // Will pick the Start class named 'StartupNtlmAuthentication'
ServerConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("NtlmAuthentation.config") : null, ServerConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("AppHostConfig/NtlmAuthentation.config") : null,
SiteName = "NtlmAuthenticationTestSite", // This is configured in the NtlmAuthentication.config SiteName = "NtlmAuthenticationTestSite", // This is configured in the NtlmAuthentication.config
TargetFramework = runtimeFlavor == RuntimeFlavor.Clr ? "net461" : "netcoreapp2.0", TargetFramework = runtimeFlavor == RuntimeFlavor.Clr ? "net461" : "netcoreapp2.0",
ApplicationType = applicationType, ApplicationType = applicationType,

View File

@ -1,3 +1,5 @@
using Xunit; // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)] // All functional tests in this project require a version of IIS express with an updated schema
[assembly: Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests.IISExpressSupportsInProcessHosting]

View File

@ -0,0 +1,117 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
public class UpgradeFeatureDetectionTests : LoggedTest
{
public UpgradeFeatureDetectionTests(ITestOutputHelper output) : base(output)
{
}
[Fact]
public Task UpgradeFeatureDetectionEnabled_InProcess_IISExpress_CoreClr_x64_Portable()
{
return UpgradeFeatureDetectionDeployer(RuntimeFlavor.CoreClr,
ApplicationType.Portable,
"AppHostConfig/WebsocketsNotSupported.config",
Helpers.GetInProcessTestSitesPath(),
"Disabled");
}
[Fact]
public Task UpgradeFeatureDetectionDisabled_InProcess_IISExpress_CoreClr_x64_Portable()
{
return UpgradeFeatureDetectionDeployer(RuntimeFlavor.CoreClr,
ApplicationType.Portable,
"AppHostConfig/Http.config",
Helpers.GetInProcessTestSitesPath(),
"Enabled");
}
[Fact]
public Task UpgradeFeatureDetectionEnabled_OutOfProcess_IISExpress_CoreClr_x64_Portable()
{
return UpgradeFeatureDetectionDeployer(RuntimeFlavor.CoreClr,
ApplicationType.Portable,
"AppHostConfig/WebsocketsNotSupported.config",
Helpers.GetOutOfProcessTestSitesPath(),
"Disabled");
}
[Fact]
public Task UpgradeFeatureDetectionDisabled_OutOfProcess_IISExpress_CoreClr_x64_Portable()
{
return UpgradeFeatureDetectionDeployer(RuntimeFlavor.CoreClr,
ApplicationType.Portable,
"AppHostConfig/Http.config",
Helpers.GetOutOfProcessTestSitesPath(),
"Enabled");
}
private async Task UpgradeFeatureDetectionDeployer(RuntimeFlavor runtimeFlavor,
ApplicationType applicationType,
string configPath,
string sitePath,
string expected)
{
var serverType = ServerType.IISExpress;
var architecture = RuntimeArchitecture.x64;
var testName = $"HelloWorld_{runtimeFlavor}";
using (StartLog(out var loggerFactory, testName))
{
var logger = loggerFactory.CreateLogger("HelloWorldTest");
var deploymentParameters = new DeploymentParameters(Helpers.GetInProcessTestSitesPath(), serverType, runtimeFlavor, architecture)
{
EnvironmentName = "UpgradeFeatureDetection", // Will pick the Start class named 'StartupHelloWorld',
ServerConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText(configPath) : null,
SiteName = "HttpTestSite", // This is configured in the Http.config
TargetFramework = "netcoreapp2.0",
ApplicationType = applicationType,
Configuration =
#if DEBUG
"Debug"
#else
"Release"
#endif
};
using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory))
{
var deploymentResult = await deployer.DeployAsync();
deploymentResult.HttpClient.Timeout = TimeSpan.FromSeconds(5);
// Request to base address and check if various parts of the body are rendered & measure the cold startup time.
var response = await RetryHelper.RetryRequest(() =>
{
return deploymentResult.HttpClient.GetAsync("UpgradeFeatureDetection");
}, logger, deploymentResult.HostShutdownToken, retryCount: 30);
var responseText = await response.Content.ReadAsStringAsync();
try
{
Assert.Equal(expected, responseText);
}
catch (XunitException)
{
logger.LogWarning(response.ToString());
logger.LogWarning(responseText);
throw;
}
}
}
}
}
}

View File

@ -8,7 +8,18 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
public class Helpers public class Helpers
{ {
public static string GetTestSitesPath() public static string GetInProcessTestSitesPath()
{
return Path.GetFullPath(
Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
"..", // tfm
"..", // debug
"..", // obj
"..", // projectfolder
"IISTestSite"));
}
public static string GetOutOfProcessTestSitesPath()
{ {
return Path.GetFullPath( return Path.GetFullPath(
Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Path.Combine(AppDomain.CurrentDomain.BaseDirectory,

View File

@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Assembly | AttributeTargets.Class)] [AttributeUsage(AttributeTargets.Method | AttributeTargets.Assembly | AttributeTargets.Class)]
public sealed class IISExpressSupportsInProcessHostingAttribute : Attribute, ITestCondition public sealed class IISExpressSupportsInProcessHostingAttribute : Attribute, ITestCondition

View File

@ -3,7 +3,7 @@
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
/// <summary> /// <summary>
/// This type just maps collection names to available fixtures /// This type just maps collection names to available fixtures

View File

@ -8,7 +8,7 @@ using System.Threading;
using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
public class IISTestSiteFixture : IDisposable public class IISTestSiteFixture : IDisposable
{ {
@ -16,12 +16,12 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
public IISTestSiteFixture() public IISTestSiteFixture()
{ {
var deploymentParameters = new DeploymentParameters(Helpers.GetTestSitesPath(), var deploymentParameters = new DeploymentParameters(Helpers.GetInProcessTestSitesPath(),
ServerType.IISExpress, ServerType.IISExpress,
RuntimeFlavor.CoreClr, RuntimeFlavor.CoreClr,
RuntimeArchitecture.x64) RuntimeArchitecture.x64)
{ {
ServerConfigTemplateContent = File.ReadAllText("Http.config"), ServerConfigTemplateContent = File.ReadAllText("AppHostConfig/Http.config"),
SiteName = "HttpTestSite", SiteName = "HttpTestSite",
TargetFramework = "netcoreapp2.0", TargetFramework = "netcoreapp2.0",
ApplicationType = ApplicationType.Portable, ApplicationType = ApplicationType.Portable,

View File

@ -1,26 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
<RootNamespace>Microsoft.AspNetCore.Server.IIS.FunctionalTests</RootNamespace>
<AssemblyName>Microsoft.AspNetCore.Server.IIS.FunctionalTests</AssemblyName>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Server.IISIntegration\Microsoft.AspNetCore.Server.IISIntegration.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="*.config" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Server.IntegrationTesting" Version="$(MicrosoftAspNetCoreServerIntegrationTestingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="$(MicrosoftExtensionsLoggingDebugPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkPackageVersion)" />
<PackageReference Include="xunit" Version="$(XunitPackageVersion)" />
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitRunnerVisualStudioPackageVersion)" />
</ItemGroup>
</Project>

View File

@ -1,5 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// All functional tests in this project require a version of IIS express with an updated schema
[assembly: Microsoft.AspNetCore.Server.IIS.FunctionalTests.IISExpressSupportsInProcessHosting]

View File

@ -1,22 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
{
public class Helpers
{
public static string GetTestSitesPath()
{
return Path.GetFullPath(
Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
"..", // tfm
"..", // debug
"..", // obj
"..", // projectfolder
"IISTestSite"));
}
}
}

View File

@ -43,6 +43,7 @@ namespace IISTestSite
app.Map("/ReadAndWriteEchoTwice", ReadAndWriteEchoTwice); app.Map("/ReadAndWriteEchoTwice", ReadAndWriteEchoTwice);
app.Map("/ReadAndWriteSlowConnection", ReadAndWriteSlowConnection); app.Map("/ReadAndWriteSlowConnection", ReadAndWriteSlowConnection);
app.Map("/WebsocketRequest", WebsocketRequest); app.Map("/WebsocketRequest", WebsocketRequest);
app.Map("/UpgradeFeatureDetection", UpgradeFeatureDetection);
} }
private void ServerVariable(IApplicationBuilder app) private void ServerVariable(IApplicationBuilder app)
@ -425,5 +426,20 @@ namespace IISTestSite
await context.Request.Body.CopyToAsync(context.Response.Body); await context.Request.Body.CopyToAsync(context.Response.Body);
}); });
} }
private void UpgradeFeatureDetection(IApplicationBuilder app)
{
app.Run(async ctx =>
{
if (ctx.Features.Get<IHttpUpgradeFeature>() != null)
{
await ctx.Response.WriteAsync("Enabled");
}
else
{
await ctx.Response.WriteAsync("Disabled");
}
});
}
} }
} }

View File

@ -0,0 +1,32 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace TestSites
{
public class StartupUpgradeFeatureDetection
{
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
app.Run(async ctx =>
{
if (ctx.Features.Get<IHttpUpgradeFeature>() != null)
{
await ctx.Response.WriteAsync("Enabled");
}
else
{
await ctx.Response.WriteAsync("Disabled");
}
});
}
}
}