Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/IISIntegration/build/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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)",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)",
Expand Down
10 changes: 10 additions & 0 deletions src/IISIntegration/samples/NativeIISSample/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
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
{
Expand Down Expand Up @@ -89,6 +92,13 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
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);
}
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ class ApplicationFactory
{
// 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()}
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// 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;
}

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)
{
// Expected format:
// IP:PORT:HOST
// where IP or HOST can be empty
const auto information = binding->GetRequiredString(CS_SITE_BINDING_INFORMATION);
const auto firstColon = information.find(CS_SITE_BINDING_INFORMATION_DELIMITER) + 1;
const auto lastColon = information.find_last_of(CS_SITE_BINDING_INFORMATION_DELIMITER);

std::wstring host;
// Check that : is not the last character
if (lastColon != information.length() + 1)
{
auto const afterLastColon = lastColon + 1;
host = information.substr(afterLastColon, information.length() - afterLastColon);
}
if (host.length() == 0)
{
host = CS_SITE_BINDING_INFORMATION_ALL_HOSTS;
}

items.emplace_back(
binding->GetRequiredString(CS_SITE_BINDING_PROTOCOL),
host,
information.substr(firstColon, lastColon - firstColon)
);
}
}
}

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";";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is pathBase encoded or decoded here? e.g. if I have a space in my base path do I get ' ' or %20? The escaped form should be preferred in this url form.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The host is likely in unicode, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a test

}

return result;
}

static
std::wstring GetHttpsPort(const std::vector<BindingInformation> & bindings)
{
for (auto binding : bindings)
{
if (equals_ignore_case(binding.QueryProtocol(), CS_SITE_BINDING_PROTOCOL_HTTPS))
{
return binding.QueryPort();
}
}
return L"";
}

private:
std::wstring m_protocol;
std::wstring m_port;
std::wstring m_host;
};
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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->GetRequiredString(CS_ASPNETCORE_COLLECTION_ITEM_VALUE));
}
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,17 @@ class ConfigurationSection: NonCopyable
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
HRESULT findElementResult;
CComPtr<IAppHostElement> element = nullptr;
CComPtr<IAppHostElementCollection> elementCollection = nullptr;
CComPtr<IAppHostElement> collectionEntry = nullptr;
ENUM_INDEX index{};
CComPtr<IAppHostElement> element = nullptr;

if (FAILED_LOG(GetElementChildByName(m_element, name.c_str(), &element)))
{
return pairs;
return std::nullopt;
}

THROW_IF_FAILED(element->get_Collection(&elementCollection));
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<IAppHostElementCollection> elementCollection = nullptr;
CComPtr<IAppHostElement> collectionEntry = nullptr;
ENUM_INDEX index{};

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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ class WebConfigConfigurationSection: public ConfigurationSection
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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),
Expand Down Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#pragma once

#include <string>
#include "BindingInformation.h"
#include "ConfigurationSource.h"
#include "WebConfigConfigurationSource.h"

Expand Down Expand Up @@ -92,11 +93,18 @@ class InProcessOptions: NonCopyable
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);

Expand All @@ -113,6 +121,7 @@ class InProcessOptions: NonCopyable
DWORD m_dwStartupTimeLimitInMS;
DWORD m_dwShutdownTimeLimitInMS;
std::vector<std::pair<std::wstring, std::wstring>> m_environmentVariables;
std::vector<BindingInformation> m_bindingInformation;

protected:
InProcessOptions() = default;
Expand Down
Loading