Add IServerAddressesFeature support (#4685)

This commit is contained in:
Pavel Krymets 2018-12-20 15:29:49 -08:00 committed by GitHub
parent 5ab3c89be3
commit 45d1c054b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 585 additions and 137 deletions

View File

@ -53,7 +53,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IISExpress.FunctionalTests"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeIISSample", "samples\NativeIISSample\NativeIISSample.csproj", "{9BC4AFCB-325D-4C81-8228-8CF301CE2F97}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InProcessWebSite", "test\WebSites\InProcessWebSite\InProcessWebSite.csproj", "{679FA2A2-898B-4320-884E-C2D294A97CE1}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InProcessWebSite", "test\testassets\InProcessWebSite\InProcessWebSite.csproj", "{679FA2A2-898B-4320-884E-C2D294A97CE1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IIS", "src\Microsoft.AspNetCore.Server.IIS\Microsoft.AspNetCore.Server.IIS.csproj", "{46A8612B-418B-4D70-B3A7-A21DD0627473}"
ProjectSection(ProjectDependencies) = postProject
@ -63,7 +63,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server
{D57EA297-6DC2-4BC0-8C91-334863327863} = {D57EA297-6DC2-4BC0-8C91-334863327863}
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StressTestWebSite", "test\WebSites\StressTestWebSite\StressTestWebSite.csproj", "{13FD8F12-FFBE-4D01-B4AC-444F2994B04F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StressTestWebSite", "test\testassets\StressTestWebSite\StressTestWebSite.csproj", "{13FD8F12-FFBE-4D01-B4AC-444F2994B04F}"
ProjectSection(ProjectDependencies) = postProject
{46A8612B-418B-4D70-B3A7-A21DD0627473} = {46A8612B-418B-4D70-B3A7-A21DD0627473}
EndProjectSection
@ -82,7 +82,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CommonLib", "src\AspNetCore
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IISLib", "src\AspNetCoreModuleV2\IISLib\IISLib.vcxproj", "{09D9D1D6-2951-4E14-BC35-76A23CF9391A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OutOfProcessWebSite", "test\WebSites\OutOfProcessWebSite\OutOfProcessWebSite.csproj", "{42E60F88-E23F-417A-8143-0CCEC05E1D02}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OutOfProcessWebSite", "test\testassets\OutOfProcessWebSite\OutOfProcessWebSite.csproj", "{42E60F88-E23F-417A-8143-0CCEC05E1D02}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{622D35C9-627B-466E-8D15-752968CC79AF}"
EndProject
@ -114,7 +114,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IIS.BackwardsCompatibility.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IIS.ForwardsCompatibility.FunctionalTests", "test\IIS.ForwardsCompatibility.FunctionalTests\IIS.ForwardsCompatibility.FunctionalTests.csproj", "{D1EA5D99-28FD-4197-81DE-17098846B38B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InProcessWebSite", "test\WebSites\InProcessForwardsCompatWebSite\InProcessWebSite.csproj", "{BBBC85B2-5D7A-4D09-90B1-8DBCC9059493}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InProcessWebSite", "test\testassets\InProcessForwardsCompatWebSite\InProcessWebSite.csproj", "{BBBC85B2-5D7A-4D09-90B1-8DBCC9059493}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

@ -16,7 +16,7 @@
"IIS_SITE_PATH": "$(MSBuildThisFileDirectory)",
"ANCM_PATH": "$(AspNetCoreModuleV1ShimDll)",
"ANCMV2_PATH": "$(AspNetCoreModuleV2ShimDll)",
"ANCM_OUTOFPROCESS_HANDLER": "$(AspNetCoreModuleV2OutOfProcessHandlerDll)",
"ASPNETCORE_MODULE_OUTOFPROCESS_HANDLER": "$(AspNetCoreModuleV2OutOfProcessHandlerDll)",
"LAUNCHER_ARGS": "$(TargetPath)",
"ASPNETCORE_ENVIRONMENT": "Development",
"LAUNCHER_PATH": "$(DotNetPath)",

View File

@ -6,9 +6,12 @@ using System.Linq;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.IIS;
using Microsoft.Extensions.DependencyInjection;
namespace NativeIISSample
{
@ -89,6 +92,13 @@ namespace NativeIISSample
{
await context.Response.WriteAsync("Websocket feature is disabled.");
}
await context.Response.WriteAsync(Environment.NewLine);
var addresses = context.RequestServices.GetService<IServer>().Features.Get<IServerAddressesFeature>();
foreach (var key in addresses.Addresses)
{
await context.Response.WriteAsync(key + Environment.NewLine);
}
});
}

View File

@ -36,10 +36,11 @@ public:
{
// m_location.data() is const ptr copy to local to get mutable pointer
auto location = m_location;
std::array<APPLICATION_PARAMETER, 2> parameters {
std::array<APPLICATION_PARAMETER, 3> parameters {
{
{"InProcessExeLocation", location.data()},
{"TraceContext", pHttpContext->GetTraceContext()}
{"TraceContext", pHttpContext->GetTraceContext()},
{"Site", pHttpContext->GetSite()}
}
};

View File

@ -0,0 +1,133 @@
// 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 <string>
#include "ConfigurationSource.h"
#include "StringHelpers.h"
#include "WebConfigConfigurationSource.h"
#define CS_SITE_SECTION L"system.applicationHost/sites"
#define CS_SITE_NAME L"name"
#define CS_SITE_BINDINGS L"bindings"
#define CS_SITE_BINDING_INFORMATION L"bindingInformation"
#define CS_SITE_BINDING_INFORMATION_ALL_HOSTS L"*"
#define CS_SITE_BINDING_PROTOCOL L"protocol"
#define CS_SITE_BINDING_PROTOCOL_HTTPS L"https"
#define CS_SITE_BINDING_INFORMATION_DELIMITER L':'
class BindingInformation
{
public:
BindingInformation(std::wstring protocol, std::wstring host, std::wstring port)
{
m_protocol = protocol;
m_host = host;
m_port = port;
}
BindingInformation(std::wstring protocol, std::wstring bindingInformation)
{
// Expected format:
// IP:PORT:HOST
// where IP or HOST can be empty
m_protocol = protocol;
const auto portStart = bindingInformation.find(CS_SITE_BINDING_INFORMATION_DELIMITER) + 1;
const auto lastColon = bindingInformation.find_last_of(CS_SITE_BINDING_INFORMATION_DELIMITER);
auto const hostStart = lastColon + 1;
m_host = bindingInformation.substr(hostStart, bindingInformation.length() - hostStart);
if (m_host.length() == 0)
{
m_host = CS_SITE_BINDING_INFORMATION_ALL_HOSTS;
}
m_port = bindingInformation.substr(portStart, lastColon - portStart);
}
std::wstring& QueryProtocol()
{
return m_protocol;
}
std::wstring& QueryPort()
{
return m_port;
}
std::wstring& QueryHost()
{
return m_host;
}
static
std::vector<BindingInformation>
Load(const ConfigurationSource &configurationSource, const IHttpSite& pSite)
{
std::vector<BindingInformation> items;
const std::wstring runningSiteName = pSite.GetSiteName();
auto const siteSection = configurationSource.GetRequiredSection(CS_SITE_SECTION);
auto sites = siteSection->GetCollection();
for (const auto& site: sites)
{
auto siteName = site->GetRequiredString(CS_SITE_NAME);
if (equals_ignore_case(runningSiteName, siteName))
{
auto bindings = site->GetRequiredSection(CS_SITE_BINDINGS)->GetCollection();
for (const auto& binding : bindings)
{
items.emplace_back(
binding->GetRequiredString(CS_SITE_BINDING_PROTOCOL),
binding->GetRequiredString(CS_SITE_BINDING_INFORMATION)
);
}
}
}
return items;
}
static
std::wstring Format(const std::vector<BindingInformation> & bindings, const std::wstring & basePath)
{
std::wstring result;
for (auto binding : bindings)
{
result += binding.QueryProtocol() + L"://" + binding.QueryHost() + L":" + binding.QueryPort() + basePath + L";";
}
return result;
}
static
std::wstring GetHttpsPort(const std::vector<BindingInformation> & bindings)
{
std::wstring selectedPort;
for (auto binding : bindings)
{
if (equals_ignore_case(binding.QueryProtocol(), CS_SITE_BINDING_PROTOCOL_HTTPS))
{
const auto bindingPort = binding.QueryPort();
if (selectedPort.empty())
{
selectedPort = binding.QueryPort();
}
else if (selectedPort != bindingPort)
{
// If there are multiple endpoints configured return empty port
return L"";
}
}
}
return selectedPort;
}
private:
std::wstring m_protocol;
std::wstring m_port;
std::wstring m_host;
};

View File

@ -200,6 +200,7 @@
<ItemGroup>
<ClInclude Include="application.h" />
<ClInclude Include="baseoutputmanager.h" />
<ClInclude Include="BindingInformation.h" />
<ClInclude Include="ConfigurationSection.h" />
<ClInclude Include="ConfigurationSource.h" />
<ClInclude Include="config_utility.h" />

View File

@ -51,6 +51,28 @@ void ConfigurationSection::ThrowRequiredException(const std::wstring& name)
throw ConfigurationLoadException(format(L"Attribute '%s' is required.", name.c_str()));
}
std::vector<std::pair<std::wstring, std::wstring>> ConfigurationSection::GetKeyValuePairs(const std::wstring& name) const
{
std::vector<std::pair<std::wstring, std::wstring>> pairs;
for (auto const element : GetRequiredSection(name)->GetCollection())
{
pairs.emplace_back(element->GetRequiredString(CS_ASPNETCORE_COLLECTION_ITEM_NAME),
element->GetString(CS_ASPNETCORE_COLLECTION_ITEM_VALUE).value_or(L""));
}
return pairs;
}
std::shared_ptr<ConfigurationSection> ConfigurationSection::GetRequiredSection(const std::wstring& name) const
{
auto section = GetSection(name);
if (!section)
{
throw ConfigurationLoadException(format(L"Unable to get required configuration section '%s'. Possible reason is web.config authoring error.", name.c_str()));
}
return section.value();
}
std::optional<std::wstring> find_element(const std::vector<std::pair<std::wstring, std::wstring>>& pairs, const std::wstring& name)
{
const auto iter = std::find_if(

View File

@ -37,12 +37,17 @@ public:
virtual std::optional<DWORD> GetLong(const std::wstring& name) const = 0;
virtual std::optional<DWORD> GetTimespan(const std::wstring& name) const = 0;
virtual std::optional<std::shared_ptr<ConfigurationSection>> GetSection(const std::wstring& name) const = 0;
virtual std::vector<std::shared_ptr<ConfigurationSection>> GetCollection() const = 0;
std::wstring GetRequiredString(const std::wstring& name) const;
bool GetRequiredBool(const std::wstring& name) const;
DWORD GetRequiredLong(const std::wstring& name) const;
DWORD GetRequiredTimespan(const std::wstring& name) const;
virtual std::vector<std::pair<std::wstring, std::wstring>> GetKeyValuePairs(const std::wstring& name) const = 0;
virtual std::vector<std::pair<std::wstring, std::wstring>> GetKeyValuePairs(const std::wstring& name) const;
virtual std::shared_ptr<ConfigurationSection> GetRequiredSection(const std::wstring & name) const;
protected:
static void ThrowRequiredException(const std::wstring& name);

View File

@ -50,43 +50,38 @@ std::optional<DWORD> WebConfigConfigurationSection::GetTimespan(const std::wstri
return std::make_optional(static_cast<DWORD>(result / 10000ull));
}
std::vector<std::pair<std::wstring, std::wstring>> WebConfigConfigurationSection::GetKeyValuePairs(const std::wstring& name) const
std::optional<std::shared_ptr<ConfigurationSection>> WebConfigConfigurationSection::GetSection(const std::wstring& name) const
{
std::vector<std::pair<std::wstring, std::wstring>> pairs;
CComPtr<IAppHostElement> element = nullptr;
if (FAILED_LOG(GetElementChildByName(m_element, name.c_str(), &element)))
{
return std::nullopt;
}
return std::make_optional(std::make_shared<WebConfigConfigurationSection>(element.Detach()));
}
std::vector<std::shared_ptr<ConfigurationSection>> WebConfigConfigurationSection::GetCollection() const
{
std::vector<std::shared_ptr<ConfigurationSection>> elements;
HRESULT findElementResult;
CComPtr<IAppHostElement> element = nullptr;
CComPtr<IAppHostElementCollection> elementCollection = nullptr;
CComPtr<IAppHostElement> collectionEntry = nullptr;
ENUM_INDEX index{};
if (FAILED_LOG(GetElementChildByName(m_element, name.c_str(), &element)))
{
return pairs;
}
THROW_IF_FAILED(element->get_Collection(&elementCollection));
THROW_IF_FAILED(m_element->get_Collection(&elementCollection));
THROW_IF_FAILED(findElementResult = FindFirstElement(elementCollection, &index, &collectionEntry));
while (findElementResult != S_FALSE)
{
CComBSTR strHandlerName;
if (LOG_IF_FAILED(GetElementStringProperty(collectionEntry, CS_ASPNETCORE_COLLECTION_ITEM_NAME, &strHandlerName.m_str)))
{
ThrowRequiredException(CS_ASPNETCORE_COLLECTION_ITEM_NAME);
}
CComBSTR strHandlerValue;
if (LOG_IF_FAILED(GetElementStringProperty(collectionEntry, CS_ASPNETCORE_COLLECTION_ITEM_VALUE, &strHandlerValue.m_str)))
{
ThrowRequiredException(CS_ASPNETCORE_COLLECTION_ITEM_VALUE);
}
pairs.emplace_back(strHandlerName, strHandlerValue);
elements.emplace_back(std::make_shared<WebConfigConfigurationSection>(collectionEntry.Detach()));
collectionEntry.Release();
THROW_IF_FAILED(findElementResult = FindNextElement(elementCollection, &index, &collectionEntry));
}
return pairs;
return elements;
}

View File

@ -19,7 +19,8 @@ public:
std::optional<bool> GetBool(const std::wstring& name) const override;
std::optional<DWORD> GetLong(const std::wstring& name) const override;
std::optional<DWORD> GetTimespan(const std::wstring& name) const override;
std::vector<std::pair<std::wstring, std::wstring>> GetKeyValuePairs(const std::wstring& name) const override;
std::optional<std::shared_ptr<ConfigurationSection>> GetSection(const std::wstring& name) const override;
std::vector<std::shared_ptr<ConfigurationSection>> GetCollection() const override;
private:
CComPtr<IAppHostElement> m_element;

View File

@ -7,13 +7,14 @@
HRESULT InProcessOptions::Create(
IHttpServer& pServer,
IHttpSite* site,
IHttpApplication& pHttpApplication,
std::unique_ptr<InProcessOptions>& options)
{
try
{
const WebConfigConfigurationSource configurationSource(pServer.GetAdminManager(), pHttpApplication);
options = std::make_unique<InProcessOptions>(configurationSource);
options = std::make_unique<InProcessOptions>(configurationSource, site);
}
catch (InvalidOperationException& ex)
{
@ -38,7 +39,7 @@ HRESULT InProcessOptions::Create(
return S_OK;
}
InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSource) :
InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSource, IHttpSite* pSite) :
m_fStdoutLogEnabled(false),
m_fWindowsAuthEnabled(false),
m_fBasicAuthEnabled(false),
@ -68,4 +69,9 @@ InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSourc
const auto anonAuthSection = configurationSource.GetSection(CS_ANONYMOUS_AUTHENTICATION_SECTION);
m_fAnonymousAuthEnabled = anonAuthSection && anonAuthSection->GetBool(CS_ENABLED).value_or(false);
if (pSite != nullptr)
{
m_bindingInformation = BindingInformation::Load(configurationSource, *pSite);
}
}

View File

@ -4,6 +4,7 @@
#pragma once
#include <string>
#include "BindingInformation.h"
#include "ConfigurationSource.h"
#include "WebConfigConfigurationSource.h"
@ -92,11 +93,18 @@ public:
return m_environmentVariables;
}
InProcessOptions(const ConfigurationSource &configurationSource);
const std::vector<BindingInformation>&
QueryBindings() const
{
return m_bindingInformation;
}
InProcessOptions(const ConfigurationSource &configurationSource, IHttpSite* pSite);
static
HRESULT InProcessOptions::Create(
IHttpServer& pServer,
IHttpSite* site,
IHttpApplication& pHttpApplication,
std::unique_ptr<InProcessOptions>& options);
@ -113,6 +121,7 @@ private:
DWORD m_dwStartupTimeLimitInMS;
DWORD m_dwShutdownTimeLimitInMS;
std::vector<std::pair<std::wstring, std::wstring>> m_environmentVariables;
std::vector<BindingInformation> m_bindingInformation;
protected:
InProcessOptions() = default;

View File

@ -100,6 +100,8 @@ CreateApplication(
)
{
TraceContextScope traceScope(FindParameter<IHttpTraceContext*>("TraceContext", pParameters, nParameters));
const auto pSite = FindParameter<IHttpSite*>("Site", pParameters, nParameters);
try
{
HRESULT hr = S_OK;
@ -117,14 +119,14 @@ CreateApplication(
g_fInProcessApplicationCreated = true;
std::unique_ptr<IN_PROCESS_APPLICATION, IAPPLICATION_DELETER> inProcessApplication;
if (!FAILED_LOG(hr = IN_PROCESS_APPLICATION::Start(*pServer, *pHttpApplication, pParameters, nParameters, inProcessApplication)))
if (!FAILED_LOG(hr = IN_PROCESS_APPLICATION::Start(*pServer, pSite, *pHttpApplication, pParameters, nParameters, inProcessApplication)))
{
*ppApplication = inProcessApplication.release();
}
else
{
std::unique_ptr<InProcessOptions> options;
THROW_IF_FAILED(InProcessOptions::Create(*pServer, *pHttpApplication, options));
THROW_IF_FAILED(InProcessOptions::Create(*pServer, pSite, *pHttpApplication, options));
// Set the currently running application to a fake application that returns startup exceptions.
auto pErrorApplication = std::make_unique<StartupExceptionApplication>(*pServer, *pHttpApplication, g_hServerModule, options->QueryDisableStartUpErrorPage(), hr);

View File

@ -344,6 +344,7 @@ void IN_PROCESS_APPLICATION::QueueStop()
HRESULT IN_PROCESS_APPLICATION::Start(
IHttpServer& pServer,
IHttpSite* pSite,
IHttpApplication& pHttpApplication,
APPLICATION_PARAMETER* pParameters,
DWORD nParameters,
@ -352,7 +353,7 @@ HRESULT IN_PROCESS_APPLICATION::Start(
try
{
std::unique_ptr<InProcessOptions> options;
THROW_IF_FAILED(InProcessOptions::Create(pServer, pHttpApplication, options));
THROW_IF_FAILED(InProcessOptions::Create(pServer, pSite, pHttpApplication, options));
application = std::unique_ptr<IN_PROCESS_APPLICATION, IAPPLICATION_DELETER>(
new IN_PROCESS_APPLICATION(pServer, pHttpApplication, std::move(options), pParameters, nParameters));
THROW_IF_FAILED(application->LoadManagedApplication());
@ -444,6 +445,7 @@ IN_PROCESS_APPLICATION::SetEnvironmentVariablesOnWorkerProcess()
m_pConfig->QueryBasicAuthEnabled(),
m_pConfig->QueryAnonymousAuthEnabled(),
QueryApplicationPhysicalPath().c_str(),
nullptr, /* pHttpsPort */
&pHashTable));
table.reset(pHashTable);

View File

@ -104,6 +104,7 @@ public:
static
HRESULT Start(
IHttpServer& pServer,
IHttpSite* pSite,
IHttpApplication& pHttpApplication,
APPLICATION_PARAMETER* pParameters,
DWORD nParameters,

View File

@ -185,6 +185,7 @@ struct IISConfigurationData
BOOL fWindowsAuthEnabled;
BOOL fBasicAuthEnabled;
BOOL fAnonymousAuthEnable;
BSTR pwzBindings;
};
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
@ -208,6 +209,8 @@ http_get_application_properties(
pIISCofigurationData->fBasicAuthEnabled = pConfiguration.QueryBasicAuthEnabled();
pIISCofigurationData->fAnonymousAuthEnable = pConfiguration.QueryAnonymousAuthEnabled();
auto const serverAddresses = BindingInformation::Format(pConfiguration.QueryBindings(), pInProcessApplication->QueryApplicationVirtualPath());
pIISCofigurationData->pwzBindings = SysAllocString(serverAddresses.c_str());
return S_OK;
}

View File

@ -251,11 +251,12 @@ CreateApplication(
)
{
TraceContextScope traceScope(FindParameter<IHttpTraceContext*>("TraceContext", pParameters, nParameters));
auto pSite = FindParameter<IHttpSite*>("Site", pParameters, nParameters);
InitializeGlobalConfiguration(pServer);
REQUESTHANDLER_CONFIG *pConfig = nullptr;
RETURN_IF_FAILED(REQUESTHANDLER_CONFIG::CreateRequestHandlerConfig(pServer, pHttpApplication, &pConfig));
RETURN_IF_FAILED(REQUESTHANDLER_CONFIG::CreateRequestHandlerConfig(pServer, pSite, pHttpApplication, &pConfig));
std::unique_ptr<REQUESTHANDLER_CONFIG> pRequestHandlerConfig(pConfig);
RETURN_IF_FAILED(EnsureOutOfProcessInitializtion(pHttpApplication));

View File

@ -162,7 +162,8 @@ PROCESS_MANAGER::GetProcess(
pConfig->QueryStdoutLogFile(),
pConfig->QueryApplicationPhysicalPath(), // physical path
pConfig->QueryApplicationPath(), // app path
pConfig->QueryApplicationVirtualPath() // App relative virtual path
pConfig->QueryApplicationVirtualPath(), // App relative virtual path,
pConfig->QueryBindings()
));
RETURN_IF_FAILED(pSelectedServerProcess->StartProcess());
}

View File

@ -30,7 +30,8 @@ SERVER_PROCESS::Initialize(
STRU *pstruStdoutLogFile,
STRU *pszAppPhysicalPath,
STRU *pszAppPath,
STRU *pszAppVirtualPath
STRU *pszAppVirtualPath,
STRU *pszHttpsPort
)
{
HRESULT hr = S_OK;
@ -52,6 +53,7 @@ SERVER_PROCESS::Initialize(
FAILED_LOG(hr = m_struAppFullPath.Copy(*pszAppPath))||
FAILED_LOG(hr = m_struAppVirtualPath.Copy(*pszAppVirtualPath))||
FAILED_LOG(hr = m_Arguments.Copy(*pszArguments)) ||
FAILED_LOG(hr = m_struHttpsPort.Copy(*pszHttpsPort)) ||
FAILED_LOG(hr = SetupJobObject()))
{
goto Finished;
@ -786,6 +788,7 @@ SERVER_PROCESS::StartProcess(
m_fBasicAuthEnabled,
m_fAnonymousAuthEnabled,
m_struAppFullPath.QueryStr(),
m_struHttpsPort.QueryStr(),
&pHashTable)))
{
pStrStage = L"InitEnvironmentVariablesTable";

View File

@ -39,7 +39,8 @@ public:
_In_ STRU *pstruStdoutLogFile,
_In_ STRU *pszAppPhysicalPath,
_In_ STRU *pszAppPath,
_In_ STRU *pszAppVirtualPath
_In_ STRU *pszAppVirtualPath,
_In_ STRU *pszHttpsPort
);
HRESULT
@ -98,7 +99,7 @@ public:
}
}
virtual
virtual
~SERVER_PROCESS();
static
@ -129,7 +130,7 @@ public:
};
VOID
SendSignal(
SendSignal(
VOID
);
@ -142,7 +143,7 @@ private:
VOID
);
BOOL
BOOL
IsDebuggerIsAttached(
VOID
);
@ -164,13 +165,13 @@ private:
_Out_ BOOL * pfReady
);
HRESULT
HRESULT
RegisterProcessWait(
_In_ PHANDLE phWaitHandle,
_In_ HANDLE hProcessToWaitOn
);
HRESULT
HRESULT
GetChildProcessHandles(
VOID
);
@ -252,6 +253,7 @@ private:
STRU m_struAppVirtualPath; // e.g., '/' for site
STRU m_struAppFullPath; // e.g., /LM/W3SVC/4/ROOT/Inproc
STRU m_struPhysicalPath; // e.g., c:/test/mysite
STRU m_struHttpsPort; // e.g., /LM/W3SVC/4/ROOT/Inproc
STRU m_struPort;
STRU m_struCommandLine;
@ -281,7 +283,7 @@ private:
HANDLE m_hProcessWaitHandle;
HANDLE m_hShutdownHandle;
//
// m_hChildProcessHandle is the handle to process created by
// m_hChildProcessHandle is the handle to process created by
// m_hProcessHandle process if it does.
//
HANDLE m_hChildProcessHandles[MAX_ACTIVE_CHILD_PROCESSES];

View File

@ -9,6 +9,7 @@
#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_PHYSICAL_PATH_ENV_STR L"ASPNETCORE_IIS_PHYSICAL_PATH="
#define ASPNETCORE_HTTPS_PORT_ENV_STR L"ASPNETCORE_HTTPS_PORT="
#define ASPNETCORE_IIS_AUTH_WINDOWS L"windows;"
#define ASPNETCORE_IIS_AUTH_BASIC L"basic;"
#define ASPNETCORE_IIS_AUTH_ANONYMOUS L"anonymous;"

View File

@ -192,6 +192,7 @@ public:
_In_ BOOL fBasicAuthEnabled,
_In_ BOOL fAnonymousAuthEnabled,
_In_ PCWSTR pApplicationPhysicalPath,
_In_ PCWSTR pHttpsPort,
_Out_ ENVIRONMENT_VAR_HASH** ppEnvironmentVarTable
)
{
@ -203,6 +204,7 @@ public:
ENVIRONMENT_VAR_ENTRY* pHostingEntry = NULL;
ENVIRONMENT_VAR_ENTRY* pIISAuthEntry = NULL;
ENVIRONMENT_VAR_ENTRY* pIISPathEntry = NULL;
ENVIRONMENT_VAR_ENTRY* pIISHttpsPort = NULL;
ENVIRONMENT_VAR_HASH* pEnvironmentVarTable = NULL;
pEnvironmentVarTable = new ENVIRONMENT_VAR_HASH();
@ -240,6 +242,26 @@ public:
goto Finished;
}
if (pHttpsPort != nullptr)
{
pEnvironmentVarTable->FindKey((PWSTR)ASPNETCORE_HTTPS_PORT_ENV_STR, &pIISHttpsPort);
if (pIISHttpsPort != NULL)
{
// user defined ASPNETCORE_HTTPS_PORT in configuration, don't override it
pIISHttpsPort->Dereference();
}
else
{
pIISHttpsPort = new ENVIRONMENT_VAR_ENTRY();
if (FAILED(hr = pIISHttpsPort->Initialize(ASPNETCORE_HTTPS_PORT_ENV_STR, pHttpsPort)) ||
FAILED(hr = pEnvironmentVarTable->InsertRecord(pIISHttpsPort)))
{
goto Finished;
}
}
}
pEnvironmentVarTable->FindKey((PWSTR)ASPNETCORE_IIS_AUTH_ENV_STR, &pIISAuthEntry);
if (pIISAuthEntry != NULL)
{

View File

@ -8,7 +8,6 @@
#include "exceptions.h"
#include "config_utility.h"
REQUESTHANDLER_CONFIG::~REQUESTHANDLER_CONFIG()
{
if (m_ppStrArguments != NULL)
@ -28,6 +27,7 @@ REQUESTHANDLER_CONFIG::~REQUESTHANDLER_CONFIG()
HRESULT
REQUESTHANDLER_CONFIG::CreateRequestHandlerConfig(
_In_ IHttpServer *pHttpServer,
_In_ IHttpSite *pSite,
_In_ IHttpApplication *pHttpApplication,
_Out_ REQUESTHANDLER_CONFIG **ppAspNetCoreConfig
)
@ -49,7 +49,7 @@ REQUESTHANDLER_CONFIG::CreateRequestHandlerConfig(
pRequestHandlerConfig = new REQUESTHANDLER_CONFIG;
hr = pRequestHandlerConfig->Populate(pHttpServer, pHttpApplication);
hr = pRequestHandlerConfig->Populate(pHttpServer, pSite, pHttpApplication);
if (FAILED(hr))
{
goto Finished;
@ -85,6 +85,7 @@ Finished:
HRESULT
REQUESTHANDLER_CONFIG::Populate(
IHttpServer *pHttpServer,
IHttpSite *pSite,
IHttpApplication *pHttpApplication
)
{
@ -122,6 +123,19 @@ REQUESTHANDLER_CONFIG::Populate(
}
pAdminManager = pHttpServer->GetAdminManager();
if (pSite != nullptr)
{
try
{
WebConfigConfigurationSource source(pAdminManager, *pHttpApplication);
m_struHttpsPort.Copy(BindingInformation::GetHttpsPort(BindingInformation::Load(source, *pSite)).c_str());
}
catch (...)
{
FINISHED_IF_FAILED(OBSERVE_CAUGHT_EXCEPTION());
}
}
hr = m_struConfigPath.Copy(pHttpApplication->GetAppConfigPath());
if (FAILED(hr))
{

View File

@ -43,6 +43,7 @@
#include "stdafx.h"
#include "environmentvariablehash.h"
#include "BindingInformation.h"
enum APP_HOSTING_MODEL
{
@ -62,6 +63,7 @@ public:
HRESULT
CreateRequestHandlerConfig(
_In_ IHttpServer *pHttpServer,
_In_ IHttpSite *pSite,
_In_ IHttpApplication *pHttpApplication,
_Out_ REQUESTHANDLER_CONFIG **ppAspNetCoreConfig
);
@ -114,6 +116,12 @@ public:
return m_dwRequestTimeoutInMS;
}
STRU*
QueryBindings()
{
return &m_struHttpsPort;
}
STRU*
QueryArguments(
VOID
@ -226,6 +234,7 @@ protected:
HRESULT
Populate(
IHttpServer *pHttpServer,
IHttpSite *pSite,
IHttpApplication *pHttpApplication
);
@ -252,5 +261,5 @@ protected:
STRU m_struHostFxrLocation;
PWSTR* m_ppStrArguments;
DWORD m_dwArgc;
STRU m_struHttpsPort;
};

View File

@ -17,5 +17,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
public bool fWindowsAuthEnabled;
public bool fBasicAuthEnabled;
public bool fAnonymousAuthEnable;
[MarshalAs(UnmanagedType.BStr)]
public string pwzBindings;
}
}

View File

@ -1,5 +1,3 @@
// 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.Buffers;
@ -10,6 +8,7 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -32,6 +31,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
private readonly ILogger<IISHttpServer> _logger;
private readonly IISServerOptions _options;
private readonly IISNativeApplication _nativeApplication;
private readonly ServerAddressesFeature _serverAddressesFeature;
private volatile int _stopping;
private bool Stopping => _stopping == 1;
@ -71,11 +71,14 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
_applicationLifetime = applicationLifetime;
_logger = logger;
_options = options.Value;
_serverAddressesFeature = new ServerAddressesFeature();
if (_options.ForwardWindowsAuthentication)
{
authentication.AddScheme(new AuthenticationScheme(IISServerDefaults.AuthenticationScheme, _options.AuthenticationDisplayName, typeof(IISServerAuthenticationHandler)));
}
Features.Set<IServerAddressesFeature>(_serverAddressesFeature);
}
public Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
@ -84,6 +87,9 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
_iisContextFactory = new IISContextFactory<TContext>(_memoryPool, application, _options, this, _logger);
_nativeApplication.RegisterCallbacks(_requestHandler, _shutdownHandler, _onDisconnect, _onAsyncCompletion, (IntPtr)_httpServerHandle, (IntPtr)_httpServerHandle);
_serverAddressesFeature.Addresses = _options.ServerAddresses;
return Task.CompletedTask;
}

View File

@ -2,16 +2,18 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Server.IIS.Core
{
internal class IISServerSetupFilter : IStartupFilter
{
private string _virtualPath;
private readonly string _virtualPath;
public IISServerSetupFilter(string virtualPath)
{

View File

@ -0,0 +1,15 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Hosting.Server.Features;
namespace Microsoft.AspNetCore.Server.IIS.Core
{
internal class ServerAddressesFeature : IServerAddressesFeature
{
public ICollection<string> Addresses { get; set; } = Array.Empty<string>();
public bool PreferHostingUrls { get; set; }
}
}

View File

@ -22,5 +22,7 @@ namespace Microsoft.AspNetCore.Builder
/// IIS has a non-anonymous authentication enabled, or for back compat with ANCMs that did not provide this information.
/// </summary>
internal bool ForwardWindowsAuthentication { get; set; } = true;
internal string[] ServerAddresses { get; set; }
}
}

View File

@ -43,7 +43,10 @@ namespace Microsoft.AspNetCore.Hosting
services.AddSingleton<IStartupFilter>(new IISServerSetupFilter(iisConfigData.pwzVirtualApplicationPath));
services.AddAuthenticationCore();
services.Configure<IISServerOptions>(
options => { options.ForwardWindowsAuthentication = iisConfigData.fWindowsAuthEnabled || iisConfigData.fBasicAuthEnabled; }
options => {
options.ServerAddresses = iisConfigData.pwzBindings.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
options.ForwardWindowsAuthentication = iisConfigData.fWindowsAuthEnabled || iisConfigData.fBasicAuthEnabled;
}
);
});
}

View File

@ -0,0 +1,177 @@
// 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.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Server.IntegrationTesting.Common;
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(PublishedSitesCollection.Name)]
public class HttpsTests : IISFunctionalTestBase
{
private readonly PublishedSitesFixture _fixture;
public HttpsTests(PublishedSitesFixture fixture)
{
_fixture = fixture;
}
public static TestMatrix TestVariants
=> TestMatrix.ForServers(DeployerSelector.ServerType)
.WithTfms(Tfm.NetCoreApp30)
.WithAllApplicationTypes()
.WithAllAncmVersions()
.WithAllHostingModels();
[ConditionalTheory]
[MemberData(nameof(TestVariants))]
public async Task HttpsHelloWorld(TestVariant variant)
{
var port = TestPortHelper.GetNextSSLPort();
var deploymentParameters = _fixture.GetBaseDeploymentParameters(variant);
deploymentParameters.ApplicationBaseUriHint = $"https://localhost:{port}/";
deploymentParameters.AddHttpsToServerConfig();
var deploymentResult = await DeployAsync(deploymentParameters);
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (a, b, c, d) => true
};
var client = deploymentResult.CreateClient(handler);
var response = await client.GetAsync("HttpsHelloWorld");
var responseText = await response.Content.ReadAsStringAsync();
if (variant.HostingModel == HostingModel.OutOfProcess)
{
Assert.Equal("Scheme:https; Original:http", responseText);
}
else
{
Assert.Equal("Scheme:https; Original:", responseText);
}
if (variant.AncmVersion == AncmVersion.AspNetCoreModuleV2 &&
DeployerSelector.HasNewHandler &&
DeployerSelector.HasNewShim)
{
// We expect ServerAddress to be set for InProcess and HTTPS_PORT for OutOfProcess
if (variant.HostingModel == HostingModel.InProcess)
{
Assert.Equal(deploymentParameters.ApplicationBaseUriHint, await client.GetStringAsync("/ServerAddresses"));
}
else
{
Assert.Equal(port.ToString(), await client.GetStringAsync("/HTTPS_PORT"));
}
}
}
[ConditionalFact]
[RequiresNewHandler]
[RequiresNewShim]
public async Task ServerAddressesIncludesBaseAddress()
{
var appName = "\u041C\u043E\u0451\u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435";
var port = TestPortHelper.GetNextSSLPort();
var deploymentParameters = _fixture.GetBaseDeploymentParameters(HostingModel.InProcess, publish: true);
deploymentParameters.ApplicationBaseUriHint = $"https://localhost:{port}/";
deploymentParameters.AddHttpsToServerConfig();
deploymentParameters.AddServerConfigAction(
(element, root) => {
element.Descendants("site").Single().Element("application").SetAttributeValue("path", "/" + appName);
Helpers.CreateEmptyApplication(element, root);
});
var deploymentResult = await DeployAsync(deploymentParameters);
Assert.Equal(deploymentParameters.ApplicationBaseUriHint + appName, await deploymentResult.HttpClient.GetStringAsync($"/{appName}/ServerAddresses"));
}
[ConditionalFact]
[RequiresNewHandler]
[RequiresNewShim]
public async Task HttpsPortCanBeOverriden()
{
var deploymentParameters = _fixture.GetBaseDeploymentParameters(HostingModel.OutOfProcess, publish: true);
deploymentParameters.AddServerConfigAction(
element => {
element.Descendants("bindings")
.Single()
.GetOrAdd("binding", "protocol", "https")
.SetAttributeValue("bindingInformation", $":{TestPortHelper.GetNextSSLPort()}:localhost");
});
deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_HTTPS_PORT"] = "123";
var deploymentResult = await DeployAsync(deploymentParameters);
Assert.Equal("123", await deploymentResult.HttpClient.GetStringAsync("/HTTPS_PORT"));
}
[ConditionalFact]
[RequiresNewHandler]
[RequiresNewShim]
public async Task MultipleHttpsPortsProduceNoEnvVar()
{
var sslPort = GetNextSSLPort();
var anotherSslPort = GetNextSSLPort(sslPort);
var deploymentParameters = _fixture.GetBaseDeploymentParameters(HostingModel.OutOfProcess, publish: true);
deploymentParameters.AddServerConfigAction(
element => {
element.Descendants("bindings")
.Single()
.Add(
new XElement("binding",
new XAttribute("protocol", "https"),
new XAttribute("bindingInformation", $":{sslPort}:localhost")),
new XElement("binding",
new XAttribute("protocol", "https"),
new XAttribute("bindingInformation", $":{anotherSslPort}:localhost")));
});
var deploymentResult = await DeployAsync(deploymentParameters);
Assert.Equal("NOVALUE", await deploymentResult.HttpClient.GetStringAsync("/HTTPS_PORT"));
}
public static int GetNextSSLPort(int avoid = 0)
{
var next = 44300;
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
while (true)
{
try
{
var port = next++;
if (port == avoid)
{
continue;
}
socket.Bind(new IPEndPoint(IPAddress.Loopback, port));
return port;
}
catch (SocketException)
{
// Retry unless exhausted
if (next > 44399)
{
throw;
}
}
}
}
}
}
}

View File

@ -1,6 +1,7 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
@ -25,5 +26,13 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
Assert.Equal("Success", await _fixture.Client.GetStringAsync(path + "/path" + "?query"));
}
[ConditionalFact]
[RequiresNewHandler]
[RequiresNewShim]
public async Task ExposesIServerAddressesFeature()
{
Assert.Equal(_fixture.Client.BaseAddress.ToString(), await _fixture.Client.GetStringAsync("/ServerAddresses"));
}
}
}

View File

@ -115,18 +115,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
siteElement.Add(newApplication);
// IIS Express requires root application to exist
var rootApplicationDirectory = new DirectoryInfo(contentRoot + "rootApp");
rootApplicationDirectory.Create();
_rootApplication = new PublishedApplication(rootApplicationDirectory.FullName, Logger);
File.WriteAllText(GetWebConfigLocation(rootApplicationDirectory.FullName), "<configuration></configuration>");
var rootApplication = new XElement(application);
rootApplication.SetAttributeValue("path", "/");
rootApplication.RequiredElement("virtualDirectory")
.SetAttributeValue("physicalPath", rootApplicationDirectory.FullName);
siteElement.Add(rootApplication);
_rootApplication = new PublishedApplication(Helpers.CreateEmptyApplication(config, contentRoot), Logger);
}
private static string GetWebConfigLocation(string siteRoot)

View File

@ -237,5 +237,30 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
throw ex;
}
}
public static string CreateEmptyApplication(XElement config, string contentRoot)
{
var siteElement = config
.RequiredElement("system.applicationHost")
.RequiredElement("sites")
.RequiredElement("site");
var application = siteElement
.RequiredElement("application");
var rootApplicationDirectory = new DirectoryInfo(contentRoot + "rootApp");
rootApplicationDirectory.Create();
File.WriteAllText(Path.Combine(rootApplicationDirectory.FullName, "web.config"), "<configuration></configuration>");
var rootApplication = new XElement(application);
rootApplication.SetAttributeValue("path", "/");
rootApplication.RequiredElement("virtualDirectory")
.SetAttributeValue("physicalPath", rootApplicationDirectory.FullName);
siteElement.Add(rootApplication);
return rootApplicationDirectory.FullName;
}
}
}

View File

@ -0,0 +1,29 @@
// 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.
#include "stdafx.h"
namespace BindingInformationTest
{
class BindingInformationTest : public testing::Test
{
protected:
void
ParseBindingInformation(std::wstring protocol, std::wstring info, std::wstring expectedHost, std::wstring expectedPort)
{
BindingInformation information(protocol, info);
EXPECT_STREQ(information.QueryHost().c_str(), expectedHost.c_str());
EXPECT_STREQ(information.QueryPort().c_str(), expectedPort.c_str());
EXPECT_STREQ(information.QueryProtocol().c_str(), protocol.c_str());
}
};
TEST_F(BindingInformationTest, ParsesInformationCorrectly)
{
ParseBindingInformation(L"https", L":80:", L"*", L"80");
ParseBindingInformation(L"https", L":80:host", L"host", L"80");
ParseBindingInformation(L"http", L":80:host", L"host", L"80");
ParseBindingInformation(L"http", L"RANDOM_IP:5:", L"*", L"5");
}
}

View File

@ -1,5 +1,4 @@

<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
@ -58,6 +57,7 @@
<ClCompile Include="inprocess_application_tests.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="PipeOutputManagerTests.cpp" />
<ClCompile Include="BindingInformationTest.cpp" />
<ClCompile Include="utility_tests.cpp" />
</ItemGroup>
<ItemGroup>
@ -195,4 +195,4 @@
</Lib>
</ItemDefinitionGroup>
<Import Project=".\NativeTests.targets" />
</Project>
</Project>

View File

@ -1,60 +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.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Server.IntegrationTesting.Common;
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(PublishedSitesCollection.Name)]
public class HttpsTests : IISFunctionalTestBase
{
private readonly PublishedSitesFixture _fixture;
public HttpsTests(PublishedSitesFixture fixture)
{
_fixture = fixture;
}
public static TestMatrix TestVariants
=> TestMatrix.ForServers(DeployerSelector.ServerType)
.WithTfms(Tfm.NetCoreApp30)
.WithAllApplicationTypes()
.WithAllAncmVersions()
.WithAllHostingModels();
[ConditionalTheory]
[MemberData(nameof(TestVariants))]
public async Task HttpsHelloWorld(TestVariant variant)
{
var port = TestPortHelper.GetNextSSLPort();
var deploymentParameters = _fixture.GetBaseDeploymentParameters(variant);
deploymentParameters.ApplicationBaseUriHint = $"https://localhost:{port}/";
deploymentParameters.AddHttpsToServerConfig();
var deploymentResult = await DeployAsync(deploymentParameters);
var handler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = (a, b, c, d) => true
};
var client = deploymentResult.CreateClient(handler);
var response = await client.GetAsync("HttpsHelloWorld");
var responseText = await response.Content.ReadAsStringAsync();
if (variant.HostingModel == HostingModel.OutOfProcess)
{
Assert.Equal("Scheme:https; Original:http", responseText);
}
else
{
Assert.Equal("Scheme:https; Original:", responseText);
}
}
}
}

View File

@ -4,16 +4,15 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.IISIntegration.FunctionalTests;
using Microsoft.AspNetCore.Server.IISIntegration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
@ -21,13 +20,9 @@ namespace TestSite
{
public partial class Startup
{
private IServerAddressesFeature _serverAddresses;
public void Configure(IApplicationBuilder app)
{
TestStartup.Register(app, this);
_serverAddresses = app.ServerFeatures.Get<IServerAddressesFeature>();
}
public Task Path(HttpContext ctx) => ctx.Response.WriteAsync(ctx.Request.Path.Value);
@ -36,7 +31,6 @@ namespace TestSite
public Task BodyLimit(HttpContext ctx) => ctx.Response.WriteAsync(ctx.Features.Get<IHttpMaxRequestBodySizeFeature>()?.MaxRequestBodySize?.ToString() ?? "null");
public Task HelloWorld(HttpContext ctx) => ctx.Response.WriteAsync("Hello World");
public Task HttpsHelloWorld(HttpContext ctx) =>
@ -97,9 +91,11 @@ namespace TestSite
await context.Response.WriteAsync(Process.GetCurrentProcess().Id.ToString());
}
private async Task ServerAddresses(HttpContext context)
public async Task HTTPS_PORT(HttpContext context)
{
await context.Response.WriteAsync(string.Join(",", _serverAddresses.Addresses));
var httpsPort = context.RequestServices.GetService<IConfiguration>().GetValue<int?>("HTTPS_PORT");
await context.Response.WriteAsync(httpsPort.HasValue ? httpsPort.Value.ToString() : "NOVALUE");
}
}
}

View File

@ -11,6 +11,8 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
@ -33,6 +35,12 @@ namespace TestSite
private async Task ASPNETCORE_IIS_PHYSICAL_PATH(HttpContext ctx) => await ctx.Response.WriteAsync(Environment.GetEnvironmentVariable("ASPNETCORE_IIS_PHYSICAL_PATH"));
private async Task ServerAddresses(HttpContext ctx)
{
var serverAddresses = ctx.RequestServices.GetService<IServer>().Features.Get<IServerAddressesFeature>();
await ctx.Response.WriteAsync(string.Join(",", serverAddresses.Addresses));
}
private async Task ConsoleWrite(HttpContext ctx)
{
Console.WriteLine("TEST MESSAGE");