Merge pull request #1321 from dotnet-maestro-bot/merge/release/2.2-to-master

[automated] Merge branch 'release/2.2' => 'master'
This commit is contained in:
Pavel Krymets 2018-08-24 21:42:54 -07:00 committed by GitHub
commit c86f308a3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 1020 additions and 278 deletions

View File

@ -19,6 +19,6 @@
</ItemGroup>
<PropertyGroup>
<AspNetCoreModuleHostingModel>inprocess</AspNetCoreModuleHostingModel>
<AspNetCoreHostingModel>inprocess</AspNetCoreHostingModel>
</PropertyGroup>
</Project>

View File

@ -10,7 +10,7 @@
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#define FileDescription "IIS AspNetCore Module. Commit: " CommitHash
#define FileDescription "IIS ASP.NET Core Module. Commit: " CommitHash
/////////////////////////////////////////////////////////////////////////////
//

View File

@ -231,7 +231,7 @@
<ClInclude Include="applicationinfo.h" />
<ClInclude Include="AppOfflineApplication.h" />
<ClInclude Include="AppOfflineHandler.h" />
<ClInclude Include="aspnetcore_shim_config.h" />
<ClInclude Include="ShimOptions.h" />
<ClInclude Include="globalmodule.h" />
<ClInclude Include="applicationmanager.h" />
<ClInclude Include="HandlerResolver.h" />
@ -246,7 +246,7 @@
<ClCompile Include="applicationmanager.cpp" />
<ClCompile Include="AppOfflineApplication.cpp" />
<ClCompile Include="AppOfflineHandler.cpp" />
<ClCompile Include="aspnetcore_shim_config.cpp" />
<ClCompile Include="ShimOptions.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="globalmodule.cpp" />
<ClCompile Include="HandlerResolver.cpp" />

View File

@ -12,6 +12,8 @@
#include "file_utility.h"
#include "LoggingHelpers.h"
#include "resources.h"
#include "ConfigurationLoadException.h"
#include "WebConfigConfigurationSource.h"
const PCWSTR HandlerResolver::s_pwzAspnetcoreInProcessRequestHandlerName = L"aspnetcorev2_inprocess.dll";
const PCWSTR HandlerResolver::s_pwzAspnetcoreOutOfProcessRequestHandlerName = L"aspnetcorev2_outofprocess.dll";
@ -25,7 +27,7 @@ HandlerResolver::HandlerResolver(HMODULE hModule, IHttpServer &pServer)
}
HRESULT
HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, ASPNETCORE_SHIM_CONFIG& pConfiguration, std::unique_ptr<ApplicationFactory>& pApplicationFactory)
HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, ShimOptions& pConfiguration, std::unique_ptr<ApplicationFactory>& pApplicationFactory)
{
HRESULT hr;
PCWSTR pstrHandlerDllName;
@ -65,7 +67,7 @@ HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, ASPN
RETURN_IF_FAILED(LoggingHelpers::CreateLoggingProvider(
pConfiguration.QueryStdoutLogEnabled(),
!m_pServer.IsCommandLineLaunch(),
pConfiguration.QueryStdoutLogFile()->QueryStr(),
pConfiguration.QueryStdoutLogFile().c_str(),
pApplication.GetApplicationPhysicalPath(),
outputManager));
@ -129,20 +131,20 @@ HandlerResolver::GetApplicationFactory(IHttpApplication &pApplication, std::uniq
{
try
{
ASPNETCORE_SHIM_CONFIG pConfiguration;
RETURN_IF_FAILED(pConfiguration.Populate(&m_pServer, &pApplication));
const WebConfigConfigurationSource configurationSource(m_pServer.GetAdminManager(), pApplication);
ShimOptions options(configurationSource);
SRWExclusiveLock lock(m_requestHandlerLoadLock);
if (m_loadedApplicationHostingModel != HOSTING_UNKNOWN)
{
// Mixed hosting models
if (m_loadedApplicationHostingModel != pConfiguration.QueryHostingModel())
if (m_loadedApplicationHostingModel != options.QueryHostingModel())
{
EventLog::Error(
ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR,
ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG,
pApplication.GetApplicationId(),
pConfiguration.QueryHostingModel());
options.QueryHostingModel());
return E_FAIL;
}
@ -158,11 +160,20 @@ HandlerResolver::GetApplicationFactory(IHttpApplication &pApplication, std::uniq
}
}
m_loadedApplicationHostingModel = pConfiguration.QueryHostingModel();
m_loadedApplicationHostingModel = options.QueryHostingModel();
m_loadedApplicationId = pApplication.GetApplicationId();
RETURN_IF_FAILED(LoadRequestHandlerAssembly(pApplication, pConfiguration, pApplicationFactory));
RETURN_IF_FAILED(LoadRequestHandlerAssembly(pApplication, options, pApplicationFactory));
}
catch(ConfigurationLoadException &ex)
{
EventLog::Error(
ASPNETCORE_CONFIGURATION_LOAD_ERROR,
ASPNETCORE_CONFIGURATION_LOAD_ERROR_MSG,
ex.get_message().c_str());
RETURN_HR(E_FAIL);
}
CATCH_RETURN();
return S_OK;
@ -178,7 +189,7 @@ void HandlerResolver::ResetHostingModel()
HRESULT
HandlerResolver::FindNativeAssemblyFromGlobalLocation(
ASPNETCORE_SHIM_CONFIG& pConfiguration,
ShimOptions& pConfiguration,
PCWSTR pstrHandlerDllName,
std::wstring& handlerDllPath
)

View File

@ -2,10 +2,10 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
#include "aspnetcore_shim_config.h"
#include <memory>
#include <string>
#include "ShimOptions.h"
#include "hostfxroptions.h"
#include "HandleWrapper.h"
#include "ApplicationFactory.h"
@ -18,8 +18,8 @@ public:
void ResetHostingModel();
private:
HRESULT LoadRequestHandlerAssembly(IHttpApplication &pApplication, ASPNETCORE_SHIM_CONFIG& pConfiguration, std::unique_ptr<ApplicationFactory>& pApplicationFactory);
HRESULT FindNativeAssemblyFromGlobalLocation(ASPNETCORE_SHIM_CONFIG& pConfiguration, PCWSTR libraryName, std::wstring& handlerDllPath);
HRESULT LoadRequestHandlerAssembly(IHttpApplication &pApplication, ShimOptions& pConfiguration, std::unique_ptr<ApplicationFactory>& pApplicationFactory);
HRESULT FindNativeAssemblyFromGlobalLocation(ShimOptions& pConfiguration, PCWSTR libraryName, std::wstring& handlerDllPath);
HRESULT FindNativeAssemblyFromHostfxr(HOSTFXR_OPTIONS& hostfxrOptions, PCWSTR libraryName, std::wstring& handlerDllPath);
HMODULE m_hModule;

View File

@ -15,7 +15,7 @@ class PollingAppOfflineApplication: public APPLICATION
{
public:
PollingAppOfflineApplication(IHttpApplication& pApplication, PollingAppOfflineApplicationMode mode)
:
: APPLICATION(pApplication),
m_ulLastCheckTime(0),
m_appOfflineLocation(GetAppOfflineLocation(pApplication)),
m_fAppOfflineFound(false),

View File

@ -0,0 +1,43 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "ShimOptions.h"
#include "StringHelpers.h"
#include "ConfigurationLoadException.h"
#define CS_ASPNETCORE_HANDLER_VERSION L"handlerVersion"
ShimOptions::ShimOptions(const ConfigurationSource &configurationSource) :
m_hostingModel(HOSTING_UNKNOWN),
m_fStdoutLogEnabled(false)
{
auto const section = configurationSource.GetRequiredSection(CS_ASPNETCORE_SECTION);
auto hostingModel = section->GetString(CS_ASPNETCORE_HOSTING_MODEL).value_or(L"");
if (hostingModel.empty() || equals_ignore_case(hostingModel, CS_ASPNETCORE_HOSTING_MODEL_OUTOFPROCESS))
{
m_hostingModel = HOSTING_OUT_PROCESS;
}
else if (equals_ignore_case(hostingModel, CS_ASPNETCORE_HOSTING_MODEL_INPROCESS))
{
m_hostingModel = HOSTING_IN_PROCESS;
}
else
{
throw ConfigurationLoadException(format(
L"Unknown hosting model '%s'. Please specify either hostingModel=\"inprocess\" "
"or hostingModel=\"outofprocess\" in the web.config file.", hostingModel.c_str()));
}
if (m_hostingModel == HOSTING_OUT_PROCESS)
{
const auto handlerSettings = section->GetKeyValuePairs(CS_ASPNETCORE_HANDLER_SETTINGS);
m_strHandlerVersion = find_element(handlerSettings, CS_ASPNETCORE_HANDLER_VERSION).value_or(std::wstring());
}
m_strProcessPath = section->GetRequiredString(CS_ASPNETCORE_PROCESS_EXE_PATH);
m_strArguments = section->GetString(CS_ASPNETCORE_PROCESS_ARGUMENTS).value_or(CS_ASPNETCORE_PROCESS_ARGUMENTS_DEFAULT);
m_fStdoutLogEnabled = section->GetRequiredBool(CS_ASPNETCORE_STDOUT_LOG_ENABLED);
m_struStdoutLogFile = section->GetRequiredString(CS_ASPNETCORE_STDOUT_LOG_FILE);
}

View File

@ -0,0 +1,65 @@
// 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 "exceptions.h"
enum APP_HOSTING_MODEL
{
HOSTING_UNKNOWN = 0,
HOSTING_IN_PROCESS,
HOSTING_OUT_PROCESS
};
class ShimOptions: NonCopyable
{
public:
const std::wstring&
QueryProcessPath() const
{
return m_strProcessPath;
}
const std::wstring&
QueryArguments() const
{
return m_strArguments;
}
APP_HOSTING_MODEL
QueryHostingModel() const
{
return m_hostingModel;
}
const std::wstring&
QueryHandlerVersion() const
{
return m_strHandlerVersion;
}
BOOL
QueryStdoutLogEnabled() const
{
return m_fStdoutLogEnabled;
}
const std::wstring&
QueryStdoutLogFile() const
{
return m_struStdoutLogFile;
}
ShimOptions(const ConfigurationSource &configurationSource);
private:
std::wstring m_strArguments;
std::wstring m_strProcessPath;
APP_HOSTING_MODEL m_hostingModel;
std::wstring m_strHandlerVersion;
std::wstring m_struStdoutLogFile;
bool m_fStdoutLogEnabled;
};

View File

@ -4,7 +4,6 @@
#pragma once
#include "hostfxroptions.h"
#include "aspnetcore_shim_config.h"
#include "iapplication.h"
#include "SRWSharedLock.h"
#include "HandlerResolver.h"

View File

@ -1,80 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "aspnetcore_shim_config.h"
#include "EventLog.h"
#include "config_utility.h"
#include "ahutil.h"
HRESULT
ASPNETCORE_SHIM_CONFIG::Populate(
IHttpServer *pHttpServer,
IHttpApplication *pHttpApplication
)
{
STACK_STRU(strHostingModel, 12);
CComPtr<IAppHostElement> pAspNetCoreElement;
IAppHostAdminManager *pAdminManager = pHttpServer->GetAdminManager();
const CComBSTR bstrAspNetCoreSection = CS_ASPNETCORE_SECTION;
const CComBSTR applicationConfigPath = pHttpApplication->GetAppConfigPath();
RETURN_IF_FAILED(pAdminManager->GetAdminSection(bstrAspNetCoreSection,
applicationConfigPath,
&pAspNetCoreElement));
CComBSTR struProcessPath;
RETURN_IF_FAILED(GetElementStringProperty(pAspNetCoreElement,
CS_ASPNETCORE_PROCESS_EXE_PATH,
&struProcessPath));
m_strProcessPath = struProcessPath;
// Swallow this error for backward compatibility
// Use default behavior for empty string
GetElementStringProperty(pAspNetCoreElement,
CS_ASPNETCORE_HOSTING_MODEL,
&strHostingModel);
if (strHostingModel.IsEmpty() || strHostingModel.Equals(L"outofprocess", TRUE))
{
m_hostingModel = HOSTING_OUT_PROCESS;
}
else if (strHostingModel.Equals(L"inprocess", TRUE))
{
m_hostingModel = HOSTING_IN_PROCESS;
}
else
{
// block unknown hosting value
EventLog::Error(
ASPNETCORE_EVENT_UNKNOWN_HOSTING_MODEL_ERROR,
ASPNETCORE_EVENT_UNKNOWN_HOSTING_MODEL_ERROR_MSG,
strHostingModel.QueryStr());
RETURN_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));
}
CComBSTR struArguments;
RETURN_IF_FAILED(GetElementStringProperty(pAspNetCoreElement,
CS_ASPNETCORE_PROCESS_ARGUMENTS,
&struArguments));
m_strArguments = struArguments;
if (m_hostingModel == HOSTING_OUT_PROCESS)
{
STRU struHandlerVersion;
RETURN_IF_FAILED(ConfigUtility::FindHandlerVersion(pAspNetCoreElement, struHandlerVersion));
m_strHandlerVersion = struHandlerVersion.QueryStr();
}
RETURN_IF_FAILED(GetElementBoolProperty(pAspNetCoreElement,
CS_ASPNETCORE_STDOUT_LOG_ENABLED,
&m_fStdoutLogEnabled));
RETURN_IF_FAILED(GetElementStringProperty(pAspNetCoreElement,
CS_ASPNETCORE_STDOUT_LOG_FILE,
&m_struStdoutLogFile));
return S_OK;
}

View File

@ -1,85 +0,0 @@
// 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 <windows.h>
#include <httpserv.h>
#define CS_ASPNETCORE_SECTION L"system.webServer/aspNetCore"
#define CS_ASPNETCORE_PROCESS_EXE_PATH L"processPath"
#define CS_ASPNETCORE_PROCESS_ARGUMENTS L"arguments"
#define CS_ASPNETCORE_HOSTING_MODEL L"hostingModel"
#define CS_ASPNETCORE_STDOUT_LOG_ENABLED L"stdoutLogEnabled"
#define CS_ASPNETCORE_STDOUT_LOG_FILE L"stdoutLogFile"
enum APP_HOSTING_MODEL
{
HOSTING_UNKNOWN = 0,
HOSTING_IN_PROCESS,
HOSTING_OUT_PROCESS
};
class ASPNETCORE_SHIM_CONFIG
{
public:
virtual
~ASPNETCORE_SHIM_CONFIG() = default;
HRESULT
Populate(
IHttpServer *pHttpServer,
IHttpApplication *pHttpApplication
);
std::wstring&
QueryProcessPath()
{
return m_strProcessPath;
}
std::wstring&
QueryArguments()
{
return m_strArguments;
}
APP_HOSTING_MODEL
QueryHostingModel()
{
return m_hostingModel;
}
std::wstring&
QueryHandlerVersion()
{
return m_strHandlerVersion;
}
BOOL
QueryStdoutLogEnabled()
{
return m_fStdoutLogEnabled;
}
STRU*
QueryStdoutLogFile()
{
return &m_struStdoutLogFile;
}
ASPNETCORE_SHIM_CONFIG() :
m_hostingModel(HOSTING_UNKNOWN)
{
}
private:
std::wstring m_strArguments;
std::wstring m_strProcessPath;
APP_HOSTING_MODEL m_hostingModel;
std::wstring m_strHandlerVersion;
BOOL m_fStdoutLogEnabled;
STRU m_struStdoutLogFile;
};

View File

@ -10,7 +10,7 @@
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#define FileDescription "IIS AspNetCore Module V2. Commit: " CommitHash
#define FileDescription "IIS ASP.NET Core Module V2. Commit: " CommitHash
/////////////////////////////////////////////////////////////////////////////
//

View File

@ -195,6 +195,9 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="application.h" />
<ClInclude Include="ConfigurationLoadException.h" />
<ClInclude Include="ConfigurationSection.h" />
<ClInclude Include="ConfigurationSource.h" />
<ClInclude Include="config_utility.h" />
<ClInclude Include="Environment.h" />
<ClInclude Include="EventLog.h" />
@ -211,6 +214,7 @@
<ClInclude Include="IOutputManager.h" />
<ClInclude Include="irequesthandler.h" />
<ClInclude Include="LoggingHelpers.h" />
<ClInclude Include="NonCopyable.h" />
<ClInclude Include="NullOutputManager.h" />
<ClInclude Include="PipeOutputManager.h" />
<ClInclude Include="StdWrapper.h" />
@ -221,8 +225,12 @@
<ClInclude Include="stdafx.h" />
<ClInclude Include="StringHelpers.h" />
<ClInclude Include="sttimer.h" />
<ClInclude Include="WebConfigConfigurationSection.h" />
<ClInclude Include="WebConfigConfigurationSource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="ConfigurationSection.cpp" />
<ClCompile Include="ConfigurationSource.cpp" />
<ClCompile Include="debugutil.cpp" />
<ClCompile Include="Environment.cpp" />
<ClCompile Include="EventLog.cpp" />
@ -245,6 +253,8 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="StringHelpers.cpp" />
<ClCompile Include="WebConfigConfigurationSection.cpp" />
<ClCompile Include="WebConfigConfigurationSource.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IISLib\IISLib.vcxproj">

View File

@ -0,0 +1,20 @@
// 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>
class ConfigurationLoadException: public std::runtime_error
{
public:
ConfigurationLoadException(std::wstring msg)
: runtime_error("Configuration load exception has occured"), message(std::move(msg))
{
}
std::wstring get_message() const { return message; }
private:
std::wstring message;
};

View File

@ -0,0 +1,57 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "ConfigurationSection.h"
#include "StringHelpers.h"
#include "ConfigurationLoadException.h"
std::wstring ConfigurationSection::GetRequiredString(const std::wstring& name) const
{
auto result = GetString(name);
if (!result.has_value() || result.value().empty())
{
ThrowRequiredException(name);
}
return result.value();
}
bool ConfigurationSection::GetRequiredBool(const std::wstring& name) const
{
auto result = GetBool(name);
if (!result.has_value())
{
ThrowRequiredException(name);
}
return result.value();
}
DWORD ConfigurationSection::GetRequiredTimespan(const std::wstring& name) const
{
auto result = GetTimespan(name);
if (!result.has_value())
{
ThrowRequiredException(name);
}
return result.value();
}
void ConfigurationSection::ThrowRequiredException(const std::wstring& name)
{
throw ConfigurationLoadException(format(L"Attribute '%s' is required.", name.c_str()));
}
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(
pairs.begin(),
pairs.end(),
[&](const std::pair<std::wstring, std::wstring>& pair) { return equals_ignore_case(pair.first, name); });
if (iter == pairs.end())
{
return std::nullopt;
}
return std::make_optional(iter->second);
}

View File

@ -0,0 +1,46 @@
// 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 <optional>
#include <vector>
#include "NonCopyable.h"
#define CS_ASPNETCORE_COLLECTION_ITEM_NAME L"name"
#define CS_ASPNETCORE_COLLECTION_ITEM_VALUE L"value"
#define CS_ASPNETCORE_ENVIRONMENT_VARIABLES L"environmentVariables"
#define CS_ASPNETCORE_STDOUT_LOG_FILE L"stdoutLogFile"
#define CS_ASPNETCORE_STDOUT_LOG_ENABLED L"stdoutLogEnabled"
#define CS_ASPNETCORE_PROCESS_EXE_PATH L"processPath"
#define CS_ASPNETCORE_PROCESS_ARGUMENTS L"arguments"
#define CS_ASPNETCORE_PROCESS_ARGUMENTS_DEFAULT L""
#define CS_ASPNETCORE_HOSTING_MODEL_OUTOFPROCESS L"outofprocess"
#define CS_ASPNETCORE_HOSTING_MODEL_INPROCESS L"inprocess"
#define CS_ASPNETCORE_HOSTING_MODEL L"hostingModel"
#define CS_ASPNETCORE_HANDLER_SETTINGS L"handlerSettings"
#define CS_ASPNETCORE_DISABLE_START_UP_ERROR_PAGE L"disableStartUpErrorPage"
#define CS_ENABLED L"enabled"
class ConfigurationSection: NonCopyable
{
public:
ConfigurationSection() = default;
virtual ~ConfigurationSection() = default;
virtual std::optional<std::wstring> GetString(const std::wstring& name) const = 0;
virtual std::optional<bool> GetBool(const std::wstring& name) const = 0;
virtual std::optional<DWORD> GetTimespan(const std::wstring& name) const = 0;
std::wstring GetRequiredString(const std::wstring& name) const;
bool GetRequiredBool(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;
protected:
static void ThrowRequiredException(const std::wstring& name);
};
std::optional<std::wstring> find_element(const std::vector<std::pair<std::wstring, std::wstring>>& pairs, const std::wstring& name);

View File

@ -0,0 +1,18 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "ConfigurationSource.h"
#include "StringHelpers.h"
#include "ConfigurationLoadException.h"
std::shared_ptr<ConfigurationSection> ConfigurationSource::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;
}

View File

@ -0,0 +1,24 @@
// 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 <memory>
#include <string>
#include <vector>
#include "NonCopyable.h"
#include "ConfigurationSection.h"
#define CS_ASPNETCORE_SECTION L"system.webServer/aspNetCore"
#define CS_WINDOWS_AUTHENTICATION_SECTION L"system.webServer/security/authentication/windowsAuthentication"
#define CS_BASIC_AUTHENTICATION_SECTION L"system.webServer/security/authentication/basicAuthentication"
#define CS_ANONYMOUS_AUTHENTICATION_SECTION L"system.webServer/security/authentication/anonymousAuthentication"
class ConfigurationSource: NonCopyable
{
public:
ConfigurationSource() = default;
virtual ~ConfigurationSource() = default;
virtual std::shared_ptr<ConfigurationSection> GetSection(const std::wstring& name) const = 0;
std::shared_ptr<ConfigurationSection> GetRequiredSection(const std::wstring& name) const;
};

View File

@ -1,8 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include <array>
#include "EventLog.h"
#include "debugutil.h"
#include "StringHelpers.h"
extern HANDLE g_hEventLog;
@ -13,6 +15,19 @@ EventLog::LogEvent(
_In_ LPCWSTR pstrMsg
)
{
// Static locals to avoid getting the process ID and string multiple times.
// Effectively have the same semantics as global variables, except initialized
// on first occurence.
static const auto processIdString = GetProcessIdString();
static const auto versionInfoString = GetVersionInfoString();
std::array<LPCWSTR, 3> eventLogDataStrings
{
pstrMsg,
processIdString.c_str(),
versionInfoString.c_str()
};
if (g_hEventLog != NULL)
{
ReportEventW(g_hEventLog,
@ -20,9 +35,9 @@ EventLog::LogEvent(
0, // wCategory
dwEventId,
NULL, // lpUserSid
1, // wNumStrings
3, // wNumStrings
0, // dwDataSize,
&pstrMsg,
eventLogDataStrings.data(),
NULL // lpRawData
);
}

View File

@ -0,0 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
class NonCopyable {
public:
NonCopyable() = default;
NonCopyable(const NonCopyable&) = default;
NonCopyable& operator=(const NonCopyable&) = default;
};

View File

@ -13,3 +13,8 @@ bool ends_with(const std::wstring &source, const std::wstring &suffix, bool igno
const auto offset = source.length() - suffix.length();
return CSTR_EQUAL == CompareStringOrdinal(source.c_str() + offset, static_cast<int>(suffix.length()), suffix.c_str(), static_cast<int>(suffix.length()), ignoreCase);
}
bool equals_ignore_case(const std::wstring& s1, const std::wstring& s2)
{
return CSTR_EQUAL == CompareStringOrdinal(s1.c_str(), static_cast<int>(s1.length()), s2.c_str(), static_cast<int>(s2.length()), true);
}

View File

@ -8,6 +8,9 @@
[[nodiscard]]
bool ends_with(const std::wstring &source, const std::wstring &suffix, bool ignoreCase = false);
[[nodiscard]]
bool equals_ignore_case(const std::wstring& s1, const std::wstring& s2);
template<typename ... Args>
[[nodiscard]]
std::wstring format(const std::wstring& format, Args ... args)
@ -18,3 +21,13 @@ std::wstring format(const std::wstring& format, Args ... args)
return std::wstring(formattedBuffer.get(), formattedBuffer.get() + size - 1);
}
template<typename ... Args>
[[nodiscard]]
std::string format(const std::string& format, Args ... args)
{
const size_t size = snprintf(nullptr, 0, format.c_str(), args ...) + 1; // Extra char for '\0'
std::unique_ptr<char[]> formattedBuffer(new char[size]);
snprintf(formattedBuffer.get(), size, format.c_str(), args ... );
return std::string(formattedBuffer.get(), formattedBuffer.get() + size - 1);
}

View File

@ -0,0 +1,81 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "WebConfigConfigurationSection.h"
#include "exceptions.h"
#include "ahutil.h"
std::optional<std::wstring> WebConfigConfigurationSection::GetString(const std::wstring& name) const
{
CComBSTR result;
if (FAILED_LOG(GetElementStringProperty(m_element, name.c_str(), &result.m_str)))
{
return std::nullopt;
}
return std::make_optional(std::wstring(result));
}
std::optional<bool> WebConfigConfigurationSection::GetBool(const std::wstring& name) const
{
bool result;
if (FAILED_LOG(GetElementBoolProperty(m_element, name.c_str(), &result)))
{
return std::nullopt;
}
return std::make_optional(result);
}
std::optional<DWORD> WebConfigConfigurationSection::GetTimespan(const std::wstring& name) const
{
ULONGLONG result;
if (FAILED_LOG(GetElementRawTimeSpanProperty(m_element, name.c_str(), &result)))
{
return std::nullopt;
}
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::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{};
if (FAILED_LOG(GetElementChildByName(m_element, name.c_str(), &element)))
{
return pairs;
}
THROW_IF_FAILED(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);
collectionEntry.Release();
THROW_IF_FAILED(findElementResult = FindNextElement(elementCollection, &index, &collectionEntry));
}
return pairs;
}

View File

@ -0,0 +1,25 @@
// 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 <atlcomcli.h>
#include <optional>
#include "ConfigurationSection.h"
class WebConfigConfigurationSection: public ConfigurationSection
{
public:
WebConfigConfigurationSection(IAppHostElement* pElement)
: m_element(pElement)
{
}
std::optional<std::wstring> GetString(const std::wstring& name) const override;
std::optional<bool> GetBool(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;
private:
CComPtr<IAppHostElement> m_element;
};

View File

@ -0,0 +1,20 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "WebConfigConfigurationSource.h"
#include "exceptions.h"
#include "WebConfigConfigurationSection.h"
std::shared_ptr<ConfigurationSection> WebConfigConfigurationSource::GetSection(const std::wstring& name) const
{
const CComBSTR bstrAspNetCoreSection = name.c_str();
const CComBSTR applicationConfigPath = m_application.GetAppConfigPath();
IAppHostElement* sectionElement;
if (LOG_IF_FAILED(m_manager->GetAdminSection(bstrAspNetCoreSection, applicationConfigPath, &sectionElement)))
{
return nullptr;
}
return std::make_unique<WebConfigConfigurationSection>(sectionElement);
}

View File

@ -0,0 +1,23 @@
// 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 <atlcomcli.h>
#include "ConfigurationSection.h"
#include "ConfigurationSource.h"
class WebConfigConfigurationSource: public ConfigurationSource
{
public:
WebConfigConfigurationSource(IAppHostAdminManager *pAdminManager, IHttpApplication &pHttpApplication)
: m_manager(pAdminManager),
m_application(pHttpApplication)
{
}
std::shared_ptr<ConfigurationSection> GetSection(const std::wstring& name) const override;
private:
CComPtr<IAppHostAdminManager> m_manager;
IHttpApplication &m_application;
};

View File

@ -3,6 +3,7 @@
#pragma once
#include <string>
#include "iapplication.h"
#include "ntassert.h"
#include "SRWExclusiveLock.h"
@ -20,11 +21,15 @@ public:
return m_fStopCalled ? APPLICATION_STATUS::RECYCLED : APPLICATION_STATUS::RUNNING;
}
APPLICATION()
APPLICATION(const IHttpApplication& pHttpApplication)
: m_fStopCalled(false),
m_cRefs(1)
m_cRefs(1),
m_applicationPhysicalPath(pHttpApplication.GetApplicationPhysicalPath()),
m_applicationConfigPath(pHttpApplication.GetAppConfigPath()),
m_applicationId(pHttpApplication.GetApplicationId())
{
InitializeSRWLock(&m_stateLock);
m_applicationVirtualPath = ToVirtualPath(m_applicationConfigPath);
}
@ -69,10 +74,58 @@ public:
}
}
const std::wstring&
QueryApplicationId() const
{
return m_applicationId;
}
const std::wstring&
QueryApplicationPhysicalPath() const
{
return m_applicationPhysicalPath;
}
const std::wstring&
QueryApplicationVirtualPath() const
{
return m_applicationVirtualPath;
}
const std::wstring&
QueryConfigPath() const
{
return m_applicationConfigPath;
}
protected:
SRWLOCK m_stateLock;
SRWLOCK m_stateLock {};
bool m_fStopCalled;
private:
mutable LONG m_cRefs;
std::wstring m_applicationPhysicalPath;
std::wstring m_applicationVirtualPath;
std::wstring m_applicationConfigPath;
std::wstring m_applicationId;
static std::wstring ToVirtualPath(const std::wstring& configurationPath)
{
auto segments = 0;
auto position = configurationPath.find('/');
// Skip first 4 segments of config path
while (segments != 3 && position != std::wstring::npos)
{
segments++;
position = configurationPath.find('/', position + 1);
}
if (position != std::wstring::npos)
{
return configurationPath.substr(position);
}
return L"/";
}
};

View File

@ -190,7 +190,7 @@ Language=English
.
Messageid=1034
SymbolicName=ASPNETCORE_EVENT_UNKNOWN_HOSTING_MODEL_ERROR
SymbolicName=ASPNETCORE_CONFIGURATION_LOAD_ERROR
Language=English
%1
.

View File

@ -13,6 +13,7 @@
#include "exceptions.h"
#include "atlbase.h"
#include "config_utility.h"
#include "StringHelpers.h"
inline HANDLE g_logFile = INVALID_HANDLE_VALUE;
inline HMODULE g_hModule;
@ -21,43 +22,72 @@ inline SRWLOCK g_logFileLock;
HRESULT
PrintDebugHeader()
{
DWORD verHandle = 0;
UINT size = 0;
LPVOID lpBuffer = NULL;
// Major, minor are stored in dwFileVersionMS field and patch, build in dwFileVersionLS field as pair of 32 bit numbers
DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO, "Initializing logs for %S. %S. %S.",
GetModuleName().c_str(),
GetProcessIdString().c_str(),
GetVersionInfoString().c_str()
);
WCHAR path[MAX_PATH];
RETURN_LAST_ERROR_IF(!GetModuleFileName(g_hModule, path, sizeof(path)));
return S_OK;
}
DWORD verSize = GetFileVersionInfoSize(path, &verHandle);
RETURN_LAST_ERROR_IF(verSize == 0);
std::wstring
GetProcessIdString()
{
return format(L"Process Id: %u.", GetCurrentProcessId());
}
// Allocate memory to hold data structure returned by GetFileVersionInfo
std::vector<BYTE> verData(verSize);
RETURN_LAST_ERROR_IF(!GetFileVersionInfo(path, verHandle, verSize, verData.data()));
RETURN_LAST_ERROR_IF(!VerQueryValue(verData.data(), L"\\", &lpBuffer, &size));
auto verInfo = reinterpret_cast<VS_FIXEDFILEINFO *>(lpBuffer);
// Check result signature
if (verInfo->dwSignature == VS_FFI_SIGNATURE)
std::wstring
GetVersionInfoString()
{
auto func = [](std::wstring& res)
{
DWORD verHandle = 0;
UINT size = 0;
LPVOID lpBuffer = NULL;
auto path = GetModuleName();
DWORD verSize = GetFileVersionInfoSize(path.c_str(), &verHandle);
RETURN_LAST_ERROR_IF(verSize == 0);
// Allocate memory to hold data structure returned by GetFileVersionInfo
std::vector<BYTE> verData(verSize);
RETURN_LAST_ERROR_IF(!GetFileVersionInfo(path.c_str(), verHandle, verSize, verData.data()));
RETURN_LAST_ERROR_IF(!VerQueryValue(verData.data(), L"\\", &lpBuffer, &size));
auto verInfo = reinterpret_cast<VS_FIXEDFILEINFO *>(lpBuffer);
if (verInfo->dwSignature != VS_FFI_SIGNATURE)
{
RETURN_IF_FAILED(E_FAIL);
}
LPVOID pvProductName = NULL;
unsigned int iProductNameLen = 0;
RETURN_LAST_ERROR_IF(!VerQueryValue(verData.data(), _T("\\StringFileInfo\\040904b0\\FileDescription"), &pvProductName, &iProductNameLen));
// Major, minor are stored in dwFileVersionMS field and patch, build in dwFileVersionLS field as pair of 32 bit numbers
DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO, "Initializing logs for %S. ProcessId: %d. File Version: %d.%d.%d.%d. Description: %S",
path,
GetCurrentProcessId(),
( verInfo->dwFileVersionMS >> 16 ) & 0xffff,
( verInfo->dwFileVersionMS >> 0 ) & 0xffff,
( verInfo->dwFileVersionLS >> 16 ) & 0xffff,
( verInfo->dwFileVersionLS >> 0 ) & 0xffff,
pvProductName
);
}
res = format(L"File Version: %d.%d.%d.%d. Description: %s",
(verInfo->dwFileVersionMS >> 16) & 0xffff,
(verInfo->dwFileVersionMS >> 0) & 0xffff,
(verInfo->dwFileVersionLS >> 16) & 0xffff,
(verInfo->dwFileVersionLS >> 0) & 0xffff,
pvProductName);
return S_OK;
};
return S_OK;
std::wstring versionInfoString;
return func(versionInfoString) == S_OK ? versionInfoString : L"";
}
std::wstring
GetModuleName()
{
WCHAR path[MAX_PATH];
LOG_LAST_ERROR_IF(GetModuleFileName(g_hModule, path, sizeof(path)));
return path;
}
void SetDebugFlags(const std::wstring &debugValue)

View File

@ -49,3 +49,12 @@ DebugPrintf(
LPCSTR szFormat,
...
);
std::wstring
GetProcessIdString();
std::wstring
GetVersionInfoString();
std::wstring
GetModuleName();

View File

@ -6,6 +6,7 @@
#include <system_error>
#include "debugutil.h"
#include "StringHelpers.h"
#define LOCATION_INFO_ENABLED TRUE
@ -26,10 +27,11 @@
#endif
#define OBSERVE_CAUGHT_EXCEPTION() CaughtExceptionHResult(LOCATION_INFO);
#define RETURN_CAUGHT_EXCEPTION() return CaughtExceptionHResult(LOCATION_INFO);
#define RETURN_HR(hr) do { HRESULT __hrRet = hr; if (FAILED(__hrRet)) { LogHResultFailed(LOCATION_INFO, __hrRet); } return __hrRet; } while (0, 0)
#define RETURN_LAST_ERROR() do { return LogLastError(LOCATION_INFO); } while (0, 0)
#define RETURN_IF_FAILED(hr) do { HRESULT __hrRet = hr; if (FAILED(__hrRet)) { LogHResultFailed(LOCATION_INFO, __hrRet); return __hrRet; }} while (0, 0)
#define RETURN_CAUGHT_EXCEPTION() return CaughtExceptionHResult(LOCATION_INFO);
#define RETURN_LAST_ERROR_IF(condition) do { if (condition) { return LogLastError(LOCATION_INFO); }} while (0, 0)
#define RETURN_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { return LogLastError(LOCATION_INFO); }} while (0, 0)
@ -39,6 +41,11 @@
#define FINISHED_LAST_ERROR_IF(condition) do { if (condition) { hr = LogLastError(LOCATION_INFO); goto Finished; }} while (0, 0)
#define FINISHED_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { hr = LogLastError(LOCATION_INFO); goto Finished; }} while (0, 0)
#define THROW_LAST_ERROR() do { ThrowResultException(LogLastError(LOCATION_INFO)); } while (0, 0)
#define THROW_IF_FAILED(hr) do { HRESULT __hrRet = hr; if (FAILED(__hrRet)) { ThrowResultException(LOCATION_INFO, __hrRet); }} while (0, 0)
#define THROW_LAST_ERROR_IF(condition) do { if (condition) { ThrowResultException(LogLastError(LOCATION_INFO)); }} while (0, 0)
#define THROW_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { ThrowResultException(LogLastError(LOCATION_INFO)); }} while (0, 0)
#define THROW_IF_NULL_ALLOC(ptr) Throw_IfNullAlloc(ptr)
#define CATCH_RETURN() catch (...) { RETURN_CAUGHT_EXCEPTION(); }
@ -48,6 +55,22 @@
#define SUCCEEDED_LOG(hr) SUCCEEDED(LOG_IF_FAILED(hr))
#define FAILED_LOG(hr) FAILED(LOG_IF_FAILED(hr))
class ResultException: public std::runtime_error
{
public:
explicit ResultException(HRESULT hr, LOCATION_ARGUMENTS_ONLY) :
runtime_error(format("HRESULT 0x%x returned at " LOCATION_FORMAT, hr, LOCATION_CALL_ONLY)),
m_hr(hr)
{
}
HRESULT GetResult() const { return m_hr; }
private:
HRESULT m_hr;
};
__declspec(noinline) inline VOID ReportUntypedException(LOCATION_ARGUMENTS_ONLY)
{
DebugPrintf(ASPNETCORE_DEBUG_FLAG_ERROR, LOCATION_FORMAT "Unhandled non-standard exception", LOCATION_CALL_ONLY);
@ -75,7 +98,7 @@
__declspec(noinline) inline VOID ReportException(LOCATION_ARGUMENTS std::exception& exception)
{
DebugPrintf(ASPNETCORE_DEBUG_FLAG_ERROR, "Exception : %s caught at" LOCATION_FORMAT, exception.what(), LOCATION_CALL_ONLY);
DebugPrintf(ASPNETCORE_DEBUG_FLAG_ERROR, "Exception '%s' caught at " LOCATION_FORMAT, exception.what(), LOCATION_CALL_ONLY);
}
__declspec(noinline) inline HRESULT LogHResultFailed(LOCATION_ARGUMENTS HRESULT hr)
@ -97,10 +120,10 @@ __declspec(noinline) inline HRESULT CaughtExceptionHResult(LOCATION_ARGUMENTS_ON
{
return E_OUTOFMEMORY;
}
catch (std::system_error& exception)
catch (ResultException& exception)
{
ReportException(LOCATION_CALL exception);
return exception.code().value();
return exception.GetResult();
}
catch (std::exception& exception)
{
@ -114,6 +137,13 @@ __declspec(noinline) inline HRESULT CaughtExceptionHResult(LOCATION_ARGUMENTS_ON
}
}
[[noreturn]]
__declspec(noinline) inline void ThrowResultException(LOCATION_ARGUMENTS HRESULT hr)
{
DebugPrintf(ASPNETCORE_DEBUG_FLAG_ERROR, "Throwing ResultException for HRESULT 0x%x at " LOCATION_FORMAT, hr, LOCATION_CALL_ONLY);
throw ResultException(hr, LOCATION_CALL_ONLY);
}
template <typename PointerT> auto Throw_IfNullAlloc(PointerT pointer)
{
if (pointer == nullptr)

View File

@ -159,7 +159,7 @@ HOSTFXR_UTILITY::ParseHostfxrArguments(
auto pwzArgs = std::unique_ptr<LPWSTR[], LocalFreeDeleter>(CommandLineToArgvW(applicationArguments.c_str(), &argc));
if (!pwzArgs)
{
throw StartupParametersResolutionException(format(L"Unable parse command line argumens '%s' or '%s'", applicationArguments.c_str()));
throw StartupParametersResolutionException(format(L"Unable parse command line arguments '%s' or '%s'", applicationArguments.c_str()));
}
for (int intArgsProcessed = 0; intArgsProcessed < argc; intArgsProcessed++)

View File

@ -28,7 +28,7 @@
#define ASPNETCORE_EVENT_APP_SHUTDOWN_SUCCESSFUL_MSG L"Application '%s' has shutdown."
#define ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP_MSG L"Only one inprocess application is allowed per IIS application pool. Please assign the application '%s' to a different IIS application pool."
#define ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG L"Mixed hosting model is not supported. Application '%s' configured with different hostingModel value '%d' other than the one of running application(s)."
#define ASPNETCORE_EVENT_UNKNOWN_HOSTING_MODEL_ERROR_MSG L"Unknown hosting model '%s'. Please specify either hostingModel=\"inprocess\" or hostingModel=\"outofprocess\" in the web.config file."
#define ASPNETCORE_CONFIGURATION_LOAD_ERROR_MSG L"Configuration load error. %s"
#define ASPNETCORE_EVENT_ADD_APPLICATION_ERROR_MSG L"Failed to start application '%s', ErrorCode '0x%x'."
#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT_MSG L"Application '%s' with physical root '%s' hit unexpected managed background thread exit, ErrorCode = '0x%x'. Last 4KB characters of captured stdout and stderr logs:\r\n%s"
#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_MSG L"Application '%s' with physical root '%s' hit unexpected managed background thread exit, ErrorCode = '0x%x'. Please check the stderr logs for more information."

View File

@ -2,6 +2,7 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
#include "stringu.h"
#include<Windows.h>
HRESULT

View File

@ -0,0 +1,30 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "InProcessOptions.h"
InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSource) :
m_fStdoutLogEnabled(false),
m_fWindowsAuthEnabled(false),
m_fBasicAuthEnabled(false),
m_fAnonymousAuthEnabled(false),
m_dwStartupTimeLimitInMS(INFINITE),
m_dwShutdownTimeLimitInMS(INFINITE)
{
auto const aspNetCoreSection = configurationSource.GetRequiredSection(CS_ASPNETCORE_SECTION);
m_strArguments = aspNetCoreSection->GetString(CS_ASPNETCORE_PROCESS_ARGUMENTS).value_or(CS_ASPNETCORE_PROCESS_ARGUMENTS_DEFAULT);
m_strProcessPath = aspNetCoreSection->GetRequiredString(CS_ASPNETCORE_PROCESS_EXE_PATH);
m_fStdoutLogEnabled = aspNetCoreSection->GetRequiredBool(CS_ASPNETCORE_STDOUT_LOG_ENABLED);
m_struStdoutLogFile = aspNetCoreSection->GetRequiredString(CS_ASPNETCORE_STDOUT_LOG_FILE);
m_fDisableStartUpErrorPage = aspNetCoreSection->GetRequiredBool(CS_ASPNETCORE_DISABLE_START_UP_ERROR_PAGE);
m_environmentVariables = aspNetCoreSection->GetKeyValuePairs(CS_ASPNETCORE_ENVIRONMENT_VARIABLES);
const auto basicAuthSection = configurationSource.GetSection(CS_BASIC_AUTHENTICATION_SECTION);
m_fBasicAuthEnabled = basicAuthSection && basicAuthSection->GetBool(CS_ENABLED).value_or(false);
const auto windowsAuthSection = configurationSource.GetSection(CS_WINDOWS_AUTHENTICATION_SECTION);
m_fWindowsAuthEnabled = windowsAuthSection && windowsAuthSection->GetBool(CS_ENABLED).value_or(false);
const auto anonAuthSection = configurationSource.GetSection(CS_ANONYMOUS_AUTHENTICATION_SECTION);
m_fAnonymousAuthEnabled = anonAuthSection && anonAuthSection->GetBool(CS_ENABLED).value_or(false);
}

View File

@ -0,0 +1,95 @@
// 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"
class InProcessOptions: NonCopyable
{
public:
const std::wstring&
QueryProcessPath() const
{
return m_strProcessPath;
}
const std::wstring&
QueryArguments() const
{
return m_strArguments;
}
bool
QueryStdoutLogEnabled() const
{
return m_fStdoutLogEnabled;
}
const std::wstring&
QueryStdoutLogFile() const
{
return m_struStdoutLogFile;
}
bool
QueryDisableStartUpErrorPage() const
{
return m_fDisableStartUpErrorPage;
}
bool
QueryWindowsAuthEnabled() const
{
return m_fWindowsAuthEnabled;
}
bool
QueryBasicAuthEnabled() const
{
return m_fBasicAuthEnabled;
}
bool
QueryAnonymousAuthEnabled() const
{
return m_fAnonymousAuthEnabled;
}
DWORD
QueryStartupTimeLimitInMS() const
{
return m_dwStartupTimeLimitInMS;
}
DWORD
QueryShutdownTimeLimitInMS() const
{
return m_dwShutdownTimeLimitInMS;
}
const std::vector<std::pair<std::wstring, std::wstring>>&
QueryEnvironmentVariables() const
{
return m_environmentVariables;
}
InProcessOptions(const ConfigurationSource &configurationSource);
private:
std::wstring m_strArguments;
std::wstring m_strProcessPath;
std::wstring m_struStdoutLogFile;
bool m_fStdoutLogEnabled;
bool m_fDisableStartUpErrorPage;
bool m_fWindowsAuthEnabled;
bool m_fBasicAuthEnabled;
bool m_fAnonymousAuthEnabled;
DWORD m_dwStartupTimeLimitInMS;
DWORD m_dwShutdownTimeLimitInMS;
std::vector<std::pair<std::wstring, std::wstring>> m_environmentVariables;
protected:
InProcessOptions() = default;
};

View File

@ -228,6 +228,7 @@
<ClInclude Include="inprocessapplication.h" />
<ClInclude Include="InProcessApplicationBase.h" />
<ClInclude Include="inprocesshandler.h" />
<ClInclude Include="InProcessOptions.h" />
<ClInclude Include="ShuttingDownApplication.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="StartupExceptionApplication.h" />
@ -238,6 +239,7 @@
<ClCompile Include="inprocessapplication.cpp" />
<ClCompile Include="InProcessApplicationBase.cpp" />
<ClCompile Include="inprocesshandler.cpp" />
<ClCompile Include="InProcessOptions.cpp" />
<ClCompile Include="managedexports.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>

View File

@ -13,6 +13,10 @@
#include "resources.h"
#include "exceptions.h"
#include "ShuttingDownApplication.h"
#include "InProcessOptions.h"
#include "EventLog.h"
#include "WebConfigConfigurationSource.h"
#include "ConfigurationLoadException.h"
DECLARE_DEBUG_PRINT_OBJECT("aspnetcorev2_inprocess.dll");
@ -106,13 +110,12 @@ CreateApplication(
return S_OK;
}
REQUESTHANDLER_CONFIG *pConfig = nullptr;
RETURN_IF_FAILED(REQUESTHANDLER_CONFIG::CreateRequestHandlerConfig(pServer, pHttpApplication, &pConfig));
std::unique_ptr<REQUESTHANDLER_CONFIG> pRequestHandlerConfig(pConfig);
const WebConfigConfigurationSource configurationSource(pServer->GetAdminManager(), *pHttpApplication);
auto pConfig = std::make_unique<InProcessOptions>(configurationSource);
BOOL disableStartupPage = pConfig->QueryDisableStartUpErrorPage();
auto pApplication = std::make_unique<IN_PROCESS_APPLICATION>(*pServer, *pHttpApplication, std::move(pRequestHandlerConfig), pParameters, nParameters);
auto pApplication = std::make_unique<IN_PROCESS_APPLICATION>(*pServer, *pHttpApplication, std::move(pConfig), pParameters, nParameters);
// never create two inprocess applications in one process
g_fInProcessApplicationCreated = true;
@ -130,6 +133,15 @@ CreateApplication(
*ppApplication = pApplication.release();
}
}
catch(ConfigurationLoadException &ex)
{
EventLog::Error(
ASPNETCORE_CONFIGURATION_LOAD_ERROR,
ASPNETCORE_CONFIGURATION_LOAD_ERROR_MSG,
ex.get_message().c_str());
RETURN_HR(E_FAIL);
}
CATCH_RETURN();
return S_OK;

View File

@ -19,7 +19,7 @@ IN_PROCESS_APPLICATION* IN_PROCESS_APPLICATION::s_Application = NULL;
IN_PROCESS_APPLICATION::IN_PROCESS_APPLICATION(
IHttpServer& pHttpServer,
IHttpApplication& pApplication,
std::unique_ptr<REQUESTHANDLER_CONFIG> pConfig,
std::unique_ptr<InProcessOptions> pConfig,
APPLICATION_PARAMETER *pParameters,
DWORD nParameters) :
InProcessApplicationBase(pHttpServer, pApplication),
@ -108,14 +108,14 @@ Finished:
EventLog::Warn(
ASPNETCORE_EVENT_GRACEFUL_SHUTDOWN_FAILURE,
ASPNETCORE_EVENT_APP_SHUTDOWN_FAILURE_MSG,
m_pConfig->QueryConfigPath()->QueryStr());
QueryConfigPath().c_str());
}
else
{
EventLog::Info(
ASPNETCORE_EVENT_APP_SHUTDOWN_SUCCESSFUL,
ASPNETCORE_EVENT_APP_SHUTDOWN_SUCCESSFUL_MSG,
m_pConfig->QueryConfigPath()->QueryStr());
QueryConfigPath().c_str());
}
InProcessApplicationBase::StopInternal(fServerInitiated);
@ -212,7 +212,7 @@ IN_PROCESS_APPLICATION::SetCallbackHandles(
EventLog::Info(
ASPNETCORE_EVENT_INPROCESS_START_SUCCESS,
ASPNETCORE_EVENT_INPROCESS_START_SUCCESS_MSG,
m_pConfig->QueryApplicationPhysicalPath()->QueryStr());
QueryApplicationPhysicalPath().c_str());
SetEvent(m_pInitalizeEvent);
m_fInitialized = TRUE;
}
@ -334,8 +334,8 @@ Finished:
EventLog::Error(
ASPNETCORE_EVENT_LOAD_CLR_FALIURE,
ASPNETCORE_EVENT_LOAD_CLR_FALIURE_MSG,
m_pConfig->QueryApplicationPath()->QueryStr(),
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
QueryApplicationId().c_str(),
QueryApplicationPhysicalPath().c_str(),
hr);
}
DereferenceApplication();
@ -365,11 +365,21 @@ IN_PROCESS_APPLICATION::SetEnvironementVariablesOnWorkerProcess(
VOID
)
{
HRESULT hr = S_OK;
auto variables = m_pConfig->QueryEnvironmentVariables();
auto inputTable = std::unique_ptr<ENVIRONMENT_VAR_HASH, ENVIRONMENT_VAR_HASH_DELETER>(new ENVIRONMENT_VAR_HASH());
RETURN_IF_FAILED(inputTable->Initialize(37 /*prime*/));
// Copy environment variables to old style hash table
for (auto & variable : variables)
{
auto pNewEntry = std::unique_ptr<ENVIRONMENT_VAR_ENTRY, ENVIRONMENT_VAR_ENTRY_DELETER>(new ENVIRONMENT_VAR_ENTRY());
RETURN_IF_FAILED(pNewEntry->Initialize((variable.first + L"=").c_str(), variable.second.c_str()));
RETURN_IF_FAILED(inputTable->InsertRecord(pNewEntry.get()));
}
ENVIRONMENT_VAR_HASH* pHashTable = NULL;
std::unique_ptr<ENVIRONMENT_VAR_HASH, ENVIRONMENT_VAR_HASH_DELETER> table;
RETURN_IF_FAILED(hr = ENVIRONMENT_VAR_HELPERS::InitEnvironmentVariablesTable(
m_pConfig->QueryEnvironmentVariables(),
RETURN_IF_FAILED(ENVIRONMENT_VAR_HELPERS::InitEnvironmentVariablesTable(
inputTable.get(),
m_pConfig->QueryWindowsAuthEnabled(),
m_pConfig->QueryBasicAuthEnabled(),
m_pConfig->QueryAnonymousAuthEnabled(),
@ -377,11 +387,13 @@ IN_PROCESS_APPLICATION::SetEnvironementVariablesOnWorkerProcess(
table.reset(pHashTable);
HRESULT hr = S_OK;
table->Apply(ENVIRONMENT_VAR_HELPERS::AppendEnvironmentVariables, &hr);
RETURN_IF_FAILED(hr);
table->Apply(ENVIRONMENT_VAR_HELPERS::SetEnvironmentVariables, &hr);
RETURN_IF_FAILED(hr);
return S_OK;
}
@ -422,9 +434,9 @@ IN_PROCESS_APPLICATION::ExecuteApplication(
FINISHED_IF_FAILED(hr = HOSTFXR_OPTIONS::Create(
m_struExeLocation.QueryStr(),
m_pConfig->QueryProcessPath()->QueryStr(),
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
m_pConfig->QueryArguments()->QueryStr(),
m_pConfig->QueryProcessPath().c_str(),
QueryApplicationPhysicalPath().c_str(),
m_pConfig->QueryArguments().c_str(),
hostFxrOptions
));
hostFxrOptions->GetArguments(hostfxrArgc, hostfxrArgv);
@ -438,8 +450,8 @@ IN_PROCESS_APPLICATION::ExecuteApplication(
FINISHED_IF_FAILED(hr = LoggingHelpers::CreateLoggingProvider(
m_pConfig->QueryStdoutLogEnabled(),
!m_pHttpServer.IsCommandLineLaunch(),
m_pConfig->QueryStdoutLogFile()->QueryStr(),
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
m_pConfig->QueryStdoutLogFile().c_str(),
QueryApplicationPhysicalPath().c_str(),
m_pLoggerProvider));
LOG_IF_FAILED(m_pLoggerProvider->Start());
@ -502,8 +514,8 @@ IN_PROCESS_APPLICATION::LogErrorsOnMainExit(
EventLog::Error(
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT,
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT_MSG,
m_pConfig->QueryApplicationPath()->QueryStr(),
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
QueryApplicationId().c_str(),
QueryApplicationPhysicalPath().c_str(),
hr,
struStdMsg.QueryStr());
}
@ -513,8 +525,8 @@ IN_PROCESS_APPLICATION::LogErrorsOnMainExit(
EventLog::Error(
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT,
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_MSG,
m_pConfig->QueryApplicationPath()->QueryStr(),
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
QueryApplicationId().c_str(),
QueryApplicationPhysicalPath().c_str(),
hr);
}
}

View File

@ -4,8 +4,8 @@
#pragma once
#include "InProcessApplicationBase.h"
#include "requesthandler_config.h"
#include "IOutputManager.h"
#include "InProcessOptions.h"
class IN_PROCESS_HANDLER;
typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_REQUEST_HANDLER) (IN_PROCESS_HANDLER* pInProcessHandler, void* pvRequestHandlerContext);
@ -18,7 +18,7 @@ public:
IN_PROCESS_APPLICATION(
IHttpServer& pHttpServer,
IHttpApplication& pApplication,
std::unique_ptr<REQUESTHANDLER_CONFIG> pConfig,
std::unique_ptr<InProcessOptions> pConfig,
APPLICATION_PARAMETER *pParameters,
DWORD nParameters);
@ -97,10 +97,10 @@ public:
return m_struExeLocation.QueryStr();
}
REQUESTHANDLER_CONFIG*
QueryConfig()
const InProcessOptions&
QueryConfig() const
{
return m_pConfig.get();
return *m_pConfig.get();
}
bool
@ -146,7 +146,7 @@ private:
volatile BOOL m_fShutdownCalledFromManaged;
BOOL m_fInitialized;
MANAGED_APPLICATION_STATUS m_status;
std::unique_ptr<REQUESTHANDLER_CONFIG> m_pConfig;
std::unique_ptr<InProcessOptions> m_pConfig;
static IN_PROCESS_APPLICATION* s_Application;

View File

@ -9,7 +9,7 @@
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#define FileDescription "IIS ASP.NET Core Module Request Handler. Commit: " CommitHash
#define FileDescription "IIS ASP.NET Core Module V2 Request Handler. Commit: " CommitHash
/////////////////////////////////////////////////////////////////////////////
//

View File

@ -187,11 +187,11 @@ http_get_application_properties(
auto pConfiguration = pInProcessApplication->QueryConfig();
pIISCofigurationData->pInProcessApplication = pInProcessApplication;
pIISCofigurationData->pwzFullApplicationPath = SysAllocString(pConfiguration->QueryApplicationPhysicalPath()->QueryStr());
pIISCofigurationData->pwzVirtualApplicationPath = SysAllocString(pConfiguration->QueryApplicationVirtualPath()->QueryStr());
pIISCofigurationData->fWindowsAuthEnabled = pConfiguration->QueryWindowsAuthEnabled();
pIISCofigurationData->fBasicAuthEnabled = pConfiguration->QueryBasicAuthEnabled();
pIISCofigurationData->fAnonymousAuthEnable = pConfiguration->QueryAnonymousAuthEnabled();
pIISCofigurationData->pwzFullApplicationPath = SysAllocString(pInProcessApplication->QueryApplicationPhysicalPath().c_str());
pIISCofigurationData->pwzVirtualApplicationPath = SysAllocString(pInProcessApplication->QueryApplicationVirtualPath().c_str());
pIISCofigurationData->fWindowsAuthEnabled = pConfiguration.QueryWindowsAuthEnabled();
pIISCofigurationData->fBasicAuthEnabled = pConfiguration.QueryBasicAuthEnabled();
pIISCofigurationData->fAnonymousAuthEnable = pConfiguration.QueryAnonymousAuthEnabled();
return S_OK;
}

View File

@ -9,7 +9,7 @@
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#define FileDescription "IIS ASP.NET Core Module Request Handler. Commit: " CommitHash
#define FileDescription "IIS ASP.NET Core Module V2 Request Handler. Commit: " CommitHash
/////////////////////////////////////////////////////////////////////////////
//

View File

@ -12,7 +12,7 @@ class AppOfflineTrackingApplication: public APPLICATION
{
public:
AppOfflineTrackingApplication(const IHttpApplication& application)
: APPLICATION(),
: APPLICATION(application),
m_applicationPath(application.GetApplicationPhysicalPath()),
m_fileWatcher(nullptr),
m_fAppOfflineProcessed(false)

View File

@ -33,13 +33,13 @@ public:
{
HRESULT hr = S_OK;
if (FAILED(hr = _strName.Copy(pszName)) ||
FAILED(hr = _strValue.Copy(pszValue)))
FAILED(hr = _strValue.Copy(pszValue)))
{
}
return hr;
return hr;
}
VOID
VOID
Reference() const
{
InterlockedIncrement(&_cRefs);
@ -139,3 +139,11 @@ struct ENVIRONMENT_VAR_HASH_DELETER
delete hashTable;
}
};
struct ENVIRONMENT_VAR_ENTRY_DELETER
{
void operator ()(ENVIRONMENT_VAR_ENTRY* entry) const
{
entry->Dereference();
}
};

View File

@ -8,11 +8,11 @@ using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(IISTestSiteCollection.Name)]
public class EnvironmentVariableTests
public class EnvironmentVariableTests: FixtureLoggedTest
{
private readonly IISTestSiteFixture _fixture;
public EnvironmentVariableTests(IISTestSiteFixture fixture)
public EnvironmentVariableTests(IISTestSiteFixture fixture): base(fixture)
{
_fixture = fixture;
}

View File

@ -1,7 +1,6 @@
// 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;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
using Microsoft.AspNetCore.Testing.xunit;

View File

@ -200,8 +200,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
var logContents = streamReader.ReadToEnd();
Assert.Contains("[aspnetcorev2.dll]", logContents);
Assert.Contains("[aspnetcorev2_inprocess.dll]", logContents);
Assert.Contains("Description: IIS AspNetCore Module V2. Commit:", logContents);
Assert.Contains("Description: IIS ASP.NET Core Module Request Handler. Commit:", logContents);
Assert.Contains("Description: IIS ASP.NET Core Module V2. Commit:", logContents);
Assert.Contains("Description: IIS ASP.NET Core Module V2 Request Handler. Commit:", logContents);
}
}
}

View File

@ -1,17 +1,24 @@
// 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 System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
using Xunit.Sdk;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
public class EventLogHelpers
{
private static readonly Regex EventLogRegex = new Regex("Event Log: (?<EventLogMessage>.+?)End Event Log Message.", RegexOptions.Singleline | RegexOptions.Compiled);
public static void VerifyEventLogEvent(IISDeploymentResult deploymentResult, ITestSink testSink, string expectedRegexMatchString)
{
Assert.True(deploymentResult.HostProcess.HasExited);
@ -36,6 +43,43 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
Assert.True(count > 0, $"'{expectedRegexMatchString}' didn't match any event log messaged");
Assert.True(count < 2, $"'{expectedRegexMatchString}' matched more then one event log message");
var eventLog = new EventLog("Application");
// Perf: only get the last 20 events from the event log.
// Eventlog is already sorted based on time of event in ascending time.
// Add results in reverse order.
var expectedRegexEventLog = new Regex(expectedRegexMatchString);
var processIdString = $"Process Id: {deploymentResult.HostProcess.Id}.";
for (var i = eventLog.Entries.Count - 1; i >= eventLog.Entries.Count - 20; i--)
{
var eventLogEntry = eventLog.Entries[i];
if (eventLogEntry.ReplacementStrings == null ||
eventLogEntry.ReplacementStrings.Length < 3)
{
continue;
}
// ReplacementStings == EventData collection in EventLog
// This is unaffected if event providers are not registered correctly
if (eventLogEntry.Source == AncmVersionToMatch(deploymentResult) &&
processIdString == eventLogEntry.ReplacementStrings[1] &&
expectedRegex.IsMatch(eventLogEntry.ReplacementStrings[0]))
{
return;
}
}
Assert.True(false, $"'{expectedRegexMatchString}' didn't match any event log messaged.");
}
private static string AncmVersionToMatch(IISDeploymentResult deploymentResult)
{
return "IIS " +
(deploymentResult.DeploymentParameters.ServerType == ServerType.IISExpress ? "Express " : "") +
"AspNetCore Module" +
(deploymentResult.DeploymentParameters.AncmVersion == AncmVersion.AspNetCoreModuleV2 ? " V2" : "");
}
}
}

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Xml.Linq;
@ -133,5 +134,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
await verificationAction();
}
}
public static IEnumerable<object[]> ToTheoryData<T>(this Dictionary<string, T> dictionary)
{
return dictionary.Keys.Select(k => new[] { k });
}
}
}

View File

@ -5,6 +5,7 @@
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "InProcessOptions.h"
class MockProperty : public IAppHostProperty
{
@ -176,20 +177,14 @@ public:
MOCK_METHOD0(GetModuleContextContainer, IHttpModuleContextContainer* ());
};
class MockRequestHandlerConfig : public REQUESTHANDLER_CONFIG
class MockInProcessOptions : public InProcessOptions
{
public:
static
MockRequestHandlerConfig*
MockInProcessOptions*
CreateConfig()
{
return new MockRequestHandlerConfig;
}
private:
MockRequestHandlerConfig()
{
return new MockInProcessOptions;
}
};

View File

@ -24,7 +24,13 @@ namespace InprocessTests
ON_CALL(application, GetApplicationPhysicalPath())
.WillByDefault(testing::Return(L"Some path"));
auto requestHandlerConfig = std::unique_ptr<REQUESTHANDLER_CONFIG>(MockRequestHandlerConfig::CreateConfig());
ON_CALL(application, GetAppConfigPath())
.WillByDefault(testing::Return(L""));
ON_CALL(application, GetApplicationId())
.WillByDefault(testing::Return(L""));
auto requestHandlerConfig = std::unique_ptr<InProcessOptions>(MockInProcessOptions::CreateConfig());
std::wstring exePath(L"hello");
@ -36,4 +42,44 @@ namespace InprocessTests
ASSERT_STREQ(app->QueryExeLocation(), L"hello");
}
TEST(InProcessTest, GeneratesVirtualPath)
{
MockHttpServer server;
NiceMock<MockHttpApplication> application;
ON_CALL(application, GetApplicationPhysicalPath())
.WillByDefault(testing::Return(L"Some path"));
ON_CALL(application, GetAppConfigPath())
.WillByDefault(testing::Return(L"SECTION1/SECTION2/SECTION3/SECTION4/SECTION5"));
ON_CALL(application, GetApplicationId())
.WillByDefault(testing::Return(L""));
auto requestHandlerConfig = std::unique_ptr<InProcessOptions>(MockInProcessOptions::CreateConfig());
IN_PROCESS_APPLICATION *app = new IN_PROCESS_APPLICATION(server, application, std::move(requestHandlerConfig), nullptr, 0);
ASSERT_STREQ(app->QueryApplicationVirtualPath().c_str(), L"/SECTION5");
}
TEST(InProcessTest, GeneratesVirtualPathForDefaultApp)
{
MockHttpServer server;
NiceMock<MockHttpApplication> application;
ON_CALL(application, GetApplicationPhysicalPath())
.WillByDefault(testing::Return(L"Some path"));
ON_CALL(application, GetAppConfigPath())
.WillByDefault(testing::Return(L"SECTION1/SECTION2/SECTION3/SECTION4"));
ON_CALL(application, GetApplicationId())
.WillByDefault(testing::Return(L""));
auto requestHandlerConfig = std::unique_ptr<InProcessOptions>(MockInProcessOptions::CreateConfig());
IN_PROCESS_APPLICATION *app = new IN_PROCESS_APPLICATION(server, application, std::move(requestHandlerConfig), nullptr, 0);
ASSERT_STREQ(app->QueryApplicationVirtualPath().c_str(), L"/");
}
}

View File

@ -2,11 +2,13 @@
// 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 System.IO;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
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.IIS;
@ -185,5 +187,45 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
EventLogHelpers.VerifyEventLogEvent(deploymentResult, TestSink, "Unknown hosting model 'bogus'. Please specify either hostingModel=\"inprocess\" or hostingModel=\"outofprocess\" in the web.config file.");
}
private static Dictionary<string, (string, Action<XElement>)> InvalidConfigTransformations = InitInvalidConfigTransformations();
public static IEnumerable<object[]> InvalidConfigTransformationsScenarios => InvalidConfigTransformations.ToTheoryData();
[ConditionalTheory]
[MemberData(nameof(InvalidConfigTransformationsScenarios))]
public async Task StartsWithWebConfigVariationsPortable(string scenario)
{
var (expectedError, action) = InvalidConfigTransformations[scenario];
var iisDeploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true);
iisDeploymentParameters.WebConfigActionList.Add((element, _) => action(element));
var deploymentResult = await DeployAsync(iisDeploymentParameters);
var result = await deploymentResult.HttpClient.GetAsync("/HelloWorld");
Assert.Equal(HttpStatusCode.InternalServerError, result.StatusCode);
StopServer();
EventLogHelpers.VerifyEventLogEvent(deploymentResult, TestSink, "Configuration load error. " + expectedError);
}
public static Dictionary<string, (string, Action<XElement>)> InitInvalidConfigTransformations()
{
var dictionary = new Dictionary<string, (string, Action<XElement>)>();
dictionary.Add("Empty process path",
(
"Attribute 'processPath' is required.",
element => element.Descendants("aspNetCore").Single().SetAttributeValue("processPath", "")
));
dictionary.Add("Unknown hostingModel",
(
"Unknown hosting model 'asdf'.",
element => element.Descendants("aspNetCore").Single().SetAttributeValue("hostingModel", "asdf")
));
dictionary.Add("environmentVariables with add",
(
"Unable to get required configuration section 'system.webServer/aspNetCore'. Possible reason is web.config authoring error.",
element => element.Descendants("aspNetCore").Single().GetOrAdd("environmentVariables").GetOrAdd("add")
));
return dictionary;
}
}
}

View File

@ -0,0 +1,7 @@
<Project>
<Import Project="..\Directory.Build.props" />
<ItemGroup>
<PackageReference Remove="Internal.AspNetCore.Sdk" />
</ItemGroup>
</Project>