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 EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeIISSample", "samples\NativeIISSample\NativeIISSample.csproj", "{9BC4AFCB-325D-4C81-8228-8CF301CE2F97}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeIISSample", "samples\NativeIISSample\NativeIISSample.csproj", "{9BC4AFCB-325D-4C81-8228-8CF301CE2F97}"
EndProject 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 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}" 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 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} {D57EA297-6DC2-4BC0-8C91-334863327863} = {D57EA297-6DC2-4BC0-8C91-334863327863}
EndProjectSection EndProjectSection
EndProject 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 ProjectSection(ProjectDependencies) = postProject
{46A8612B-418B-4D70-B3A7-A21DD0627473} = {46A8612B-418B-4D70-B3A7-A21DD0627473} {46A8612B-418B-4D70-B3A7-A21DD0627473} = {46A8612B-418B-4D70-B3A7-A21DD0627473}
EndProjectSection EndProjectSection
@ -82,7 +82,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CommonLib", "src\AspNetCore
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IISLib", "src\AspNetCoreModuleV2\IISLib\IISLib.vcxproj", "{09D9D1D6-2951-4E14-BC35-76A23CF9391A}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "IISLib", "src\AspNetCoreModuleV2\IISLib\IISLib.vcxproj", "{09D9D1D6-2951-4E14-BC35-76A23CF9391A}"
EndProject 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 EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{622D35C9-627B-466E-8D15-752968CC79AF}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{622D35C9-627B-466E-8D15-752968CC79AF}"
EndProject EndProject
@ -114,7 +114,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IIS.BackwardsCompatibility.
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IIS.ForwardsCompatibility.FunctionalTests", "test\IIS.ForwardsCompatibility.FunctionalTests\IIS.ForwardsCompatibility.FunctionalTests.csproj", "{D1EA5D99-28FD-4197-81DE-17098846B38B}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IIS.ForwardsCompatibility.FunctionalTests", "test\IIS.ForwardsCompatibility.FunctionalTests\IIS.ForwardsCompatibility.FunctionalTests.csproj", "{D1EA5D99-28FD-4197-81DE-17098846B38B}"
EndProject 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 EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

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

View File

@ -6,9 +6,12 @@ using System.Linq;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.IIS; using Microsoft.AspNetCore.Server.IIS;
using Microsoft.Extensions.DependencyInjection;
namespace NativeIISSample namespace NativeIISSample
{ {
@ -89,6 +92,13 @@ namespace NativeIISSample
{ {
await context.Response.WriteAsync("Websocket feature is disabled."); 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 // m_location.data() is const ptr copy to local to get mutable pointer
auto location = m_location; auto location = m_location;
std::array<APPLICATION_PARAMETER, 2> parameters { std::array<APPLICATION_PARAMETER, 3> parameters {
{ {
{"InProcessExeLocation", location.data()}, {"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> <ItemGroup>
<ClInclude Include="application.h" /> <ClInclude Include="application.h" />
<ClInclude Include="baseoutputmanager.h" /> <ClInclude Include="baseoutputmanager.h" />
<ClInclude Include="BindingInformation.h" />
<ClInclude Include="ConfigurationSection.h" /> <ClInclude Include="ConfigurationSection.h" />
<ClInclude Include="ConfigurationSource.h" /> <ClInclude Include="ConfigurationSource.h" />
<ClInclude Include="config_utility.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())); 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) 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( 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> GetLong(const std::wstring& name) const = 0;
virtual std::optional<DWORD> GetTimespan(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; std::wstring GetRequiredString(const std::wstring& name) const;
bool GetRequiredBool(const std::wstring& name) const; bool GetRequiredBool(const std::wstring& name) const;
DWORD GetRequiredLong(const std::wstring& name) const; DWORD GetRequiredLong(const std::wstring& name) const;
DWORD GetRequiredTimespan(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: protected:
static void ThrowRequiredException(const std::wstring& name); 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)); 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; HRESULT findElementResult;
CComPtr<IAppHostElement> element = nullptr;
CComPtr<IAppHostElementCollection> elementCollection = nullptr; CComPtr<IAppHostElementCollection> elementCollection = nullptr;
CComPtr<IAppHostElement> collectionEntry = nullptr; CComPtr<IAppHostElement> collectionEntry = nullptr;
ENUM_INDEX index{}; ENUM_INDEX index{};
if (FAILED_LOG(GetElementChildByName(m_element, name.c_str(), &element))) THROW_IF_FAILED(m_element->get_Collection(&elementCollection));
{
return pairs;
}
THROW_IF_FAILED(element->get_Collection(&elementCollection));
THROW_IF_FAILED(findElementResult = FindFirstElement(elementCollection, &index, &collectionEntry)); THROW_IF_FAILED(findElementResult = FindFirstElement(elementCollection, &index, &collectionEntry));
while (findElementResult != S_FALSE) 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; elements.emplace_back(std::make_shared<WebConfigConfigurationSection>(collectionEntry.Detach()));
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);
collectionEntry.Release(); collectionEntry.Release();
THROW_IF_FAILED(findElementResult = FindNextElement(elementCollection, &index, &collectionEntry)); 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<bool> GetBool(const std::wstring& name) const override;
std::optional<DWORD> GetLong(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::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: private:
CComPtr<IAppHostElement> m_element; CComPtr<IAppHostElement> m_element;

View File

@ -7,13 +7,14 @@
HRESULT InProcessOptions::Create( HRESULT InProcessOptions::Create(
IHttpServer& pServer, IHttpServer& pServer,
IHttpSite* site,
IHttpApplication& pHttpApplication, IHttpApplication& pHttpApplication,
std::unique_ptr<InProcessOptions>& options) std::unique_ptr<InProcessOptions>& options)
{ {
try try
{ {
const WebConfigConfigurationSource configurationSource(pServer.GetAdminManager(), pHttpApplication); const WebConfigConfigurationSource configurationSource(pServer.GetAdminManager(), pHttpApplication);
options = std::make_unique<InProcessOptions>(configurationSource); options = std::make_unique<InProcessOptions>(configurationSource, site);
} }
catch (InvalidOperationException& ex) catch (InvalidOperationException& ex)
{ {
@ -38,7 +39,7 @@ HRESULT InProcessOptions::Create(
return S_OK; return S_OK;
} }
InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSource) : InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSource, IHttpSite* pSite) :
m_fStdoutLogEnabled(false), m_fStdoutLogEnabled(false),
m_fWindowsAuthEnabled(false), m_fWindowsAuthEnabled(false),
m_fBasicAuthEnabled(false), m_fBasicAuthEnabled(false),
@ -68,4 +69,9 @@ InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSourc
const auto anonAuthSection = configurationSource.GetSection(CS_ANONYMOUS_AUTHENTICATION_SECTION); const auto anonAuthSection = configurationSource.GetSection(CS_ANONYMOUS_AUTHENTICATION_SECTION);
m_fAnonymousAuthEnabled = anonAuthSection && anonAuthSection->GetBool(CS_ENABLED).value_or(false); 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 #pragma once
#include <string> #include <string>
#include "BindingInformation.h"
#include "ConfigurationSource.h" #include "ConfigurationSource.h"
#include "WebConfigConfigurationSource.h" #include "WebConfigConfigurationSource.h"
@ -92,11 +93,18 @@ public:
return m_environmentVariables; return m_environmentVariables;
} }
InProcessOptions(const ConfigurationSource &configurationSource); const std::vector<BindingInformation>&
QueryBindings() const
{
return m_bindingInformation;
}
InProcessOptions(const ConfigurationSource &configurationSource, IHttpSite* pSite);
static static
HRESULT InProcessOptions::Create( HRESULT InProcessOptions::Create(
IHttpServer& pServer, IHttpServer& pServer,
IHttpSite* site,
IHttpApplication& pHttpApplication, IHttpApplication& pHttpApplication,
std::unique_ptr<InProcessOptions>& options); std::unique_ptr<InProcessOptions>& options);
@ -113,6 +121,7 @@ private:
DWORD m_dwStartupTimeLimitInMS; DWORD m_dwStartupTimeLimitInMS;
DWORD m_dwShutdownTimeLimitInMS; DWORD m_dwShutdownTimeLimitInMS;
std::vector<std::pair<std::wstring, std::wstring>> m_environmentVariables; std::vector<std::pair<std::wstring, std::wstring>> m_environmentVariables;
std::vector<BindingInformation> m_bindingInformation;
protected: protected:
InProcessOptions() = default; InProcessOptions() = default;

View File

@ -100,6 +100,8 @@ CreateApplication(
) )
{ {
TraceContextScope traceScope(FindParameter<IHttpTraceContext*>("TraceContext", pParameters, nParameters)); TraceContextScope traceScope(FindParameter<IHttpTraceContext*>("TraceContext", pParameters, nParameters));
const auto pSite = FindParameter<IHttpSite*>("Site", pParameters, nParameters);
try try
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
@ -117,14 +119,14 @@ CreateApplication(
g_fInProcessApplicationCreated = true; g_fInProcessApplicationCreated = true;
std::unique_ptr<IN_PROCESS_APPLICATION, IAPPLICATION_DELETER> inProcessApplication; 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(); *ppApplication = inProcessApplication.release();
} }
else else
{ {
std::unique_ptr<InProcessOptions> options; 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. // 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); 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( HRESULT IN_PROCESS_APPLICATION::Start(
IHttpServer& pServer, IHttpServer& pServer,
IHttpSite* pSite,
IHttpApplication& pHttpApplication, IHttpApplication& pHttpApplication,
APPLICATION_PARAMETER* pParameters, APPLICATION_PARAMETER* pParameters,
DWORD nParameters, DWORD nParameters,
@ -352,7 +353,7 @@ HRESULT IN_PROCESS_APPLICATION::Start(
try try
{ {
std::unique_ptr<InProcessOptions> options; 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>( application = std::unique_ptr<IN_PROCESS_APPLICATION, IAPPLICATION_DELETER>(
new IN_PROCESS_APPLICATION(pServer, pHttpApplication, std::move(options), pParameters, nParameters)); new IN_PROCESS_APPLICATION(pServer, pHttpApplication, std::move(options), pParameters, nParameters));
THROW_IF_FAILED(application->LoadManagedApplication()); THROW_IF_FAILED(application->LoadManagedApplication());
@ -444,6 +445,7 @@ IN_PROCESS_APPLICATION::SetEnvironmentVariablesOnWorkerProcess()
m_pConfig->QueryBasicAuthEnabled(), m_pConfig->QueryBasicAuthEnabled(),
m_pConfig->QueryAnonymousAuthEnabled(), m_pConfig->QueryAnonymousAuthEnabled(),
QueryApplicationPhysicalPath().c_str(), QueryApplicationPhysicalPath().c_str(),
nullptr, /* pHttpsPort */
&pHashTable)); &pHashTable));
table.reset(pHashTable); table.reset(pHashTable);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -39,7 +39,8 @@ public:
_In_ STRU *pstruStdoutLogFile, _In_ STRU *pstruStdoutLogFile,
_In_ STRU *pszAppPhysicalPath, _In_ STRU *pszAppPhysicalPath,
_In_ STRU *pszAppPath, _In_ STRU *pszAppPath,
_In_ STRU *pszAppVirtualPath _In_ STRU *pszAppVirtualPath,
_In_ STRU *pszHttpsPort
); );
HRESULT HRESULT
@ -98,7 +99,7 @@ public:
} }
} }
virtual virtual
~SERVER_PROCESS(); ~SERVER_PROCESS();
static static
@ -129,7 +130,7 @@ public:
}; };
VOID VOID
SendSignal( SendSignal(
VOID VOID
); );
@ -142,7 +143,7 @@ private:
VOID VOID
); );
BOOL BOOL
IsDebuggerIsAttached( IsDebuggerIsAttached(
VOID VOID
); );
@ -164,13 +165,13 @@ private:
_Out_ BOOL * pfReady _Out_ BOOL * pfReady
); );
HRESULT HRESULT
RegisterProcessWait( RegisterProcessWait(
_In_ PHANDLE phWaitHandle, _In_ PHANDLE phWaitHandle,
_In_ HANDLE hProcessToWaitOn _In_ HANDLE hProcessToWaitOn
); );
HRESULT HRESULT
GetChildProcessHandles( GetChildProcessHandles(
VOID VOID
); );
@ -252,6 +253,7 @@ private:
STRU m_struAppVirtualPath; // e.g., '/' for site STRU m_struAppVirtualPath; // e.g., '/' for site
STRU m_struAppFullPath; // e.g., /LM/W3SVC/4/ROOT/Inproc STRU m_struAppFullPath; // e.g., /LM/W3SVC/4/ROOT/Inproc
STRU m_struPhysicalPath; // e.g., c:/test/mysite STRU m_struPhysicalPath; // e.g., c:/test/mysite
STRU m_struHttpsPort; // e.g., /LM/W3SVC/4/ROOT/Inproc
STRU m_struPort; STRU m_struPort;
STRU m_struCommandLine; STRU m_struCommandLine;
@ -281,7 +283,7 @@ private:
HANDLE m_hProcessWaitHandle; HANDLE m_hProcessWaitHandle;
HANDLE m_hShutdownHandle; 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. // m_hProcessHandle process if it does.
// //
HANDLE m_hChildProcessHandles[MAX_ACTIVE_CHILD_PROCESSES]; 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_AUTH_ENV_STR L"ASPNETCORE_IIS_HTTPAUTH="
#define ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED_ENV_STR L"ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED=" #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_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_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;"

View File

@ -192,6 +192,7 @@ public:
_In_ BOOL fBasicAuthEnabled, _In_ BOOL fBasicAuthEnabled,
_In_ BOOL fAnonymousAuthEnabled, _In_ BOOL fAnonymousAuthEnabled,
_In_ PCWSTR pApplicationPhysicalPath, _In_ PCWSTR pApplicationPhysicalPath,
_In_ PCWSTR pHttpsPort,
_Out_ ENVIRONMENT_VAR_HASH** ppEnvironmentVarTable _Out_ ENVIRONMENT_VAR_HASH** ppEnvironmentVarTable
) )
{ {
@ -203,6 +204,7 @@ public:
ENVIRONMENT_VAR_ENTRY* pHostingEntry = NULL; ENVIRONMENT_VAR_ENTRY* pHostingEntry = NULL;
ENVIRONMENT_VAR_ENTRY* pIISAuthEntry = NULL; ENVIRONMENT_VAR_ENTRY* pIISAuthEntry = NULL;
ENVIRONMENT_VAR_ENTRY* pIISPathEntry = NULL; ENVIRONMENT_VAR_ENTRY* pIISPathEntry = NULL;
ENVIRONMENT_VAR_ENTRY* pIISHttpsPort = NULL;
ENVIRONMENT_VAR_HASH* pEnvironmentVarTable = NULL; ENVIRONMENT_VAR_HASH* pEnvironmentVarTable = NULL;
pEnvironmentVarTable = new ENVIRONMENT_VAR_HASH(); pEnvironmentVarTable = new ENVIRONMENT_VAR_HASH();
@ -240,6 +242,26 @@ public:
goto Finished; 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); pEnvironmentVarTable->FindKey((PWSTR)ASPNETCORE_IIS_AUTH_ENV_STR, &pIISAuthEntry);
if (pIISAuthEntry != NULL) if (pIISAuthEntry != NULL)
{ {

View File

@ -8,7 +8,6 @@
#include "exceptions.h" #include "exceptions.h"
#include "config_utility.h" #include "config_utility.h"
REQUESTHANDLER_CONFIG::~REQUESTHANDLER_CONFIG() REQUESTHANDLER_CONFIG::~REQUESTHANDLER_CONFIG()
{ {
if (m_ppStrArguments != NULL) if (m_ppStrArguments != NULL)
@ -28,6 +27,7 @@ REQUESTHANDLER_CONFIG::~REQUESTHANDLER_CONFIG()
HRESULT HRESULT
REQUESTHANDLER_CONFIG::CreateRequestHandlerConfig( REQUESTHANDLER_CONFIG::CreateRequestHandlerConfig(
_In_ IHttpServer *pHttpServer, _In_ IHttpServer *pHttpServer,
_In_ IHttpSite *pSite,
_In_ IHttpApplication *pHttpApplication, _In_ IHttpApplication *pHttpApplication,
_Out_ REQUESTHANDLER_CONFIG **ppAspNetCoreConfig _Out_ REQUESTHANDLER_CONFIG **ppAspNetCoreConfig
) )
@ -49,7 +49,7 @@ REQUESTHANDLER_CONFIG::CreateRequestHandlerConfig(
pRequestHandlerConfig = new REQUESTHANDLER_CONFIG; pRequestHandlerConfig = new REQUESTHANDLER_CONFIG;
hr = pRequestHandlerConfig->Populate(pHttpServer, pHttpApplication); hr = pRequestHandlerConfig->Populate(pHttpServer, pSite, pHttpApplication);
if (FAILED(hr)) if (FAILED(hr))
{ {
goto Finished; goto Finished;
@ -85,6 +85,7 @@ Finished:
HRESULT HRESULT
REQUESTHANDLER_CONFIG::Populate( REQUESTHANDLER_CONFIG::Populate(
IHttpServer *pHttpServer, IHttpServer *pHttpServer,
IHttpSite *pSite,
IHttpApplication *pHttpApplication IHttpApplication *pHttpApplication
) )
{ {
@ -122,6 +123,19 @@ REQUESTHANDLER_CONFIG::Populate(
} }
pAdminManager = pHttpServer->GetAdminManager(); 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()); hr = m_struConfigPath.Copy(pHttpApplication->GetAppConfigPath());
if (FAILED(hr)) if (FAILED(hr))
{ {

View File

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

View File

@ -17,5 +17,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
public bool fWindowsAuthEnabled; public bool fWindowsAuthEnabled;
public bool fBasicAuthEnabled; public bool fBasicAuthEnabled;
public bool fAnonymousAuthEnable; 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;
using System.Buffers; using System.Buffers;
@ -10,6 +8,7 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -32,6 +31,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
private readonly ILogger<IISHttpServer> _logger; private readonly ILogger<IISHttpServer> _logger;
private readonly IISServerOptions _options; private readonly IISServerOptions _options;
private readonly IISNativeApplication _nativeApplication; private readonly IISNativeApplication _nativeApplication;
private readonly ServerAddressesFeature _serverAddressesFeature;
private volatile int _stopping; private volatile int _stopping;
private bool Stopping => _stopping == 1; private bool Stopping => _stopping == 1;
@ -71,11 +71,14 @@ namespace Microsoft.AspNetCore.Server.IIS.Core
_applicationLifetime = applicationLifetime; _applicationLifetime = applicationLifetime;
_logger = logger; _logger = logger;
_options = options.Value; _options = options.Value;
_serverAddressesFeature = new ServerAddressesFeature();
if (_options.ForwardWindowsAuthentication) if (_options.ForwardWindowsAuthentication)
{ {
authentication.AddScheme(new AuthenticationScheme(IISServerDefaults.AuthenticationScheme, _options.AuthenticationDisplayName, typeof(IISServerAuthenticationHandler))); authentication.AddScheme(new AuthenticationScheme(IISServerDefaults.AuthenticationScheme, _options.AuthenticationDisplayName, typeof(IISServerAuthenticationHandler)));
} }
Features.Set<IServerAddressesFeature>(_serverAddressesFeature);
} }
public Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken) 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); _iisContextFactory = new IISContextFactory<TContext>(_memoryPool, application, _options, this, _logger);
_nativeApplication.RegisterCallbacks(_requestHandler, _shutdownHandler, _onDisconnect, _onAsyncCompletion, (IntPtr)_httpServerHandle, (IntPtr)_httpServerHandle); _nativeApplication.RegisterCallbacks(_requestHandler, _shutdownHandler, _onDisconnect, _onAsyncCompletion, (IntPtr)_httpServerHandle, (IntPtr)_httpServerHandle);
_serverAddressesFeature.Addresses = _options.ServerAddresses;
return Task.CompletedTask; 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Server.IIS.Core namespace Microsoft.AspNetCore.Server.IIS.Core
{ {
internal class IISServerSetupFilter : IStartupFilter internal class IISServerSetupFilter : IStartupFilter
{ {
private string _virtualPath; private readonly string _virtualPath;
public IISServerSetupFilter(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. /// IIS has a non-anonymous authentication enabled, or for back compat with ANCMs that did not provide this information.
/// </summary> /// </summary>
internal bool ForwardWindowsAuthentication { get; set; } = true; 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.AddSingleton<IStartupFilter>(new IISServerSetupFilter(iisConfigData.pwzVirtualApplicationPath));
services.AddAuthenticationCore(); services.AddAuthenticationCore();
services.Configure<IISServerOptions>( 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. // 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.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
using Xunit; using Xunit;
@ -25,5 +26,13 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
Assert.Equal("Success", await _fixture.Client.GetStringAsync(path + "/path" + "?query")); 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); siteElement.Add(newApplication);
// IIS Express requires root application to exist // IIS Express requires root application to exist
var rootApplicationDirectory = new DirectoryInfo(contentRoot + "rootApp");
rootApplicationDirectory.Create();
_rootApplication = new PublishedApplication(rootApplicationDirectory.FullName, Logger); _rootApplication = new PublishedApplication(Helpers.CreateEmptyApplication(config, contentRoot), 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);
} }
private static string GetWebConfigLocation(string siteRoot) private static string GetWebConfigLocation(string siteRoot)

View File

@ -237,5 +237,30 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
throw ex; 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"> <ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32"> <ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration> <Configuration>Debug</Configuration>
@ -58,6 +57,7 @@
<ClCompile Include="inprocess_application_tests.cpp" /> <ClCompile Include="inprocess_application_tests.cpp" />
<ClCompile Include="main.cpp" /> <ClCompile Include="main.cpp" />
<ClCompile Include="PipeOutputManagerTests.cpp" /> <ClCompile Include="PipeOutputManagerTests.cpp" />
<ClCompile Include="BindingInformationTest.cpp" />
<ClCompile Include="utility_tests.cpp" /> <ClCompile Include="utility_tests.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -195,4 +195,4 @@
</Lib> </Lib>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<Import Project=".\NativeTests.targets" /> <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;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Security.Principal; using System.Security.Principal;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.IISIntegration.FunctionalTests; using Microsoft.AspNetCore.IISIntegration.FunctionalTests;
using Microsoft.AspNetCore.Server.IISIntegration; using Microsoft.AspNetCore.Server.IISIntegration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Xunit; using Xunit;
@ -21,13 +20,9 @@ namespace TestSite
{ {
public partial class Startup public partial class Startup
{ {
private IServerAddressesFeature _serverAddresses;
public void Configure(IApplicationBuilder app) public void Configure(IApplicationBuilder app)
{ {
TestStartup.Register(app, this); TestStartup.Register(app, this);
_serverAddresses = app.ServerFeatures.Get<IServerAddressesFeature>();
} }
public Task Path(HttpContext ctx) => ctx.Response.WriteAsync(ctx.Request.Path.Value); 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 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 HelloWorld(HttpContext ctx) => ctx.Response.WriteAsync("Hello World");
public Task HttpsHelloWorld(HttpContext ctx) => public Task HttpsHelloWorld(HttpContext ctx) =>
@ -97,9 +91,11 @@ namespace TestSite
await context.Response.WriteAsync(Process.GetCurrentProcess().Id.ToString()); 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.Authentication;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection; 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 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) private async Task ConsoleWrite(HttpContext ctx)
{ {
Console.WriteLine("TEST MESSAGE"); Console.WriteLine("TEST MESSAGE");