Merge pull request #1377 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-09-07 09:00:21 -07:00 committed by GitHub
commit ca1c0ea354
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 480 additions and 346 deletions

View File

@ -10,7 +10,7 @@
class AppOfflineApplication: public PollingAppOfflineApplication class AppOfflineApplication: public PollingAppOfflineApplication
{ {
public: public:
AppOfflineApplication(IHttpApplication& pApplication) AppOfflineApplication(const IHttpApplication& pApplication)
: PollingAppOfflineApplication(pApplication, PollingAppOfflineApplicationMode::StopWhenRemoved) : PollingAppOfflineApplication(pApplication, PollingAppOfflineApplicationMode::StopWhenRemoved)
{ {
CheckAppOffline(); CheckAppOffline();

View File

@ -13,7 +13,7 @@ typedef
HRESULT HRESULT
(WINAPI * PFN_ASPNETCORE_CREATE_APPLICATION)( (WINAPI * PFN_ASPNETCORE_CREATE_APPLICATION)(
_In_ IHttpServer *pServer, _In_ IHttpServer *pServer,
_In_ IHttpApplication *pHttpApplication, _In_ const IHttpApplication * pHttpApplication,
_In_ APPLICATION_PARAMETER *pParameters, _In_ APPLICATION_PARAMETER *pParameters,
_In_ DWORD nParameters, _In_ DWORD nParameters,
_Out_ IAPPLICATION **pApplication _Out_ IAPPLICATION **pApplication
@ -31,7 +31,7 @@ public:
HRESULT Execute( HRESULT Execute(
_In_ IHttpServer *pServer, _In_ IHttpServer *pServer,
_In_ IHttpApplication *pHttpApplication, _In_ const IHttpApplication *pHttpApplication,
_Outptr_ IAPPLICATION **pApplication) const noexcept _Outptr_ IAPPLICATION **pApplication) const noexcept
{ {
std::array<APPLICATION_PARAMETER, 1> parameters { std::array<APPLICATION_PARAMETER, 1> parameters {

View File

@ -15,6 +15,7 @@
#include "ConfigurationLoadException.h" #include "ConfigurationLoadException.h"
#include "WebConfigConfigurationSource.h" #include "WebConfigConfigurationSource.h"
#include "ModuleHelpers.h" #include "ModuleHelpers.h"
#include "BaseOutputManager.h"
const PCWSTR HandlerResolver::s_pwzAspnetcoreInProcessRequestHandlerName = L"aspnetcorev2_inprocess.dll"; const PCWSTR HandlerResolver::s_pwzAspnetcoreInProcessRequestHandlerName = L"aspnetcorev2_inprocess.dll";
const PCWSTR HandlerResolver::s_pwzAspnetcoreOutOfProcessRequestHandlerName = L"aspnetcorev2_outofprocess.dll"; const PCWSTR HandlerResolver::s_pwzAspnetcoreOutOfProcessRequestHandlerName = L"aspnetcorev2_outofprocess.dll";
@ -54,7 +55,7 @@ HandlerResolver::LoadRequestHandlerAssembly(const IHttpApplication &pApplication
if (pConfiguration.QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS) if (pConfiguration.QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS)
{ {
std::unique_ptr<HOSTFXR_OPTIONS> options; std::unique_ptr<HOSTFXR_OPTIONS> options;
std::unique_ptr<IOutputManager> outputManager; std::unique_ptr<BaseOutputManager> outputManager;
RETURN_IF_FAILED(HOSTFXR_OPTIONS::Create( RETURN_IF_FAILED(HOSTFXR_OPTIONS::Create(
L"", L"",
@ -72,28 +73,21 @@ HandlerResolver::LoadRequestHandlerAssembly(const IHttpApplication &pApplication
pApplication.GetApplicationPhysicalPath(), pApplication.GetApplicationPhysicalPath(),
outputManager)); outputManager));
outputManager->Start(); outputManager->TryStartRedirection();
hr = FindNativeAssemblyFromHostfxr(*options.get(), pstrHandlerDllName, handlerDllPath); hr = FindNativeAssemblyFromHostfxr(*options.get(), pstrHandlerDllName, handlerDllPath);
outputManager->Stop();
outputManager->TryStopRedirection();
if (FAILED(hr) && m_hHostFxrDll != nullptr) if (FAILED(hr) && m_hHostFxrDll != nullptr)
{ {
STRA content; auto output = outputManager->GetStdOutContent();
STRU struStdMsg;
outputManager->GetStdOutContent(&content);
if (content.QueryCCH() > 0)
{
struStdMsg.CopyA(content.QueryStr());
}
EventLog::Error( EventLog::Error(
ASPNETCORE_EVENT_GENERAL_ERROR, ASPNETCORE_EVENT_GENERAL_ERROR,
ASPNETCORE_EVENT_INPROCESS_RH_ERROR_MSG, ASPNETCORE_EVENT_INPROCESS_RH_ERROR_MSG,
handlerDllPath.empty()? s_pwzAspnetcoreInProcessRequestHandlerName : handlerDllPath.c_str(), handlerDllPath.empty()? s_pwzAspnetcoreInProcessRequestHandlerName : handlerDllPath.c_str(),
struStdMsg.QueryStr()); output.c_str());
} }
} }
else else

View File

@ -8,15 +8,20 @@
#include "HandleWrapper.h" #include "HandleWrapper.h"
#include "exceptions.h" #include "exceptions.h"
APPLICATION_STATUS PollingAppOfflineApplication::QueryStatus() HRESULT PollingAppOfflineApplication::TryCreateHandler(_In_ IHttpContext* pHttpContext, _Outptr_result_maybenull_ IREQUEST_HANDLER** pRequestHandler)
{ {
CheckAppOffline(); CheckAppOffline();
return APPLICATION::QueryStatus(); return LOG_IF_FAILED(APPLICATION::TryCreateHandler(pHttpContext, pRequestHandler));
} }
void void
PollingAppOfflineApplication::CheckAppOffline() PollingAppOfflineApplication::CheckAppOffline()
{ {
if (m_fStopCalled)
{
return;
}
const auto ulCurrentTime = GetTickCount64(); const auto ulCurrentTime = GetTickCount64();
// //
// we only care about app offline presented. If not, it means the application has started // we only care about app offline presented. If not, it means the application has started

View File

@ -14,7 +14,7 @@ enum PollingAppOfflineApplicationMode
class PollingAppOfflineApplication: public APPLICATION class PollingAppOfflineApplication: public APPLICATION
{ {
public: public:
PollingAppOfflineApplication(IHttpApplication& pApplication, PollingAppOfflineApplicationMode mode) PollingAppOfflineApplication(const IHttpApplication& pApplication, PollingAppOfflineApplicationMode mode)
: APPLICATION(pApplication), : APPLICATION(pApplication),
m_ulLastCheckTime(0), m_ulLastCheckTime(0),
m_appOfflineLocation(GetAppOfflineLocation(pApplication)), m_appOfflineLocation(GetAppOfflineLocation(pApplication)),
@ -23,8 +23,12 @@ public:
{ {
InitializeSRWLock(&m_statusLock); InitializeSRWLock(&m_statusLock);
} }
HRESULT
TryCreateHandler(
_In_ IHttpContext *pHttpContext,
_Outptr_result_maybenull_ IREQUEST_HANDLER **pRequestHandler) override;
APPLICATION_STATUS QueryStatus() override;
void CheckAppOffline(); void CheckAppOffline();
virtual HRESULT OnAppOfflineFound() = 0; virtual HRESULT OnAppOfflineFound() = 0;
void StopInternal(bool fServerInitiated) override { UNREFERENCED_PARAMETER(fServerInitiated); } void StopInternal(bool fServerInitiated) override { UNREFERENCED_PARAMETER(fServerInitiated); }

View File

@ -9,7 +9,7 @@
class ServerErrorApplication : public PollingAppOfflineApplication class ServerErrorApplication : public PollingAppOfflineApplication
{ {
public: public:
ServerErrorApplication(IHttpApplication& pApplication, HRESULT hr) ServerErrorApplication(const IHttpApplication& pApplication, HRESULT hr)
: m_HR(hr), : m_HR(hr),
PollingAppOfflineApplication(pApplication, PollingAppOfflineApplicationMode::StopWhenAdded) PollingAppOfflineApplication(pApplication, PollingAppOfflineApplicationMode::StopWhenAdded)
{ {

View File

@ -14,52 +14,77 @@
#include "AppOfflineApplication.h" #include "AppOfflineApplication.h"
HRESULT HRESULT
APPLICATION_INFO::GetOrCreateApplication( APPLICATION_INFO::CreateHandler(
IHttpContext& pHttpContext, IHttpContext& pHttpContext,
std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER>& pApplication std::unique_ptr<IREQUEST_HANDLER, IREQUEST_HANDLER_DELETER>& pHandler
) )
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
SRWExclusiveLock lock(m_applicationLock);
auto& httpApplication = *pHttpContext.GetApplication();
if (m_pApplication != nullptr)
{ {
if (m_pApplication->QueryStatus() == RECYCLED) SRWSharedLock lock(m_applicationLock);
{
LOG_INFO(L"Application went offline"); RETURN_IF_FAILED(hr = TryCreateHandler(pHttpContext, pHandler));
// Call to wait for application to complete stopping if (hr == S_OK)
m_pApplication->Stop(/* fServerInitiated */ false);
m_pApplication = nullptr;
m_pApplicationFactory = nullptr;
}
else
{ {
// another thread created the application return S_OK;
FINISHED(S_OK); }
}
{
SRWExclusiveLock lock(m_applicationLock);
// check if other thread created application
RETURN_IF_FAILED(hr = TryCreateHandler(pHttpContext, pHandler));
// In some cases (adding and removing app_offline quickly) application might start and stop immediately
// so retry until we get valid handler or error
while (hr != S_OK)
{
// At this point application is either null or shutdown and is returning S_FALSE
if (m_pApplication != nullptr)
{
LOG_INFO(L"Application went offline");
// Call to wait for application to complete stopping
m_pApplication->Stop(/* fServerInitiated */ false);
m_pApplication = nullptr;
m_pApplicationFactory = nullptr;
}
RETURN_IF_FAILED(CreateApplication(*pHttpContext.GetApplication()));
RETURN_IF_FAILED(hr = TryCreateHandler(pHttpContext, pHandler));
} }
} }
if (AppOfflineApplication::ShouldBeStarted(httpApplication)) return S_OK;
}
HRESULT
APPLICATION_INFO::CreateApplication(const IHttpApplication& pHttpApplication)
{
HRESULT hr = S_OK;
if (AppOfflineApplication::ShouldBeStarted(pHttpApplication))
{ {
LOG_INFO(L"Detected app_offline file, creating polling application"); LOG_INFO(L"Detected app_offline file, creating polling application");
#pragma warning( push ) #pragma warning( push )
#pragma warning ( disable : 26409 ) // Disable "Avoid using new", using custom deleter here #pragma warning ( disable : 26409 ) // Disable "Avoid using new", using custom deleter here
m_pApplication.reset(new AppOfflineApplication(httpApplication)); m_pApplication.reset(new AppOfflineApplication(pHttpApplication));
#pragma warning( pop ) #pragma warning( pop )
} }
else else
{ {
FINISHED_IF_FAILED(m_handlerResolver.GetApplicationFactory(httpApplication, m_pApplicationFactory)); FINISHED_IF_FAILED(m_handlerResolver.GetApplicationFactory(pHttpApplication, m_pApplicationFactory));
LOG_INFO(L"Creating handler application"); LOG_INFO(L"Creating handler application");
IAPPLICATION * newApplication; IAPPLICATION * newApplication;
FINISHED_IF_FAILED(m_pApplicationFactory->Execute( FINISHED_IF_FAILED(m_pApplicationFactory->Execute(
&m_pServer, &m_pServer,
&httpApplication, &pHttpApplication,
&newApplication)); &newApplication));
m_pApplication.reset(newApplication); m_pApplication.reset(newApplication);
@ -67,29 +92,44 @@ APPLICATION_INFO::GetOrCreateApplication(
Finished: Finished:
if (FAILED(hr)) if (m_pApplication == nullptr || FAILED(hr))
{ {
// Log the failure and update application info to not try again // Log the failure and update application info to not try again
EventLog::Error( EventLog::Error(
ASPNETCORE_EVENT_ADD_APPLICATION_ERROR, ASPNETCORE_EVENT_ADD_APPLICATION_ERROR,
ASPNETCORE_EVENT_ADD_APPLICATION_ERROR_MSG, ASPNETCORE_EVENT_ADD_APPLICATION_ERROR_MSG,
httpApplication.GetApplicationId(), pHttpApplication.GetApplicationId(),
hr); hr);
#pragma warning( push ) #pragma warning( push )
#pragma warning ( disable : 26409 ) // Disable "Avoid using new", using custom deleter here #pragma warning ( disable : 26409 ) // Disable "Avoid using new", using custom deleter here
m_pApplication.reset(new ServerErrorApplication(httpApplication, hr)); m_pApplication.reset(new ServerErrorApplication(pHttpApplication, hr));
#pragma warning( pop ) #pragma warning( pop )
} }
if (m_pApplication)
{
pApplication = ReferenceApplication(m_pApplication.get());
}
return hr; return hr;
} }
HRESULT
APPLICATION_INFO::TryCreateHandler(
IHttpContext& pHttpContext,
std::unique_ptr<IREQUEST_HANDLER, IREQUEST_HANDLER_DELETER>& pHandler)
{
if (m_pApplication != nullptr)
{
IREQUEST_HANDLER * newHandler;
const auto result = m_pApplication->TryCreateHandler(&pHttpContext, &newHandler);
RETURN_IF_FAILED(result);
if (result == S_OK)
{
pHandler.reset(newHandler);
// another thread created the application
return S_OK;
}
}
return S_FALSE;
}
VOID VOID
APPLICATION_INFO::ShutDownApplication(bool fServerInitiated) APPLICATION_INFO::ShutDownApplication(bool fServerInitiated)

View File

@ -32,13 +32,13 @@ public:
~APPLICATION_INFO() = default; ~APPLICATION_INFO() = default;
const std::wstring& const std::wstring&
QueryApplicationInfoKey() const noexcept QueryApplicationInfoKey() noexcept
{ {
return m_strInfoKey; return m_strInfoKey;
} }
const std::wstring& const std::wstring&
QueryConfigPath() const noexcept QueryConfigPath() noexcept
{ {
return m_strConfigPath; return m_strConfigPath;
} }
@ -47,11 +47,10 @@ public:
ShutDownApplication(bool fServerInitiated); ShutDownApplication(bool fServerInitiated);
HRESULT HRESULT
GetOrCreateApplication( CreateHandler(
IHttpContext& pHttpContext, IHttpContext& pHttpContext,
std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER>& pApplication std::unique_ptr<IREQUEST_HANDLER, IREQUEST_HANDLER_DELETER>& pHandler);
);
bool ConfigurationPathApplies(const std::wstring& path) bool ConfigurationPathApplies(const std::wstring& path)
{ {
// We need to check that the last character of the config path // We need to check that the last character of the config path
@ -68,6 +67,15 @@ public:
} }
private: private:
HRESULT
TryCreateHandler(
IHttpContext& pHttpContext,
std::unique_ptr<IREQUEST_HANDLER, IREQUEST_HANDLER_DELETER>& pHandler);
HRESULT
CreateApplication(const IHttpApplication& pHttpApplication);
IHttpServer &m_pServer; IHttpServer &m_pServer;
HandlerResolver &m_handlerResolver; HandlerResolver &m_handlerResolver;

View File

@ -101,7 +101,7 @@ HRESULT
{ {
g_hEventLog = RegisterEventSource(nullptr, ASPNETCORE_EVENT_PROVIDER); g_hEventLog = RegisterEventSource(nullptr, ASPNETCORE_EVENT_PROVIDER);
} }
// check whether the feature is disabled due to security reason // check whether the feature is disabled due to security reason
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\IIS Extensions\\IIS AspNetCore Module V2\\Parameters", L"SOFTWARE\\Microsoft\\IIS Extensions\\IIS AspNetCore Module V2\\Parameters",

View File

@ -87,13 +87,7 @@ ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler(
*pHttpContext, *pHttpContext,
m_pApplicationInfo)); m_pApplicationInfo));
std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER> pApplication; FINISHED_IF_FAILED(m_pApplicationInfo->CreateHandler(*pHttpContext, m_pHandler));
FINISHED_IF_FAILED(m_pApplicationInfo->GetOrCreateApplication(*pHttpContext, pApplication));
IREQUEST_HANDLER* pHandler;
// Create RequestHandler and process the request
FINISHED_IF_FAILED(pApplication->CreateHandler(pHttpContext, &pHandler));
m_pHandler.reset(pHandler);
retVal = m_pHandler->OnExecuteRequestHandler(); retVal = m_pHandler->OnExecuteRequestHandler();
} }

View File

@ -0,0 +1,68 @@
// 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 "IOutputManager.h"
#include "StdWrapper.h"
#include "EventLog.h"
#include "exceptions.h"
#include "StringHelpers.h"
#include "debugutil.h"
class BaseOutputManager :
public IOutputManager
{
public:
BaseOutputManager() : BaseOutputManager(/* fEnableNativeLogging */ true) {}
BaseOutputManager(bool enableNativeLogging) :
m_disposed(false),
stdoutWrapper(nullptr),
stderrWrapper(nullptr),
m_enableNativeRedirection(enableNativeLogging)
{
InitializeSRWLock(&m_srwLock);
}
~BaseOutputManager() {}
void
TryStartRedirection()
{
const auto startLambda = [&]() { this->Start(); };
TryOperation(startLambda, L"Could not start stdout redirection in %s. Exception message: %s.");
}
void
TryStopRedirection()
{
const auto stopLambda = [&]() { this->Stop(); };
TryOperation(stopLambda, L"Could not stop stdout redirection in %s. Exception message: %s.");
}
protected:
std::wstring m_stdOutContent;
bool m_disposed;
bool m_enableNativeRedirection;
SRWLOCK m_srwLock{};
std::unique_ptr<StdWrapper> stdoutWrapper;
std::unique_ptr<StdWrapper> stderrWrapper;
template<typename Functor>
void
TryOperation(Functor func,
std::wstring exceptionMessage)
{
try
{
func();
}
catch (std::runtime_error& exception)
{
EventLog::Warn(ASPNETCORE_EVENT_GENERAL_WARNING, exceptionMessage.c_str(), GetModuleName().c_str(), to_wide_string(exception.what(), GetConsoleOutputCP()).c_str());
}
catch (...)
{
OBSERVE_CAUGHT_EXCEPTION();
}
}
};

View File

@ -199,6 +199,7 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="application.h" /> <ClInclude Include="application.h" />
<ClInclude Include="baseoutputmanager.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

@ -9,18 +9,18 @@
#include "SRWExclusiveLock.h" #include "SRWExclusiveLock.h"
#include "file_utility.h" #include "file_utility.h"
#include "StdWrapper.h" #include "StdWrapper.h"
#include "StringHelpers.h"
extern HINSTANCE g_hModule; extern HINSTANCE g_hModule;
FileOutputManager::FileOutputManager() : FileOutputManager::FileOutputManager(std::wstring pwzStdOutLogFileName, std::wstring pwzApplicationPath) :
FileOutputManager(/* fEnableNativeLogging */ true) { } FileOutputManager(pwzStdOutLogFileName, pwzApplicationPath, /* fEnableNativeLogging */ true) { }
FileOutputManager::FileOutputManager(bool fEnableNativeLogging) : FileOutputManager::FileOutputManager(std::wstring pwzStdOutLogFileName, std::wstring pwzApplicationPath, bool fEnableNativeLogging) :
BaseOutputManager(fEnableNativeLogging),
m_hLogFileHandle(INVALID_HANDLE_VALUE), m_hLogFileHandle(INVALID_HANDLE_VALUE),
m_disposed(false), m_applicationPath(pwzApplicationPath),
stdoutWrapper(nullptr), m_stdOutLogFileName(pwzStdOutLogFileName)
stderrWrapper(nullptr),
m_fEnableNativeRedirection(fEnableNativeLogging)
{ {
InitializeSRWLock(&m_srwLock); InitializeSRWLock(&m_srwLock);
} }
@ -30,23 +30,13 @@ FileOutputManager::~FileOutputManager()
FileOutputManager::Stop(); FileOutputManager::Stop();
} }
HRESULT
FileOutputManager::Initialize(PCWSTR pwzStdOutLogFileName, PCWSTR pwzApplicationPath)
{
RETURN_IF_FAILED(m_wsApplicationPath.Copy(pwzApplicationPath));
RETURN_IF_FAILED(m_wsStdOutLogFileName.Copy(pwzStdOutLogFileName));
return S_OK;
}
// Start redirecting stdout and stderr into the file handle. // Start redirecting stdout and stderr into the file handle.
// Uses sttimer to continuously flush output into the file. // Uses sttimer to continuously flush output into the file.
HRESULT void
FileOutputManager::Start() FileOutputManager::Start()
{ {
SYSTEMTIME systemTime; SYSTEMTIME systemTime;
SECURITY_ATTRIBUTES saAttr = { 0 }; SECURITY_ATTRIBUTES saAttr = { 0 };
STRU struPath;
FILETIME processCreationTime; FILETIME processCreationTime;
FILETIME dummyFileTime; FILETIME dummyFileTime;
@ -54,48 +44,38 @@ FileOutputManager::Start()
// in the current process. // in the current process.
if (!AllocConsole()) if (!AllocConsole())
{ {
// ERROR_ACCESS_DENIED means there is a console already present. THROW_LAST_ERROR_IF(GetLastError() != ERROR_ACCESS_DENIED);
if (GetLastError() != ERROR_ACCESS_DENIED)
{
RETURN_LAST_ERROR();
}
} }
// Concatenate the log file name and application path // Concatenate the log file name and application path
RETURN_IF_FAILED(FILE_UTILITY::ConvertPathToFullPath( auto logPath = m_applicationPath / m_stdOutLogFileName;
m_wsStdOutLogFileName.QueryStr(), create_directories(logPath.parent_path());
m_wsApplicationPath.QueryStr(),
&struPath));
RETURN_IF_FAILED(FILE_UTILITY::EnsureDirectoryPathExist(struPath.QueryStr())); THROW_LAST_ERROR_IF(!GetProcessTimes(
// TODO fix string as it is incorrect
RETURN_LAST_ERROR_IF(!GetProcessTimes(
GetCurrentProcess(), GetCurrentProcess(),
&processCreationTime, &processCreationTime,
&dummyFileTime, &dummyFileTime,
&dummyFileTime, &dummyFileTime,
&dummyFileTime)); &dummyFileTime));
RETURN_LAST_ERROR_IF(!FileTimeToSystemTime(&processCreationTime, &systemTime));
RETURN_IF_FAILED( THROW_LAST_ERROR_IF(!FileTimeToSystemTime(&processCreationTime, &systemTime));
m_struLogFilePath.SafeSnwprintf(L"%s_%d%02d%02d%02d%02d%02d_%d.log",
struPath.QueryStr(), m_logFilePath = format(L"%s_%d%02d%02d%02d%02d%02d_%d.log",
systemTime.wYear, logPath.c_str(),
systemTime.wMonth, systemTime.wYear,
systemTime.wDay, systemTime.wMonth,
systemTime.wHour, systemTime.wDay,
systemTime.wMinute, systemTime.wHour,
systemTime.wSecond, systemTime.wMinute,
GetCurrentProcessId())); systemTime.wSecond,
GetCurrentProcessId());
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE; saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL; saAttr.lpSecurityDescriptor = NULL;
// Create the file with both READ and WRITE. // Create the file with both READ and WRITE.
m_hLogFileHandle = CreateFileW(m_struLogFilePath.QueryStr(), m_hLogFileHandle = CreateFileW(m_logFilePath.c_str(),
FILE_READ_DATA | FILE_WRITE_DATA, FILE_READ_DATA | FILE_WRITE_DATA,
FILE_SHARE_READ, FILE_SHARE_READ,
&saAttr, &saAttr,
@ -103,22 +83,18 @@ FileOutputManager::Start()
FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NORMAL,
nullptr); nullptr);
RETURN_LAST_ERROR_IF(m_hLogFileHandle == INVALID_HANDLE_VALUE); THROW_LAST_ERROR_IF(m_hLogFileHandle == INVALID_HANDLE_VALUE);
stdoutWrapper = std::make_unique<StdWrapper>(stdout, STD_OUTPUT_HANDLE, m_hLogFileHandle, m_fEnableNativeRedirection); stdoutWrapper = std::make_unique<StdWrapper>(stdout, STD_OUTPUT_HANDLE, m_hLogFileHandle, m_enableNativeRedirection);
stderrWrapper = std::make_unique<StdWrapper>(stderr, STD_ERROR_HANDLE, m_hLogFileHandle, m_fEnableNativeRedirection); stderrWrapper = std::make_unique<StdWrapper>(stderr, STD_ERROR_HANDLE, m_hLogFileHandle, m_enableNativeRedirection);
stdoutWrapper->StartRedirection(); stdoutWrapper->StartRedirection();
stderrWrapper->StartRedirection(); stderrWrapper->StartRedirection();
return S_OK;
} }
void
HRESULT
FileOutputManager::Stop() FileOutputManager::Stop()
{ {
STRA straStdOutput;
CHAR pzFileContents[MAX_FILE_READ_SIZE] = { 0 }; CHAR pzFileContents[MAX_FILE_READ_SIZE] = { 0 };
DWORD dwNumBytesRead; DWORD dwNumBytesRead;
LARGE_INTEGER li = { 0 }; LARGE_INTEGER li = { 0 };
@ -128,75 +104,77 @@ FileOutputManager::Stop()
if (m_disposed) if (m_disposed)
{ {
return S_OK; return;
} }
SRWExclusiveLock lock(m_srwLock); SRWExclusiveLock lock(m_srwLock);
if (m_disposed) if (m_disposed)
{ {
return S_OK; return;
} }
m_disposed = true; m_disposed = true;
if (m_hLogFileHandle == INVALID_HANDLE_VALUE) if (m_hLogFileHandle == INVALID_HANDLE_VALUE)
{ {
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); THROW_HR(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
} }
FlushFileBuffers(m_hLogFileHandle); FlushFileBuffers(m_hLogFileHandle);
if (stdoutWrapper != nullptr) if (stdoutWrapper != nullptr)
{ {
RETURN_IF_FAILED(stdoutWrapper->StopRedirection()); THROW_IF_FAILED(stdoutWrapper->StopRedirection());
} }
if (stderrWrapper != nullptr) if (stderrWrapper != nullptr)
{ {
RETURN_IF_FAILED(stderrWrapper->StopRedirection()); THROW_IF_FAILED(stderrWrapper->StopRedirection());
} }
// delete empty log file // delete empty log file
handle = FindFirstFile(m_struLogFilePath.QueryStr(), &fileData); handle = FindFirstFile(m_logFilePath.c_str(), &fileData);
if (handle != INVALID_HANDLE_VALUE && if (handle != INVALID_HANDLE_VALUE &&
handle != NULL && handle != NULL &&
fileData.nFileSizeHigh == 0 && fileData.nFileSizeHigh == 0 &&
fileData.nFileSizeLow == 0) // skip check of nFileSizeHigh fileData.nFileSizeLow == 0) // skip check of nFileSizeHigh
{ {
FindClose(handle); FindClose(handle);
LOG_LAST_ERROR_IF(!DeleteFile(m_struLogFilePath.QueryStr())); LOG_LAST_ERROR_IF(!DeleteFile(m_logFilePath.c_str()));
return;
} }
// Read the first 30Kb from the file and store it in a buffer. // Read the first 30Kb from the file and store it in a buffer.
// By doing this, we can close the handle to the file and be done with it. // By doing this, we can close the handle to the file and be done with it.
RETURN_LAST_ERROR_IF(!GetFileSizeEx(m_hLogFileHandle, &li)); THROW_LAST_ERROR_IF(!GetFileSizeEx(m_hLogFileHandle, &li));
if (li.LowPart == 0 || li.HighPart > 0) if (li.HighPart > 0)
{ {
RETURN_HR(HRESULT_FROM_WIN32(ERROR_FILE_INVALID)); THROW_HR(HRESULT_FROM_WIN32(ERROR_FILE_INVALID));
} }
dwFilePointer = SetFilePointer(m_hLogFileHandle, 0, NULL, FILE_BEGIN); dwFilePointer = SetFilePointer(m_hLogFileHandle, 0, NULL, FILE_BEGIN);
RETURN_LAST_ERROR_IF(dwFilePointer == INVALID_SET_FILE_POINTER); THROW_LAST_ERROR_IF(dwFilePointer == INVALID_SET_FILE_POINTER);
RETURN_LAST_ERROR_IF(!ReadFile(m_hLogFileHandle, pzFileContents, MAX_FILE_READ_SIZE, &dwNumBytesRead, NULL)); THROW_LAST_ERROR_IF(!ReadFile(m_hLogFileHandle, pzFileContents, MAX_FILE_READ_SIZE, &dwNumBytesRead, NULL));
m_straFileContent.Copy(pzFileContents, dwNumBytesRead); m_stdOutContent = to_wide_string(std::string(pzFileContents, dwNumBytesRead), GetConsoleOutputCP());
// printf will fail in in full IIS auto content = GetStdOutContent();
if (printf(m_straFileContent.QueryStr()) != -1) if (!content.empty())
{ {
// Need to flush contents for the new stdout and stderr // printf will fail in in full IIS
_flushall(); if (wprintf(content.c_str()) != -1)
{
// Need to flush contents for the new stdout and stderr
_flushall();
}
} }
return S_OK;
} }
bool FileOutputManager::GetStdOutContent(STRA* struStdOutput) std::wstring FileOutputManager::GetStdOutContent()
{ {
struStdOutput->Copy(m_straFileContent); return m_stdOutContent;
return m_straFileContent.QueryCCH() > 0;
} }

View File

@ -4,38 +4,27 @@
#pragma once #pragma once
#include "sttimer.h" #include "sttimer.h"
#include "IOutputManager.h"
#include "HandleWrapper.h" #include "HandleWrapper.h"
#include "StdWrapper.h" #include "StdWrapper.h"
#include "stringa.h" #include "stringa.h"
#include "stringu.h" #include "stringu.h"
#include "BaseOutputManager.h"
class FileOutputManager : public IOutputManager class FileOutputManager : public BaseOutputManager
{ {
#define FILE_FLUSH_TIMEOUT 3000
#define MAX_FILE_READ_SIZE 30000 #define MAX_FILE_READ_SIZE 30000
public: public:
FileOutputManager(); FileOutputManager(std::wstring pwzApplicationPath, std::wstring pwzStdOutLogFileName);
FileOutputManager(bool fEnableNativeLogging); FileOutputManager(std::wstring pwzApplicationPath, std::wstring pwzStdOutLogFileName, bool fEnableNativeLogging);
~FileOutputManager(); ~FileOutputManager();
HRESULT virtual std::wstring GetStdOutContent() override;
Initialize(PCWSTR pwzStdOutLogFileName, PCWSTR pwzApplciationpath); void Start() override;
void Stop() override;
virtual bool GetStdOutContent(STRA* struStdOutput) override;
virtual HRESULT Start() override;
virtual HRESULT Stop() override;
private: private:
HandleWrapper<InvalidHandleTraits> m_hLogFileHandle; HandleWrapper<InvalidHandleTraits> m_hLogFileHandle;
STTIMER m_Timer; std::wstring m_stdOutLogFileName;
STRU m_wsStdOutLogFileName; std::filesystem::path m_applicationPath;
STRU m_wsApplicationPath; std::filesystem::path m_logFilePath;
STRU m_struLogFilePath;
STRA m_straFileContent;
BOOL m_disposed;
BOOL m_fEnableNativeRedirection;
SRWLOCK m_srwLock{};
std::unique_ptr<StdWrapper> stdoutWrapper;
std::unique_ptr<StdWrapper> stderrWrapper;
}; };

View File

@ -10,18 +10,18 @@ class IOutputManager
{ {
public: public:
virtual virtual
HRESULT void
Start() = 0; Start() = 0;
virtual virtual
~IOutputManager() = default; ~IOutputManager() = default;
virtual virtual
bool std::wstring
GetStdOutContent(STRA* struStdOutput) = 0; GetStdOutContent() = 0;
virtual virtual
HRESULT void
Stop() = 0; Stop() = 0;
}; };

View File

@ -3,7 +3,6 @@
#include "stdafx.h" #include "stdafx.h"
#include "LoggingHelpers.h" #include "LoggingHelpers.h"
#include "IOutputManager.h"
#include "FileOutputManager.h" #include "FileOutputManager.h"
#include "PipeOutputManager.h" #include "PipeOutputManager.h"
#include "NullOutputManager.h" #include "NullOutputManager.h"
@ -11,6 +10,9 @@
#include <Windows.h> #include <Windows.h>
#include <io.h> #include <io.h>
#include "ntassert.h" #include "ntassert.h"
#include "exceptions.h"
#include "EventLog.h"
#include "BaseOutputManager.h"
HRESULT HRESULT
LoggingHelpers::CreateLoggingProvider( LoggingHelpers::CreateLoggingProvider(
@ -18,7 +20,7 @@ LoggingHelpers::CreateLoggingProvider(
bool fEnableNativeLogging, bool fEnableNativeLogging,
PCWSTR pwzStdOutFileName, PCWSTR pwzStdOutFileName,
PCWSTR pwzApplicationPath, PCWSTR pwzApplicationPath,
std::unique_ptr<IOutputManager>& outputManager std::unique_ptr<BaseOutputManager>& outputManager
) )
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
@ -34,8 +36,7 @@ LoggingHelpers::CreateLoggingProvider(
if (fIsLoggingEnabled) if (fIsLoggingEnabled)
{ {
auto manager = std::make_unique<FileOutputManager>(fEnableNativeLogging); auto manager = std::make_unique<FileOutputManager>(pwzStdOutFileName, pwzApplicationPath, fEnableNativeLogging);
hr = manager->Initialize(pwzStdOutFileName, pwzApplicationPath);
outputManager = std::move(manager); outputManager = std::move(manager);
} }
else if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &dummy)) else if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &dummy))

View File

@ -3,7 +3,7 @@
#pragma once #pragma once
#include "IOutputManager.h" #include "BaseOutputManager.h"
class LoggingHelpers class LoggingHelpers
{ {
@ -16,7 +16,7 @@ public:
bool fEnableNativeLogging, bool fEnableNativeLogging,
PCWSTR pwzStdOutFileName, PCWSTR pwzStdOutFileName,
PCWSTR pwzApplicationPath, PCWSTR pwzApplicationPath,
std::unique_ptr<IOutputManager>& outputManager std::unique_ptr<BaseOutputManager>& outputManager
); );
}; };

View File

@ -5,7 +5,7 @@
#include "stdafx.h" #include "stdafx.h"
class NullOutputManager : public IOutputManager class NullOutputManager : public BaseOutputManager
{ {
public: public:
@ -13,19 +13,17 @@ public:
~NullOutputManager() = default; ~NullOutputManager() = default;
HRESULT Start() void Start()
{ {
return S_OK;
} }
HRESULT Stop() void Stop()
{ {
return S_OK;
} }
bool GetStdOutContent(STRA*) std::wstring GetStdOutContent()
{ {
return false; return L"";
} }
}; };

View File

@ -1,12 +1,14 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information. // Licensed under the MIT License. See License.txt in the project root for license information.
#include "stdafx.h"
#include "PipeOutputManager.h" #include "PipeOutputManager.h"
#include "exceptions.h"
#include "stdafx.h"
#include "Exceptions.h"
#include "SRWExclusiveLock.h" #include "SRWExclusiveLock.h"
#include "StdWrapper.h" #include "StdWrapper.h"
#include "ntassert.h" #include "ntassert.h"
#include "StringHelpers.h"
#define LOG_IF_DUPFAIL(err) do { if (err == -1) { LOG_IF_FAILED(HRESULT_FROM_WIN32(_doserrno)); } } while (0, 0); #define LOG_IF_DUPFAIL(err) do { if (err == -1) { LOG_IF_FAILED(HRESULT_FROM_WIN32(_doserrno)); } } while (0, 0);
#define LOG_IF_ERRNO(err) do { if (err != 0) { LOG_IF_FAILED(HRESULT_FROM_WIN32(_doserrno)); } } while (0, 0); #define LOG_IF_ERRNO(err) do { if (err != 0) { LOG_IF_FAILED(HRESULT_FROM_WIN32(_doserrno)); } } while (0, 0);
@ -17,16 +19,12 @@ PipeOutputManager::PipeOutputManager()
} }
PipeOutputManager::PipeOutputManager(bool fEnableNativeLogging) : PipeOutputManager::PipeOutputManager(bool fEnableNativeLogging) :
BaseOutputManager(fEnableNativeLogging),
m_hErrReadPipe(INVALID_HANDLE_VALUE), m_hErrReadPipe(INVALID_HANDLE_VALUE),
m_hErrWritePipe(INVALID_HANDLE_VALUE), m_hErrWritePipe(INVALID_HANDLE_VALUE),
m_hErrThread(nullptr), m_hErrThread(nullptr),
m_dwStdErrReadTotal(0), m_numBytesReadTotal(0)
m_disposed(FALSE),
m_fEnableNativeRedirection(fEnableNativeLogging),
stdoutWrapper(nullptr),
stderrWrapper(nullptr)
{ {
InitializeSRWLock(&m_srwLock);
} }
PipeOutputManager::~PipeOutputManager() PipeOutputManager::~PipeOutputManager()
@ -37,7 +35,7 @@ PipeOutputManager::~PipeOutputManager()
// Start redirecting stdout and stderr into a pipe // Start redirecting stdout and stderr into a pipe
// Continuously read the pipe on a background thread // Continuously read the pipe on a background thread
// until Stop is called. // until Stop is called.
HRESULT PipeOutputManager::Start() void PipeOutputManager::Start()
{ {
SECURITY_ATTRIBUTES saAttr = { 0 }; SECURITY_ATTRIBUTES saAttr = { 0 };
HANDLE hStdErrReadPipe; HANDLE hStdErrReadPipe;
@ -50,17 +48,17 @@ HRESULT PipeOutputManager::Start()
// ERROR_ACCESS_DENIED means there is a console already present. // ERROR_ACCESS_DENIED means there is a console already present.
if (GetLastError() != ERROR_ACCESS_DENIED) if (GetLastError() != ERROR_ACCESS_DENIED)
{ {
RETURN_LAST_ERROR(); THROW_LAST_ERROR();
} }
} }
RETURN_LAST_ERROR_IF(!CreatePipe(&hStdErrReadPipe, &hStdErrWritePipe, &saAttr, 0 /*nSize*/)); THROW_LAST_ERROR_IF(!CreatePipe(&hStdErrReadPipe, &hStdErrWritePipe, &saAttr, 0 /*nSize*/));
m_hErrReadPipe = hStdErrReadPipe; m_hErrReadPipe = hStdErrReadPipe;
m_hErrWritePipe = hStdErrWritePipe; m_hErrWritePipe = hStdErrWritePipe;
stdoutWrapper = std::make_unique<StdWrapper>(stdout, STD_OUTPUT_HANDLE, hStdErrWritePipe, m_fEnableNativeRedirection); stdoutWrapper = std::make_unique<StdWrapper>(stdout, STD_OUTPUT_HANDLE, hStdErrWritePipe, m_enableNativeRedirection);
stderrWrapper = std::make_unique<StdWrapper>(stderr, STD_ERROR_HANDLE, hStdErrWritePipe, m_fEnableNativeRedirection); stderrWrapper = std::make_unique<StdWrapper>(stderr, STD_ERROR_HANDLE, hStdErrWritePipe, m_enableNativeRedirection);
LOG_IF_FAILED(stdoutWrapper->StartRedirection()); LOG_IF_FAILED(stdoutWrapper->StartRedirection());
LOG_IF_FAILED(stderrWrapper->StartRedirection()); LOG_IF_FAILED(stderrWrapper->StartRedirection());
@ -74,9 +72,7 @@ HRESULT PipeOutputManager::Start()
0, // default creation flags 0, // default creation flags
nullptr); // receive thread identifier nullptr); // receive thread identifier
RETURN_LAST_ERROR_IF_NULL(m_hErrThread); THROW_LAST_ERROR_IF_NULL(m_hErrThread);
return S_OK;
} }
// Stop redirecting stdout and stderr into a pipe // Stop redirecting stdout and stderr into a pipe
@ -84,21 +80,20 @@ HRESULT PipeOutputManager::Start()
// and prints any output that was captured in the pipe. // and prints any output that was captured in the pipe.
// If more than 30Kb was written to the pipe, that output will // If more than 30Kb was written to the pipe, that output will
// be thrown away. // be thrown away.
HRESULT PipeOutputManager::Stop() void PipeOutputManager::Stop()
{ {
DWORD dwThreadStatus = 0; DWORD dwThreadStatus = 0;
STRA straStdOutput;
if (m_disposed) if (m_disposed)
{ {
return S_OK; return;
} }
SRWExclusiveLock lock(m_srwLock); SRWExclusiveLock lock(m_srwLock);
if (m_disposed) if (m_disposed)
{ {
return S_OK; return;
} }
m_disposed = true; m_disposed = true;
@ -108,7 +103,7 @@ HRESULT PipeOutputManager::Stop()
if (m_hErrWritePipe != INVALID_HANDLE_VALUE) if (m_hErrWritePipe != INVALID_HANDLE_VALUE)
{ {
// Flush the pipe writer before closing to capture all output // Flush the pipe writer before closing to capture all output
RETURN_LAST_ERROR_IF(!FlushFileBuffers(m_hErrWritePipe)); THROW_LAST_ERROR_IF(!FlushFileBuffers(m_hErrWritePipe));
CloseHandle(m_hErrWritePipe); CloseHandle(m_hErrWritePipe);
m_hErrWritePipe = INVALID_HANDLE_VALUE; m_hErrWritePipe = INVALID_HANDLE_VALUE;
} }
@ -163,33 +158,22 @@ HRESULT PipeOutputManager::Stop()
// If we captured any output, relog it to the original stdout // If we captured any output, relog it to the original stdout
// Useful for the IIS Express scenario as it is running with stdout and stderr // Useful for the IIS Express scenario as it is running with stdout and stderr
if (GetStdOutContent(&straStdOutput)) m_stdOutContent = to_wide_string(std::string(m_pipeContents, m_numBytesReadTotal), GetConsoleOutputCP());
if (!m_stdOutContent.empty())
{ {
// printf will fail in in full IIS // printf will fail in in full IIS
if (printf(straStdOutput.QueryStr()) != -1) if (wprintf(m_stdOutContent.c_str()) != -1)
{ {
// Need to flush contents for the new stdout and stderr // Need to flush contents for the new stdout and stderr
_flushall(); _flushall();
} }
} }
return S_OK;
} }
bool PipeOutputManager::GetStdOutContent(STRA* straStdOutput) std::wstring PipeOutputManager::GetStdOutContent()
{ {
bool fLogged = false; return m_stdOutContent;
// TODO consider returning the file contents rather than copying.
if (m_dwStdErrReadTotal > 0)
{
if (SUCCEEDED(straStdOutput->Copy(m_pzFileContents, m_dwStdErrReadTotal)))
{
fLogged = TRUE;
}
}
return fLogged;
} }
void void
@ -211,13 +195,13 @@ PipeOutputManager::ReadStdErrHandleInternal()
{ {
// Fill a maximum of MAX_PIPE_READ_SIZE into a buffer. // Fill a maximum of MAX_PIPE_READ_SIZE into a buffer.
if (ReadFile(m_hErrReadPipe, if (ReadFile(m_hErrReadPipe,
&m_pzFileContents[m_dwStdErrReadTotal], &m_pipeContents[m_numBytesReadTotal],
MAX_PIPE_READ_SIZE - m_dwStdErrReadTotal, MAX_PIPE_READ_SIZE - m_numBytesReadTotal,
&dwNumBytesRead, &dwNumBytesRead,
nullptr)) nullptr))
{ {
m_dwStdErrReadTotal += dwNumBytesRead; m_numBytesReadTotal += dwNumBytesRead;
if (m_dwStdErrReadTotal >= MAX_PIPE_READ_SIZE) if (m_numBytesReadTotal >= MAX_PIPE_READ_SIZE)
{ {
break; break;
} }

View File

@ -3,11 +3,11 @@
#pragma once #pragma once
#include "IOutputManager.h"
#include "StdWrapper.h" #include "StdWrapper.h"
#include "stringu.h" #include "stringu.h"
#include "BaseOutputManager.h"
class PipeOutputManager : public IOutputManager class PipeOutputManager : public BaseOutputManager
{ {
// Timeout to be used if a thread never exits // Timeout to be used if a thread never exits
#define PIPE_OUTPUT_THREAD_TIMEOUT 2000 #define PIPE_OUTPUT_THREAD_TIMEOUT 2000
@ -19,9 +19,9 @@ public:
PipeOutputManager(bool fEnableNativeLogging); PipeOutputManager(bool fEnableNativeLogging);
~PipeOutputManager(); ~PipeOutputManager();
HRESULT Start() override; void Start() override;
HRESULT Stop() override; void Stop() override;
bool GetStdOutContent(STRA* straStdOutput) override; std::wstring GetStdOutContent() override;
// Thread functions // Thread functions
void ReadStdErrHandleInternal(); void ReadStdErrHandleInternal();
@ -32,13 +32,7 @@ private:
HANDLE m_hErrReadPipe; HANDLE m_hErrReadPipe;
HANDLE m_hErrWritePipe; HANDLE m_hErrWritePipe;
STRU m_struLogFilePath;
HANDLE m_hErrThread; HANDLE m_hErrThread;
CHAR m_pzFileContents[MAX_PIPE_READ_SIZE] = { 0 }; CHAR m_pipeContents[MAX_PIPE_READ_SIZE] = { 0 };
DWORD m_dwStdErrReadTotal; DWORD m_numBytesReadTotal;
SRWLOCK m_srwLock {};
BOOL m_disposed;
BOOL m_fEnableNativeRedirection;
std::unique_ptr<StdWrapper> stdoutWrapper;
std::unique_ptr<StdWrapper> stderrWrapper;
}; };

View File

@ -12,7 +12,7 @@ StdWrapper::StdWrapper(FILE* stdStream, DWORD stdHandleNumber, HANDLE handleToRe
: m_previousFileDescriptor(0), : m_previousFileDescriptor(0),
m_stdStream(stdStream), m_stdStream(stdStream),
m_stdHandleNumber(stdHandleNumber), m_stdHandleNumber(stdHandleNumber),
m_fEnableNativeRedirection(fEnableNativeRedirection), m_enableNativeRedirection(fEnableNativeRedirection),
m_handleToRedirectTo(handleToRedirectTo), m_handleToRedirectTo(handleToRedirectTo),
m_redirectedFile(nullptr) m_redirectedFile(nullptr)
{ {
@ -48,7 +48,7 @@ StdWrapper::StartRedirection()
m_previousFileDescriptor = _dup(_fileno(m_stdStream)); m_previousFileDescriptor = _dup(_fileno(m_stdStream));
} }
if (!m_fEnableNativeRedirection) if (!m_enableNativeRedirection)
{ {
RETURN_LAST_ERROR_IF(!SetStdHandle(m_stdHandleNumber, m_handleToRedirectTo)); RETURN_LAST_ERROR_IF(!SetStdHandle(m_stdHandleNumber, m_handleToRedirectTo));
@ -122,7 +122,7 @@ StdWrapper::StopRedirection() const
RETURN_LAST_ERROR_IF(!SetStdHandle(m_stdHandleNumber, reinterpret_cast<HANDLE>(_get_osfhandle(m_previousFileDescriptor)))); RETURN_LAST_ERROR_IF(!SetStdHandle(m_stdHandleNumber, reinterpret_cast<HANDLE>(_get_osfhandle(m_previousFileDescriptor))));
if (!m_fEnableNativeRedirection) if (!m_enableNativeRedirection)
{ {
return S_OK; return S_OK;
} }

View File

@ -18,7 +18,7 @@ private:
int m_previousFileDescriptor; int m_previousFileDescriptor;
FILE* m_stdStream; FILE* m_stdStream;
DWORD m_stdHandleNumber; DWORD m_stdHandleNumber;
BOOL m_fEnableNativeRedirection; BOOL m_enableNativeRedirection;
HANDLE m_handleToRedirectTo; HANDLE m_handleToRedirectTo;
FILE* m_redirectedFile; FILE* m_redirectedFile;
}; };

View File

@ -2,6 +2,7 @@
// 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.
#include "StringHelpers.h" #include "StringHelpers.h"
#include "exceptions.h"
bool ends_with(const std::wstring &source, const std::wstring &suffix, bool ignoreCase) bool ends_with(const std::wstring &source, const std::wstring &suffix, bool ignoreCase)
{ {
@ -18,3 +19,25 @@ 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); return CSTR_EQUAL == CompareStringOrdinal(s1.c_str(), static_cast<int>(s1.length()), s2.c_str(), static_cast<int>(s2.length()), true);
} }
std::wstring to_wide_string(const std::string &source, const unsigned int codePage)
{
// MultiByteToWideChar returns 0 on failure, which is also the same return value
// for empty strings. Preemptive return.
if (source.length() == 0)
{
return L"";
}
std::wstring destination;
int nChars = MultiByteToWideChar(codePage, 0, source.data(), static_cast<int>(source.length()), NULL, 0);
THROW_LAST_ERROR_IF(nChars == 0);
destination.resize(nChars);
nChars = MultiByteToWideChar(codePage, 0, source.data(), static_cast<int>(source.length()), destination.data(), nChars);
THROW_LAST_ERROR_IF(nChars == 0);
return destination;
}

View File

@ -11,6 +11,9 @@ bool ends_with(const std::wstring &source, const std::wstring &suffix, bool igno
[[nodiscard]] [[nodiscard]]
bool equals_ignore_case(const std::wstring& s1, const std::wstring& s2); bool equals_ignore_case(const std::wstring& s1, const std::wstring& s2);
[[nodiscard]]
std::wstring to_wide_string(const std::string &source, const unsigned int codePage);
template<typename ... Args> template<typename ... Args>
[[nodiscard]] [[nodiscard]]
std::wstring format(const std::wstring& format, Args ... args) std::wstring format(const std::wstring& format, Args ... args)

View File

@ -7,7 +7,6 @@
#include "iapplication.h" #include "iapplication.h"
#include "ntassert.h" #include "ntassert.h"
#include "SRWExclusiveLock.h" #include "SRWExclusiveLock.h"
#include "SRWSharedLock.h"
class APPLICATION : public IAPPLICATION class APPLICATION : public IAPPLICATION
{ {
@ -16,6 +15,26 @@ public:
APPLICATION(const APPLICATION&) = delete; APPLICATION(const APPLICATION&) = delete;
const APPLICATION& operator=(const APPLICATION&) = delete; const APPLICATION& operator=(const APPLICATION&) = delete;
HRESULT
TryCreateHandler(
_In_ IHttpContext *pHttpContext,
_Outptr_result_maybenull_ IREQUEST_HANDLER **pRequestHandler) override
{
*pRequestHandler = nullptr;
if (m_fStopCalled)
{
return S_FALSE;
}
return CreateHandler(pHttpContext, pRequestHandler);
}
virtual
HRESULT
CreateHandler(
_In_ IHttpContext *pHttpContext,
_Outptr_opt_ IREQUEST_HANDLER **pRequestHandler) = 0;
APPLICATION(const IHttpApplication& pHttpApplication) APPLICATION(const IHttpApplication& pHttpApplication)
: m_fStopCalled(false), : m_fStopCalled(false),
@ -28,12 +47,6 @@ public:
m_applicationVirtualPath = ToVirtualPath(m_applicationConfigPath); m_applicationVirtualPath = ToVirtualPath(m_applicationConfigPath);
} }
APPLICATION_STATUS
QueryStatus() override
{
SRWSharedLock stateLock(m_stateLock);
return m_fStopCalled ? APPLICATION_STATUS::RECYCLED : APPLICATION_STATUS::RUNNING;
}
VOID VOID
Stop(bool fServerInitiated) override Stop(bool fServerInitiated) override

View File

@ -59,6 +59,7 @@
#define FINISHED_LAST_ERROR_IF(condition) do { if (condition) { hr = LogLastError(LOCATION_INFO); _GOTO_FINISHED(); }} while (0, 0) #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 FINISHED_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { hr = LogLastError(LOCATION_INFO); _GOTO_FINISHED(); }} while (0, 0)
#define THROW_HR(hr) do { _HR_RET(hr); ThrowResultException(LOCATION_INFO, LogHResultFailed(LOCATION_INFO, __hrRet)); } while (0, 0)
#define THROW_LAST_ERROR() do { ThrowResultException(LOCATION_INFO, LogLastError(LOCATION_INFO)); } while (0, 0) #define THROW_LAST_ERROR() do { ThrowResultException(LOCATION_INFO, LogLastError(LOCATION_INFO)); } while (0, 0)
#define THROW_IF_FAILED(hr) do { _HR_RET(hr); if (FAILED(__hrRet)) { ThrowResultException(LOCATION_INFO, __hrRet); }} while (0, 0) #define THROW_IF_FAILED(hr) do { _HR_RET(hr); if (FAILED(__hrRet)) { ThrowResultException(LOCATION_INFO, __hrRet); }} while (0, 0)
#define THROW_LAST_ERROR_IF(condition) do { if (condition) { ThrowResultException(LOCATION_INFO, LogLastError(LOCATION_INFO)); }} while (0, 0) #define THROW_LAST_ERROR_IF(condition) do { if (condition) { ThrowResultException(LOCATION_INFO, LogLastError(LOCATION_INFO)); }} while (0, 0)

View File

@ -6,12 +6,6 @@
#include <memory> #include <memory>
#include "irequesthandler.h" #include "irequesthandler.h"
enum APPLICATION_STATUS
{
RUNNING,
RECYCLED,
};
struct APPLICATION_PARAMETER struct APPLICATION_PARAMETER
{ {
LPCSTR pzName; LPCSTR pzName;
@ -28,10 +22,6 @@ public:
virtual virtual
~IAPPLICATION() = 0 { }; ~IAPPLICATION() = 0 { };
virtual
APPLICATION_STATUS
QueryStatus() = 0;
virtual virtual
VOID VOID
ReferenceApplication() = 0; ReferenceApplication() = 0;
@ -42,7 +32,7 @@ public:
virtual virtual
HRESULT HRESULT
CreateHandler( TryCreateHandler(
_In_ IHttpContext *pHttpContext, _In_ IHttpContext *pHttpContext,
_Outptr_ IREQUEST_HANDLER **pRequestHandler) = 0; _Outptr_ IREQUEST_HANDLER **pRequestHandler) = 0;
}; };

View File

@ -39,8 +39,8 @@
#define ASPNETCORE_EVENT_RECYCLE_FAILURE_CONFIGURATION_MSG L"Failed to recycle application due to a configuration change at '%s'. Recycling worker process." #define ASPNETCORE_EVENT_RECYCLE_FAILURE_CONFIGURATION_MSG L"Failed to recycle application due to a configuration change at '%s'. Recycling worker process."
#define ASPNETCORE_EVENT_MODULE_DISABLED_MSG L"AspNetCore Module is disabled" #define ASPNETCORE_EVENT_MODULE_DISABLED_MSG L"AspNetCore Module is disabled"
#define ASPNETCORE_EVENT_HOSTFXR_DLL_INVALID_VERSION_MSG L"Hostfxr version used does not support 'hostfxr_get_native_search_directories', update the version of hostfxr to a higher version. Path to hostfxr: '%s'." #define ASPNETCORE_EVENT_HOSTFXR_DLL_INVALID_VERSION_MSG L"Hostfxr version used does not support 'hostfxr_get_native_search_directories', update the version of hostfxr to a higher version. Path to hostfxr: '%s'."
#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXCEPTION_MSG L"Application '%s' with physical root '%s' hit unexpected managed exception, exception code = '0x%x. Please check the stderr logs for more information." #define ASPNETCORE_EVENT_INPROCESS_THREAD_EXCEPTION_MSG L"Application '%s' with physical root '%s' hit unexpected managed exception, exception code = '0x%x'. Please check the stderr logs for more information."
#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXCEPTION_STDOUT_MSG L"Application '%s' with physical root '%s' hit unexpected managed exception, exception code = '0x%x. Last 4KB characters of captured stdout and stderr logs:\r\n%s" #define ASPNETCORE_EVENT_INPROCESS_THREAD_EXCEPTION_STDOUT_MSG L"Application '%s' with physical root '%s' hit unexpected managed exception, exception code = '0x%x'. Last 4KB characters of captured stdout and stderr logs:\r\n%s"
#define ASPNETCORE_EVENT_INPROCESS_RH_ERROR_MSG L"Could not find the assembly '%s' for in-process application. Please confirm the Microsoft.AspNetCore.Server.IIS package is referenced in your application. Captured output: %s" #define ASPNETCORE_EVENT_INPROCESS_RH_ERROR_MSG L"Could not find the assembly '%s' for in-process application. Please confirm the Microsoft.AspNetCore.Server.IIS package is referenced in your application. Captured output: %s"
#define ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING_MSG L"Could not find the assembly '%s' for out-of-process application. Please confirm the assembly is installed correctly for IIS or IISExpress." #define ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING_MSG L"Could not find the assembly '%s' for out-of-process application. Please confirm the assembly is installed correctly for IIS or IISExpress."
#define ASPNETCORE_EVENT_INPROCESS_START_SUCCESS_MSG L"Application '%s' started the coreclr in-process successfully." #define ASPNETCORE_EVENT_INPROCESS_START_SUCCESS_MSG L"Application '%s' started the coreclr in-process successfully."

View File

@ -207,7 +207,7 @@ IN_PROCESS_APPLICATION::ExecuteApplication()
QueryApplicationPhysicalPath().c_str(), QueryApplicationPhysicalPath().c_str(),
m_pLoggerProvider)); m_pLoggerProvider));
LOG_IF_FAILED(m_pLoggerProvider->Start()); m_pLoggerProvider->TryStartRedirection();
} }
// There can only ever be a single instance of .NET Core // There can only ever be a single instance of .NET Core
@ -246,7 +246,7 @@ IN_PROCESS_APPLICATION::ExecuteApplication()
// At this point CLR thread either finished or timed out, abandon it. // At this point CLR thread either finished or timed out, abandon it.
m_clrThread.detach(); m_clrThread.detach();
LOG_IF_FAILED(m_pLoggerProvider->Stop()); m_pLoggerProvider->TryStopRedirection();
if (m_fStopCalled) if (m_fStopCalled)
{ {
@ -391,8 +391,6 @@ IN_PROCESS_APPLICATION::ExecuteClr(const std::shared_ptr<ExecuteClrContext>& con
VOID VOID
IN_PROCESS_APPLICATION::ClrThreadEntryPoint(const std::shared_ptr<ExecuteClrContext> &context) IN_PROCESS_APPLICATION::ClrThreadEntryPoint(const std::shared_ptr<ExecuteClrContext> &context)
{ {
LOG_INFO(L"Starting CLR thread");
// Keep aspnetcorev2_inprocess.dll loaded while this thread is running // Keep aspnetcorev2_inprocess.dll loaded while this thread is running
// this is required because thread might be abandoned // this is required because thread might be abandoned
HandleWrapper<ModuleHandleTraits> moduleHandle; HandleWrapper<ModuleHandleTraits> moduleHandle;
@ -441,14 +439,11 @@ IN_PROCESS_APPLICATION::SetEnvironmentVariablesOnWorkerProcess()
VOID VOID
IN_PROCESS_APPLICATION::UnexpectedThreadExit(const ExecuteClrContext& context) const IN_PROCESS_APPLICATION::UnexpectedThreadExit(const ExecuteClrContext& context) const
{ {
STRA straStdErrOutput; auto content = m_pLoggerProvider->GetStdOutContent();
STRU struStdMsg;
auto hasStdOut = m_pLoggerProvider->GetStdOutContent(&straStdErrOutput) &&
SUCCEEDED(struStdMsg.CopyA(straStdErrOutput.QueryStr()));
if (context.m_exceptionCode != 0) if (context.m_exceptionCode != 0)
{ {
if (hasStdOut) if (!content.empty())
{ {
EventLog::Error( EventLog::Error(
ASPNETCORE_EVENT_INPROCESS_THREAD_EXCEPTION, ASPNETCORE_EVENT_INPROCESS_THREAD_EXCEPTION,
@ -456,7 +451,7 @@ IN_PROCESS_APPLICATION::UnexpectedThreadExit(const ExecuteClrContext& context) c
QueryApplicationId().c_str(), QueryApplicationId().c_str(),
QueryApplicationPhysicalPath().c_str(), QueryApplicationPhysicalPath().c_str(),
context.m_exceptionCode, context.m_exceptionCode,
struStdMsg.QueryStr()); content.c_str());
} }
else else
{ {
@ -476,7 +471,8 @@ IN_PROCESS_APPLICATION::UnexpectedThreadExit(const ExecuteClrContext& context) c
// This will be a common place for errors as it means the hostfxr_main returned // This will be a common place for errors as it means the hostfxr_main returned
// or there was an exception. // or there was an exception.
// //
if (hasStdOut)
if (!content.empty())
{ {
EventLog::Error( EventLog::Error(
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT, ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT,
@ -484,7 +480,7 @@ IN_PROCESS_APPLICATION::UnexpectedThreadExit(const ExecuteClrContext& context) c
QueryApplicationId().c_str(), QueryApplicationId().c_str(),
QueryApplicationPhysicalPath().c_str(), QueryApplicationPhysicalPath().c_str(),
context.m_exitCode, context.m_exitCode,
struStdMsg.QueryStr()); content.c_str());
} }
else else
{ {

View File

@ -5,8 +5,8 @@
#include <thread> #include <thread>
#include "InProcessApplicationBase.h" #include "InProcessApplicationBase.h"
#include "IOutputManager.h"
#include "InProcessOptions.h" #include "InProcessOptions.h"
#include "BaseOutputManager.h"
class IN_PROCESS_HANDLER; class IN_PROCESS_HANDLER;
typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_REQUEST_HANDLER) (IN_PROCESS_HANDLER* pInProcessHandler, void* pvRequestHandlerContext); typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_REQUEST_HANDLER) (IN_PROCESS_HANDLER* pInProcessHandler, void* pvRequestHandlerContext);
@ -155,7 +155,7 @@ private:
static IN_PROCESS_APPLICATION* s_Application; static IN_PROCESS_APPLICATION* s_Application;
std::unique_ptr<IOutputManager> m_pLoggerProvider; std::unique_ptr<BaseOutputManager> m_pLoggerProvider;
inline static const LPCSTR s_exeLocationParameterName = "InProcessExeLocation"; inline static const LPCSTR s_exeLocationParameterName = "InProcessExeLocation";

View File

@ -59,8 +59,7 @@ IN_PROCESS_HANDLER::OnExecuteRequestHandler()
return RQ_NOTIFICATION_FINISH_REQUEST; return RQ_NOTIFICATION_FINISH_REQUEST;
} }
else if (m_pApplication->QueryStatus() != APPLICATION_STATUS::RUNNING || m_pApplication-> else if (m_pApplication->QueryBlockCallbacksIntoManaged())
QueryBlockCallbacksIntoManaged())
{ {
return ServerShutdownMessage(); return ServerShutdownMessage();
} }

View File

@ -24,8 +24,8 @@ RESPONSE_HEADER_HASH * FORWARDING_HANDLER::sm_pResponseHeaderHash = NULL;
FORWARDING_HANDLER::FORWARDING_HANDLER( FORWARDING_HANDLER::FORWARDING_HANDLER(
_In_ IHttpContext *pW3Context, _In_ IHttpContext *pW3Context,
_In_ OUT_OF_PROCESS_APPLICATION *pApplication _In_ std::unique_ptr<OUT_OF_PROCESS_APPLICATION, IAPPLICATION_DELETER> pApplication
) : IREQUEST_HANDLER(), ) : REQUEST_HANDLER(),
m_Signature(FORWARDING_HANDLER_SIGNATURE), m_Signature(FORWARDING_HANDLER_SIGNATURE),
m_RequestStatus(FORWARDER_START), m_RequestStatus(FORWARDER_START),
m_fClientDisconnected(FALSE), m_fClientDisconnected(FALSE),
@ -46,7 +46,7 @@ FORWARDING_HANDLER::FORWARDING_HANDLER(
m_fServerResetConn(FALSE), m_fServerResetConn(FALSE),
m_cRefs(1), m_cRefs(1),
m_pW3Context(pW3Context), m_pW3Context(pW3Context),
m_pApplication(pApplication) m_pApplication(std::move(pApplication))
{ {
LOG_TRACE(L"FORWARDING_HANDLER::FORWARDING_HANDLER"); LOG_TRACE(L"FORWARDING_HANDLER::FORWARDING_HANDLER");
@ -100,7 +100,6 @@ FORWARDING_HANDLER::OnExecuteRequestHandler()
IHttpRequest *pRequest = m_pW3Context->GetRequest(); IHttpRequest *pRequest = m_pW3Context->GetRequest();
IHttpResponse *pResponse = m_pW3Context->GetResponse(); IHttpResponse *pResponse = m_pW3Context->GetResponse();
IHttpConnection *pClientConnection = NULL; IHttpConnection *pClientConnection = NULL;
OUT_OF_PROCESS_APPLICATION *pApplication = NULL;
PROTOCOL_CONFIG *pProtocol = &sm_ProtocolConfig; PROTOCOL_CONFIG *pProtocol = &sm_ProtocolConfig;
SERVER_PROCESS *pServerProcess = NULL; SERVER_PROCESS *pServerProcess = NULL;
@ -128,14 +127,13 @@ FORWARDING_HANDLER::OnExecuteRequestHandler()
goto Failure; goto Failure;
} }
pApplication = static_cast<OUT_OF_PROCESS_APPLICATION*> (m_pApplication); if (m_pApplication == NULL)
if (pApplication == NULL)
{ {
hr = E_INVALIDARG; hr = E_INVALIDARG;
goto Failure; goto Failure;
} }
hr = pApplication->GetProcess(&pServerProcess); hr = m_pApplication->GetProcess(&pServerProcess);
if (FAILED_LOG(hr)) if (FAILED_LOG(hr))
{ {
fFailedToStartKestrel = TRUE; fFailedToStartKestrel = TRUE;

View File

@ -22,7 +22,7 @@ class FORWARDING_HANDLER : public REQUEST_HANDLER
public: public:
FORWARDING_HANDLER( FORWARDING_HANDLER(
_In_ IHttpContext *pW3Context, _In_ IHttpContext *pW3Context,
_In_ OUT_OF_PROCESS_APPLICATION *pApplication _In_ std::unique_ptr<OUT_OF_PROCESS_APPLICATION, IAPPLICATION_DELETER> pApplication
); );
~FORWARDING_HANDLER(); ~FORWARDING_HANDLER();
@ -238,6 +238,6 @@ private:
mutable LONG m_cRefs; mutable LONG m_cRefs;
IHttpContext* m_pW3Context; IHttpContext* m_pW3Context;
OUT_OF_PROCESS_APPLICATION* m_pApplication; std::unique_ptr<OUT_OF_PROCESS_APPLICATION, IAPPLICATION_DELETER> m_pApplication;
HTTP_MODULE_ID m_pModuleId; HTTP_MODULE_ID m_pModuleId;
}; };

View File

@ -72,7 +72,7 @@ OUT_OF_PROCESS_APPLICATION::CreateHandler(
SetWebsocketStatus(pHttpContext); SetWebsocketStatus(pHttpContext);
} }
pHandler = new FORWARDING_HANDLER(pHttpContext, this); pHandler = new FORWARDING_HANDLER(pHttpContext, ::ReferenceApplication(this));
*pRequestHandler = pHandler; *pRequestHandler = pHandler;
return S_OK; return S_OK;
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Server.IntegrationTesting;
@ -68,6 +69,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
var deploymentResult = await DeployAsync(deploymentParameters); var deploymentResult = await DeployAsync(deploymentParameters);
await Helpers.AssertStarts(deploymentResult, "HelloWorld"); await Helpers.AssertStarts(deploymentResult, "HelloWorld");
StopServer();
EventLogHelpers.VerifyEventLogEvent(deploymentResult, "Could not start stdout redirection in (.*)aspnetcorev2.dll. Exception message: HRESULT 0x80070003");
EventLogHelpers.VerifyEventLogEvent(deploymentResult, "Could not stop stdout redirection in (.*)aspnetcorev2.dll. Exception message: HRESULT 0x80070002");
} }
[ConditionalFact] [ConditionalFact]
@ -222,6 +227,48 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
Assert.Contains("FORWARDING_HANDLER::", logContents); Assert.Contains("FORWARDING_HANDLER::", logContents);
} }
[ConditionalFact]
public async Task CheckUnicodePipe()
{
var path = "CheckConsoleFunctions";
var deploymentParameters = _fixture.GetBaseDeploymentParameters(_fixture.StartupExceptionWebsite, publish: true);
deploymentParameters.TransformArguments((a, _) => $"{a} {path}");
var deploymentResult = await DeployAsync(deploymentParameters);
var response = await deploymentResult.HttpClient.GetAsync(path);
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
StopServer();
EventLogHelpers.VerifyEventLogEvent(deploymentResult, EventLogHelpers.InProcessThreadExitStdOut(deploymentResult, "12", "(.*)彡⾔(.*)"));
}
[ConditionalFact]
public async Task CheckUTF8File()
{
var path = "CheckConsoleFunctions";
var deploymentParameters = _fixture.GetBaseDeploymentParameters(_fixture.StartupExceptionWebsite, publish: true);
deploymentParameters.TransformArguments((a, _) => $"{a} {path}");
deploymentParameters.EnableLogging(_logFolderPath);
var deploymentResult = await DeployAsync(deploymentParameters);
var response = await deploymentResult.HttpClient.GetAsync(path);
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
StopServer();
var contents = File.ReadAllText(Helpers.GetExpectedLogName(deploymentResult, _logFolderPath));
Assert.Contains("彡⾔", contents);
EventLogHelpers.VerifyEventLogEvent(deploymentResult, EventLogHelpers.InProcessThreadExitStdOut(deploymentResult, "12", "(.*)彡⾔(.*)"));
}
private static string ReadLogs(string logPath) private static string ReadLogs(string logPath)
{ {
using (var stream = File.Open(logPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) using (var stream = File.Open(logPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))

View File

@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
await AssertFailsToStart(deploymentParameters); await AssertFailsToStart(deploymentParameters);
Assert.Contains(TestSink.Writes, context => context.Message.Contains(new string('a', 4096))); Assert.Contains(TestSink.Writes, context => context.Message.Contains(new string('a', 30000)));
} }
[ConditionalFact] [ConditionalFact]

View File

@ -121,7 +121,11 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
return $"Application '/LM/W3SVC/1/ROOT' with physical root '{EscapedContentRoot(deploymentResult)}' hit unexpected managed background thread exit, exit code = '{code}'."; return $"Application '/LM/W3SVC/1/ROOT' with physical root '{EscapedContentRoot(deploymentResult)}' hit unexpected managed background thread exit, exit code = '{code}'.";
} }
public static string InProcessThreadExitStdOut(IISDeploymentResult deploymentResult, string code, string output)
{
return $"Application '/LM/W3SVC/1/ROOT' with physical root '{EscapedContentRoot(deploymentResult)}' hit unexpected managed background thread exit, exit code = '{code}'. Last 4KB characters of captured stdout and stderr logs:\r\n{output}";
}
public static string FailedToStartApplication(IISDeploymentResult deploymentResult, string code) public static string FailedToStartApplication(IISDeploymentResult deploymentResult, string code)
{ {
return $"Failed to start application '/LM/W3SVC/1/ROOT', ErrorCode '{code}'."; return $"Failed to start application '/LM/W3SVC/1/ROOT', ErrorCode '{code}'.";

View File

@ -33,9 +33,8 @@ namespace FileOutManagerStartupTests
PCWSTR expected = L"test"; PCWSTR expected = L"test";
auto tempDirectory = TempDirectory(); auto tempDirectory = TempDirectory();
FileOutputManager* pManager = new FileOutputManager; FileOutputManager* pManager = new FileOutputManager(fileNamePrefix, tempDirectory.path());
pManager->Initialize(fileNamePrefix.c_str(), tempDirectory.path().c_str());
{ {
FileManagerWrapper wrapper(pManager); FileManagerWrapper wrapper(pManager);
@ -69,89 +68,84 @@ namespace FileOutManagerOutputTests
{ {
TEST(FileOutManagerOutputTest, StdOut) TEST(FileOutManagerOutputTest, StdOut)
{ {
PCSTR expected = "test"; PCWSTR expected = L"test";
auto tempDirectory = TempDirectory(); auto tempDirectory = TempDirectory();
FileOutputManager* pManager = new FileOutputManager; FileOutputManager* pManager = new FileOutputManager(L"", tempDirectory.path());
pManager->Initialize(L"", tempDirectory.path().c_str());
{ {
FileManagerWrapper wrapper(pManager); FileManagerWrapper wrapper(pManager);
fprintf(stdout, expected); fwprintf(stdout, expected);
pManager->Stop(); pManager->Stop();
STRA straContent; auto output = pManager->GetStdOutContent();
ASSERT_TRUE(pManager->GetStdOutContent(&straContent)); ASSERT_FALSE(output.empty());
ASSERT_STREQ(straContent.QueryStr(), expected); ASSERT_STREQ(output.c_str(), expected);
} }
} }
TEST(FileOutManagerOutputTest, StdErr) TEST(FileOutManagerOutputTest, StdErr)
{ {
PCSTR expected = "test"; PCWSTR expected = L"test";
auto tempDirectory = TempDirectory(); auto tempDirectory = TempDirectory();
FileOutputManager* pManager = new FileOutputManager; FileOutputManager* pManager = new FileOutputManager(L"", tempDirectory.path().c_str());
pManager->Initialize(L"", tempDirectory.path().c_str());
{ {
FileManagerWrapper wrapper(pManager); FileManagerWrapper wrapper(pManager);
fprintf(stderr, expected); fwprintf(stderr, expected);
pManager->Stop(); pManager->Stop();
STRA straContent; auto output = pManager->GetStdOutContent();
ASSERT_TRUE(pManager->GetStdOutContent(&straContent)); ASSERT_FALSE(output.empty());
ASSERT_STREQ(straContent.QueryStr(), expected); ASSERT_STREQ(output.c_str(), expected);
} }
} }
TEST(FileOutManagerOutputTest, CapAt30KB) TEST(FileOutManagerOutputTest, CapAt30KB)
{ {
PCSTR expected = "hello world"; PCWSTR expected = L"hello world";
auto tempDirectory = TempDirectory(); auto tempDirectory = TempDirectory();
FileOutputManager* pManager = new FileOutputManager; FileOutputManager* pManager = new FileOutputManager(L"", tempDirectory.path());
pManager->Initialize(L"", tempDirectory.path().c_str());
{ {
FileManagerWrapper wrapper(pManager); FileManagerWrapper wrapper(pManager);
for (int i = 0; i < 3000; i++) for (int i = 0; i < 3000; i++)
{ {
printf(expected); wprintf(expected);
} }
pManager->Stop(); pManager->Stop();
STRA straContent; auto output = pManager->GetStdOutContent();
ASSERT_TRUE(pManager->GetStdOutContent(&straContent)); ASSERT_FALSE(output.empty());
ASSERT_EQ(straContent.QueryCCH(), 30000); ASSERT_EQ(output.size(), 30000);
} }
} }
TEST(FileOutManagerOutputTest, StartStopRestoresCorrectly) TEST(FileOutManagerOutputTest, StartStopRestoresCorrectly)
{ {
PCSTR expected = "test"; PCWSTR expected = L"test";
auto tempDirectory = TempDirectory(); auto tempDirectory = TempDirectory();
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
FileOutputManager* pManager = new FileOutputManager; FileOutputManager* pManager = new FileOutputManager(L"", tempDirectory.path());
pManager->Initialize(L"", tempDirectory.path().c_str());
{ {
FileManagerWrapper wrapper(pManager); FileManagerWrapper wrapper(pManager);
printf(expected); wprintf(expected);
pManager->Stop(); pManager->Stop();
STRA straContent; auto output = pManager->GetStdOutContent();
ASSERT_TRUE(pManager->GetStdOutContent(&straContent)); ASSERT_FALSE(output.empty());
ASSERT_STREQ(straContent.QueryStr(), expected); ASSERT_STREQ(output.c_str(), expected);
} }
} }
} }

View File

@ -26,40 +26,49 @@ namespace PipeOutputManagerTests
TEST(PipeManagerOutputTest, StdOut) TEST(PipeManagerOutputTest, StdOut)
{ {
PCWSTR expected = L"test"; PCWSTR expected = L"test";
STRA output;
PipeOutputManager* pManager = new PipeOutputManager(true); PipeOutputManager* pManager = new PipeOutputManager(true);
ASSERT_EQ(S_OK, pManager->Start()); pManager->Start();
fwprintf(stdout, expected); fwprintf(stdout, expected);
pManager->Stop();
ASSERT_EQ(S_OK, pManager->Stop()); auto output = pManager->GetStdOutContent();
ASSERT_STREQ(output.c_str(), expected);
delete pManager;
}
pManager->GetStdOutContent(&output); TEST(PipeManagerOutputTest, StdOutMultiToWide)
ASSERT_STREQ(output.QueryStr(), "test"); {
PipeOutputManager* pManager = new PipeOutputManager(true);
pManager->Start();
fprintf(stdout, "test");
pManager->Stop();
auto output = pManager->GetStdOutContent();
ASSERT_STREQ(output.c_str(), L"test");
delete pManager; delete pManager;
} }
TEST(PipeManagerOutputTest, StdErr) TEST(PipeManagerOutputTest, StdErr)
{ {
PCWSTR expected = L"test"; PCWSTR expected = L"test";
STRA output;
PipeOutputManager* pManager = new PipeOutputManager(); PipeOutputManager* pManager = new PipeOutputManager();
ASSERT_EQ(S_OK, pManager->Start()); pManager->Start();
fwprintf(stderr, expected); fwprintf(stderr, expected);
ASSERT_EQ(S_OK, pManager->Stop()); pManager->Stop();
pManager->GetStdOutContent(&output); auto output = pManager->GetStdOutContent();
ASSERT_STREQ(output.QueryStr(), "test"); ASSERT_STREQ(output.c_str(), expected);
delete pManager; delete pManager;
} }
TEST(PipeManagerOutputTest, CheckMaxPipeSize) TEST(PipeManagerOutputTest, CheckMaxPipeSize)
{ {
std::wstring test; std::wstring test;
STRA output;
for (int i = 0; i < 3000; i++) for (int i = 0; i < 3000; i++)
{ {
test.append(L"hello world"); test.append(L"hello world");
@ -67,14 +76,15 @@ namespace PipeOutputManagerTests
PipeOutputManager* pManager = new PipeOutputManager(); PipeOutputManager* pManager = new PipeOutputManager();
ASSERT_EQ(S_OK, pManager->Start()); pManager->Start();
wprintf(test.c_str()); wprintf(test.c_str());
ASSERT_EQ(S_OK, pManager->Stop()); pManager->Stop();
pManager->GetStdOutContent(&output); auto output = pManager->GetStdOutContent();
ASSERT_EQ(output.QueryCCH(), (DWORD)30000); ASSERT_EQ(output.size(), (DWORD)30000);
delete pManager; delete pManager;
} }
TEST(PipeManagerOutputTest, SetInvalidHandlesForErrAndOut) TEST(PipeManagerOutputTest, SetInvalidHandlesForErrAndOut)
{ {
auto m_fdPreviousStdOut = _dup(_fileno(stdout)); auto m_fdPreviousStdOut = _dup(_fileno(stdout));
@ -86,7 +96,7 @@ namespace PipeOutputManagerTests
PCWSTR expected = L"test"; PCWSTR expected = L"test";
PipeOutputManager* pManager = new PipeOutputManager(); PipeOutputManager* pManager = new PipeOutputManager();
ASSERT_EQ(S_OK, pManager->Start()); pManager->Start();
_dup2(m_fdPreviousStdOut, _fileno(stdout)); _dup2(m_fdPreviousStdOut, _fileno(stdout));
_dup2(m_fdPreviousStdErr, _fileno(stderr)); _dup2(m_fdPreviousStdErr, _fileno(stderr));
@ -104,17 +114,16 @@ namespace PipeOutputManagerTests
auto stdoutBefore = _fileno(stdout); auto stdoutBefore = _fileno(stdout);
auto stderrBefore = _fileno(stderr); auto stderrBefore = _fileno(stderr);
PCWSTR expected = L"test"; PCWSTR expected = L"test";
STRA output;
PipeOutputManager* pManager = new PipeOutputManager(); PipeOutputManager* pManager = new PipeOutputManager();
ASSERT_EQ(S_OK, pManager->Start()); pManager->Start();
fwprintf(stdout, expected); fwprintf(stdout, expected);
ASSERT_EQ(S_OK, pManager->Stop()); pManager->Stop();
pManager->GetStdOutContent(&output); auto output = pManager->GetStdOutContent();
ASSERT_STREQ(output.QueryStr(), "test"); ASSERT_STREQ(output.c_str(), expected);
ASSERT_EQ(stdoutBefore, _fileno(stdout)); ASSERT_EQ(stdoutBefore, _fileno(stdout));
ASSERT_EQ(stderrBefore, _fileno(stderr)); ASSERT_EQ(stderrBefore, _fileno(stderr));
delete pManager; delete pManager;
@ -131,16 +140,15 @@ namespace PipeOutputManagerTests
auto stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); auto stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
auto stderrHandle = GetStdHandle(STD_ERROR_HANDLE); auto stderrHandle = GetStdHandle(STD_ERROR_HANDLE);
PCWSTR expected = L"test"; PCWSTR expected = L"test";
STRA output;
PipeOutputManager* pManager = new PipeOutputManager(); PipeOutputManager* pManager = new PipeOutputManager();
ASSERT_EQ(S_OK, pManager->Start()); pManager->Start();
fwprintf(stderr, expected); fwprintf(stderr, expected);
ASSERT_EQ(S_OK, pManager->Stop()); pManager->Stop();
pManager->GetStdOutContent(&output); auto output = pManager->GetStdOutContent();
ASSERT_STREQ(output.QueryStr(), "test"); ASSERT_STREQ(output.c_str(), expected);
ASSERT_EQ(stdoutBefore, _fileno(stdout)); ASSERT_EQ(stdoutBefore, _fileno(stdout));
ASSERT_EQ(stderrBefore, _fileno(stderr)); ASSERT_EQ(stderrBefore, _fileno(stderr));

View File

@ -21,10 +21,10 @@ namespace IISTestSite
{ {
// Semicolons are appended to env variables; removing them. // Semicolons are appended to env variables; removing them.
case "CheckLargeStdOutWrites": case "CheckLargeStdOutWrites":
Console.WriteLine(new string('a', 4096)); Console.WriteLine(new string('a', 30000));
break; break;
case "CheckLargeStdErrWrites": case "CheckLargeStdErrWrites":
Console.Error.WriteLine(new string('a', 4096)); Console.Error.WriteLine(new string('a', 30000));
Console.Error.Flush(); Console.Error.Flush();
break; break;
case "CheckLogFile": case "CheckLogFile":
@ -35,17 +35,16 @@ namespace IISTestSite
Console.Error.Flush(); Console.Error.Flush();
break; break;
case "CheckOversizedStdErrWrites": case "CheckOversizedStdErrWrites":
Console.WriteLine(new string('a', 5000)); Console.WriteLine(new string('a', 31000));
break; break;
case "CheckOversizedStdOutWrites": case "CheckOversizedStdOutWrites":
Console.Error.WriteLine(new string('a', 4096)); Console.Error.WriteLine(new string('a', 31000));
Console.Error.Flush(); Console.Error.Flush();
break; break;
case "Hang": case "Hang":
Thread.Sleep(Timeout.Infinite); Thread.Sleep(Timeout.Infinite);
break; break;
case "HangOnStop": case "HangOnStop":
var host = new WebHostBuilder() var host = new WebHostBuilder()
.UseIIS() .UseIIS()
.UseStartup<Startup>() .UseStartup<Startup>()
@ -60,6 +59,7 @@ namespace IISTestSite
Console.Title = "Test"; Console.Title = "Test";
Console.WriteLine($"Is Console redirection: {Console.IsOutputRedirected}"); Console.WriteLine($"Is Console redirection: {Console.IsOutputRedirected}");
Console.BackgroundColor = ConsoleColor.Blue; Console.BackgroundColor = ConsoleColor.Blue;
Console.WriteLine("彡⾔");
break; break;
} }