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:
commit
ca1c0ea354
|
|
@ -10,7 +10,7 @@
|
|||
class AppOfflineApplication: public PollingAppOfflineApplication
|
||||
{
|
||||
public:
|
||||
AppOfflineApplication(IHttpApplication& pApplication)
|
||||
AppOfflineApplication(const IHttpApplication& pApplication)
|
||||
: PollingAppOfflineApplication(pApplication, PollingAppOfflineApplicationMode::StopWhenRemoved)
|
||||
{
|
||||
CheckAppOffline();
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ typedef
|
|||
HRESULT
|
||||
(WINAPI * PFN_ASPNETCORE_CREATE_APPLICATION)(
|
||||
_In_ IHttpServer *pServer,
|
||||
_In_ IHttpApplication *pHttpApplication,
|
||||
_In_ const IHttpApplication * pHttpApplication,
|
||||
_In_ APPLICATION_PARAMETER *pParameters,
|
||||
_In_ DWORD nParameters,
|
||||
_Out_ IAPPLICATION **pApplication
|
||||
|
|
@ -31,7 +31,7 @@ public:
|
|||
|
||||
HRESULT Execute(
|
||||
_In_ IHttpServer *pServer,
|
||||
_In_ IHttpApplication *pHttpApplication,
|
||||
_In_ const IHttpApplication *pHttpApplication,
|
||||
_Outptr_ IAPPLICATION **pApplication) const noexcept
|
||||
{
|
||||
std::array<APPLICATION_PARAMETER, 1> parameters {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include "ConfigurationLoadException.h"
|
||||
#include "WebConfigConfigurationSource.h"
|
||||
#include "ModuleHelpers.h"
|
||||
#include "BaseOutputManager.h"
|
||||
|
||||
const PCWSTR HandlerResolver::s_pwzAspnetcoreInProcessRequestHandlerName = L"aspnetcorev2_inprocess.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)
|
||||
{
|
||||
std::unique_ptr<HOSTFXR_OPTIONS> options;
|
||||
std::unique_ptr<IOutputManager> outputManager;
|
||||
std::unique_ptr<BaseOutputManager> outputManager;
|
||||
|
||||
RETURN_IF_FAILED(HOSTFXR_OPTIONS::Create(
|
||||
L"",
|
||||
|
|
@ -72,28 +73,21 @@ HandlerResolver::LoadRequestHandlerAssembly(const IHttpApplication &pApplication
|
|||
pApplication.GetApplicationPhysicalPath(),
|
||||
outputManager));
|
||||
|
||||
outputManager->Start();
|
||||
outputManager->TryStartRedirection();
|
||||
|
||||
hr = FindNativeAssemblyFromHostfxr(*options.get(), pstrHandlerDllName, handlerDllPath);
|
||||
outputManager->Stop();
|
||||
|
||||
outputManager->TryStopRedirection();
|
||||
|
||||
if (FAILED(hr) && m_hHostFxrDll != nullptr)
|
||||
{
|
||||
STRA content;
|
||||
STRU struStdMsg;
|
||||
|
||||
outputManager->GetStdOutContent(&content);
|
||||
if (content.QueryCCH() > 0)
|
||||
{
|
||||
struStdMsg.CopyA(content.QueryStr());
|
||||
}
|
||||
auto output = outputManager->GetStdOutContent();
|
||||
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_GENERAL_ERROR,
|
||||
ASPNETCORE_EVENT_INPROCESS_RH_ERROR_MSG,
|
||||
handlerDllPath.empty()? s_pwzAspnetcoreInProcessRequestHandlerName : handlerDllPath.c_str(),
|
||||
struStdMsg.QueryStr());
|
||||
|
||||
output.c_str());
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -8,15 +8,20 @@
|
|||
#include "HandleWrapper.h"
|
||||
#include "exceptions.h"
|
||||
|
||||
APPLICATION_STATUS PollingAppOfflineApplication::QueryStatus()
|
||||
HRESULT PollingAppOfflineApplication::TryCreateHandler(_In_ IHttpContext* pHttpContext, _Outptr_result_maybenull_ IREQUEST_HANDLER** pRequestHandler)
|
||||
{
|
||||
CheckAppOffline();
|
||||
return APPLICATION::QueryStatus();
|
||||
return LOG_IF_FAILED(APPLICATION::TryCreateHandler(pHttpContext, pRequestHandler));
|
||||
}
|
||||
|
||||
void
|
||||
PollingAppOfflineApplication::CheckAppOffline()
|
||||
{
|
||||
if (m_fStopCalled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto ulCurrentTime = GetTickCount64();
|
||||
//
|
||||
// we only care about app offline presented. If not, it means the application has started
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ enum PollingAppOfflineApplicationMode
|
|||
class PollingAppOfflineApplication: public APPLICATION
|
||||
{
|
||||
public:
|
||||
PollingAppOfflineApplication(IHttpApplication& pApplication, PollingAppOfflineApplicationMode mode)
|
||||
PollingAppOfflineApplication(const IHttpApplication& pApplication, PollingAppOfflineApplicationMode mode)
|
||||
: APPLICATION(pApplication),
|
||||
m_ulLastCheckTime(0),
|
||||
m_appOfflineLocation(GetAppOfflineLocation(pApplication)),
|
||||
|
|
@ -23,8 +23,12 @@ public:
|
|||
{
|
||||
InitializeSRWLock(&m_statusLock);
|
||||
}
|
||||
|
||||
HRESULT
|
||||
TryCreateHandler(
|
||||
_In_ IHttpContext *pHttpContext,
|
||||
_Outptr_result_maybenull_ IREQUEST_HANDLER **pRequestHandler) override;
|
||||
|
||||
APPLICATION_STATUS QueryStatus() override;
|
||||
void CheckAppOffline();
|
||||
virtual HRESULT OnAppOfflineFound() = 0;
|
||||
void StopInternal(bool fServerInitiated) override { UNREFERENCED_PARAMETER(fServerInitiated); }
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
class ServerErrorApplication : public PollingAppOfflineApplication
|
||||
{
|
||||
public:
|
||||
ServerErrorApplication(IHttpApplication& pApplication, HRESULT hr)
|
||||
ServerErrorApplication(const IHttpApplication& pApplication, HRESULT hr)
|
||||
: m_HR(hr),
|
||||
PollingAppOfflineApplication(pApplication, PollingAppOfflineApplicationMode::StopWhenAdded)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,52 +14,77 @@
|
|||
#include "AppOfflineApplication.h"
|
||||
|
||||
HRESULT
|
||||
APPLICATION_INFO::GetOrCreateApplication(
|
||||
APPLICATION_INFO::CreateHandler(
|
||||
IHttpContext& pHttpContext,
|
||||
std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER>& pApplication
|
||||
std::unique_ptr<IREQUEST_HANDLER, IREQUEST_HANDLER_DELETER>& pHandler
|
||||
)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
SRWExclusiveLock lock(m_applicationLock);
|
||||
|
||||
auto& httpApplication = *pHttpContext.GetApplication();
|
||||
|
||||
if (m_pApplication != nullptr)
|
||||
{
|
||||
if (m_pApplication->QueryStatus() == RECYCLED)
|
||||
{
|
||||
LOG_INFO(L"Application went offline");
|
||||
SRWSharedLock lock(m_applicationLock);
|
||||
|
||||
RETURN_IF_FAILED(hr = TryCreateHandler(pHttpContext, pHandler));
|
||||
|
||||
// Call to wait for application to complete stopping
|
||||
m_pApplication->Stop(/* fServerInitiated */ false);
|
||||
m_pApplication = nullptr;
|
||||
m_pApplicationFactory = nullptr;
|
||||
}
|
||||
else
|
||||
if (hr == S_OK)
|
||||
{
|
||||
// another thread created the application
|
||||
FINISHED(S_OK);
|
||||
return 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");
|
||||
#pragma warning( push )
|
||||
#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 )
|
||||
}
|
||||
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");
|
||||
IAPPLICATION * newApplication;
|
||||
FINISHED_IF_FAILED(m_pApplicationFactory->Execute(
|
||||
&m_pServer,
|
||||
&httpApplication,
|
||||
&pHttpApplication,
|
||||
&newApplication));
|
||||
|
||||
m_pApplication.reset(newApplication);
|
||||
|
|
@ -67,29 +92,44 @@ APPLICATION_INFO::GetOrCreateApplication(
|
|||
|
||||
Finished:
|
||||
|
||||
if (FAILED(hr))
|
||||
if (m_pApplication == nullptr || FAILED(hr))
|
||||
{
|
||||
// Log the failure and update application info to not try again
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_ADD_APPLICATION_ERROR,
|
||||
ASPNETCORE_EVENT_ADD_APPLICATION_ERROR_MSG,
|
||||
httpApplication.GetApplicationId(),
|
||||
pHttpApplication.GetApplicationId(),
|
||||
hr);
|
||||
|
||||
#pragma warning( push )
|
||||
#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 )
|
||||
}
|
||||
|
||||
if (m_pApplication)
|
||||
{
|
||||
pApplication = ReferenceApplication(m_pApplication.get());
|
||||
}
|
||||
|
||||
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
|
||||
APPLICATION_INFO::ShutDownApplication(bool fServerInitiated)
|
||||
|
|
|
|||
|
|
@ -32,13 +32,13 @@ public:
|
|||
~APPLICATION_INFO() = default;
|
||||
|
||||
const std::wstring&
|
||||
QueryApplicationInfoKey() const noexcept
|
||||
QueryApplicationInfoKey() noexcept
|
||||
{
|
||||
return m_strInfoKey;
|
||||
}
|
||||
|
||||
const std::wstring&
|
||||
QueryConfigPath() const noexcept
|
||||
QueryConfigPath() noexcept
|
||||
{
|
||||
return m_strConfigPath;
|
||||
}
|
||||
|
|
@ -47,11 +47,10 @@ public:
|
|||
ShutDownApplication(bool fServerInitiated);
|
||||
|
||||
HRESULT
|
||||
GetOrCreateApplication(
|
||||
CreateHandler(
|
||||
IHttpContext& pHttpContext,
|
||||
std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER>& pApplication
|
||||
);
|
||||
|
||||
std::unique_ptr<IREQUEST_HANDLER, IREQUEST_HANDLER_DELETER>& pHandler);
|
||||
|
||||
bool ConfigurationPathApplies(const std::wstring& path)
|
||||
{
|
||||
// We need to check that the last character of the config path
|
||||
|
|
@ -68,6 +67,15 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
|
||||
HRESULT
|
||||
TryCreateHandler(
|
||||
IHttpContext& pHttpContext,
|
||||
std::unique_ptr<IREQUEST_HANDLER, IREQUEST_HANDLER_DELETER>& pHandler);
|
||||
|
||||
HRESULT
|
||||
CreateApplication(const IHttpApplication& pHttpApplication);
|
||||
|
||||
IHttpServer &m_pServer;
|
||||
HandlerResolver &m_handlerResolver;
|
||||
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ HRESULT
|
|||
{
|
||||
g_hEventLog = RegisterEventSource(nullptr, ASPNETCORE_EVENT_PROVIDER);
|
||||
}
|
||||
|
||||
|
||||
// check whether the feature is disabled due to security reason
|
||||
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
||||
L"SOFTWARE\\Microsoft\\IIS Extensions\\IIS AspNetCore Module V2\\Parameters",
|
||||
|
|
|
|||
|
|
@ -87,13 +87,7 @@ ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler(
|
|||
*pHttpContext,
|
||||
m_pApplicationInfo));
|
||||
|
||||
std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER> pApplication;
|
||||
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);
|
||||
FINISHED_IF_FAILED(m_pApplicationInfo->CreateHandler(*pHttpContext, m_pHandler));
|
||||
|
||||
retVal = m_pHandler->OnExecuteRequestHandler();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -199,6 +199,7 @@
|
|||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="application.h" />
|
||||
<ClInclude Include="baseoutputmanager.h" />
|
||||
<ClInclude Include="ConfigurationSection.h" />
|
||||
<ClInclude Include="ConfigurationSource.h" />
|
||||
<ClInclude Include="config_utility.h" />
|
||||
|
|
|
|||
|
|
@ -9,18 +9,18 @@
|
|||
#include "SRWExclusiveLock.h"
|
||||
#include "file_utility.h"
|
||||
#include "StdWrapper.h"
|
||||
#include "StringHelpers.h"
|
||||
|
||||
extern HINSTANCE g_hModule;
|
||||
|
||||
FileOutputManager::FileOutputManager() :
|
||||
FileOutputManager(/* fEnableNativeLogging */ true) { }
|
||||
FileOutputManager::FileOutputManager(std::wstring pwzStdOutLogFileName, std::wstring pwzApplicationPath) :
|
||||
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_disposed(false),
|
||||
stdoutWrapper(nullptr),
|
||||
stderrWrapper(nullptr),
|
||||
m_fEnableNativeRedirection(fEnableNativeLogging)
|
||||
m_applicationPath(pwzApplicationPath),
|
||||
m_stdOutLogFileName(pwzStdOutLogFileName)
|
||||
{
|
||||
InitializeSRWLock(&m_srwLock);
|
||||
}
|
||||
|
|
@ -30,23 +30,13 @@ FileOutputManager::~FileOutputManager()
|
|||
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.
|
||||
// Uses sttimer to continuously flush output into the file.
|
||||
HRESULT
|
||||
void
|
||||
FileOutputManager::Start()
|
||||
{
|
||||
SYSTEMTIME systemTime;
|
||||
SECURITY_ATTRIBUTES saAttr = { 0 };
|
||||
STRU struPath;
|
||||
FILETIME processCreationTime;
|
||||
FILETIME dummyFileTime;
|
||||
|
||||
|
|
@ -54,48 +44,38 @@ FileOutputManager::Start()
|
|||
// in the current process.
|
||||
if (!AllocConsole())
|
||||
{
|
||||
// ERROR_ACCESS_DENIED means there is a console already present.
|
||||
if (GetLastError() != ERROR_ACCESS_DENIED)
|
||||
{
|
||||
RETURN_LAST_ERROR();
|
||||
}
|
||||
THROW_LAST_ERROR_IF(GetLastError() != ERROR_ACCESS_DENIED);
|
||||
}
|
||||
|
||||
// Concatenate the log file name and application path
|
||||
RETURN_IF_FAILED(FILE_UTILITY::ConvertPathToFullPath(
|
||||
m_wsStdOutLogFileName.QueryStr(),
|
||||
m_wsApplicationPath.QueryStr(),
|
||||
&struPath));
|
||||
auto logPath = m_applicationPath / m_stdOutLogFileName;
|
||||
create_directories(logPath.parent_path());
|
||||
|
||||
RETURN_IF_FAILED(FILE_UTILITY::EnsureDirectoryPathExist(struPath.QueryStr()));
|
||||
|
||||
|
||||
// TODO fix string as it is incorrect
|
||||
RETURN_LAST_ERROR_IF(!GetProcessTimes(
|
||||
THROW_LAST_ERROR_IF(!GetProcessTimes(
|
||||
GetCurrentProcess(),
|
||||
&processCreationTime,
|
||||
&dummyFileTime,
|
||||
&dummyFileTime,
|
||||
&dummyFileTime));
|
||||
RETURN_LAST_ERROR_IF(!FileTimeToSystemTime(&processCreationTime, &systemTime));
|
||||
|
||||
RETURN_IF_FAILED(
|
||||
m_struLogFilePath.SafeSnwprintf(L"%s_%d%02d%02d%02d%02d%02d_%d.log",
|
||||
struPath.QueryStr(),
|
||||
systemTime.wYear,
|
||||
systemTime.wMonth,
|
||||
systemTime.wDay,
|
||||
systemTime.wHour,
|
||||
systemTime.wMinute,
|
||||
systemTime.wSecond,
|
||||
GetCurrentProcessId()));
|
||||
THROW_LAST_ERROR_IF(!FileTimeToSystemTime(&processCreationTime, &systemTime));
|
||||
|
||||
m_logFilePath = format(L"%s_%d%02d%02d%02d%02d%02d_%d.log",
|
||||
logPath.c_str(),
|
||||
systemTime.wYear,
|
||||
systemTime.wMonth,
|
||||
systemTime.wDay,
|
||||
systemTime.wHour,
|
||||
systemTime.wMinute,
|
||||
systemTime.wSecond,
|
||||
GetCurrentProcessId());
|
||||
|
||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
saAttr.bInheritHandle = TRUE;
|
||||
saAttr.lpSecurityDescriptor = NULL;
|
||||
|
||||
// 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_SHARE_READ,
|
||||
&saAttr,
|
||||
|
|
@ -103,22 +83,18 @@ FileOutputManager::Start()
|
|||
FILE_ATTRIBUTE_NORMAL,
|
||||
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);
|
||||
stderrWrapper = std::make_unique<StdWrapper>(stderr, STD_ERROR_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_enableNativeRedirection);
|
||||
|
||||
stdoutWrapper->StartRedirection();
|
||||
stderrWrapper->StartRedirection();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT
|
||||
void
|
||||
FileOutputManager::Stop()
|
||||
{
|
||||
STRA straStdOutput;
|
||||
CHAR pzFileContents[MAX_FILE_READ_SIZE] = { 0 };
|
||||
DWORD dwNumBytesRead;
|
||||
LARGE_INTEGER li = { 0 };
|
||||
|
|
@ -128,75 +104,77 @@ FileOutputManager::Stop()
|
|||
|
||||
if (m_disposed)
|
||||
{
|
||||
return S_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
SRWExclusiveLock lock(m_srwLock);
|
||||
|
||||
if (m_disposed)
|
||||
{
|
||||
return S_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
m_disposed = true;
|
||||
|
||||
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);
|
||||
|
||||
if (stdoutWrapper != nullptr)
|
||||
{
|
||||
RETURN_IF_FAILED(stdoutWrapper->StopRedirection());
|
||||
THROW_IF_FAILED(stdoutWrapper->StopRedirection());
|
||||
}
|
||||
|
||||
if (stderrWrapper != nullptr)
|
||||
{
|
||||
RETURN_IF_FAILED(stderrWrapper->StopRedirection());
|
||||
THROW_IF_FAILED(stderrWrapper->StopRedirection());
|
||||
}
|
||||
|
||||
// delete empty log file
|
||||
handle = FindFirstFile(m_struLogFilePath.QueryStr(), &fileData);
|
||||
handle = FindFirstFile(m_logFilePath.c_str(), &fileData);
|
||||
if (handle != INVALID_HANDLE_VALUE &&
|
||||
handle != NULL &&
|
||||
fileData.nFileSizeHigh == 0 &&
|
||||
fileData.nFileSizeLow == 0) // skip check of nFileSizeHigh
|
||||
{
|
||||
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.
|
||||
// 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);
|
||||
|
||||
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
|
||||
if (printf(m_straFileContent.QueryStr()) != -1)
|
||||
auto content = GetStdOutContent();
|
||||
if (!content.empty())
|
||||
{
|
||||
// Need to flush contents for the new stdout and stderr
|
||||
_flushall();
|
||||
// printf will fail in in full IIS
|
||||
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_straFileContent.QueryCCH() > 0;
|
||||
return m_stdOutContent;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,38 +4,27 @@
|
|||
#pragma once
|
||||
|
||||
#include "sttimer.h"
|
||||
#include "IOutputManager.h"
|
||||
#include "HandleWrapper.h"
|
||||
#include "StdWrapper.h"
|
||||
#include "stringa.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
|
||||
public:
|
||||
FileOutputManager();
|
||||
FileOutputManager(bool fEnableNativeLogging);
|
||||
FileOutputManager(std::wstring pwzApplicationPath, std::wstring pwzStdOutLogFileName);
|
||||
FileOutputManager(std::wstring pwzApplicationPath, std::wstring pwzStdOutLogFileName, bool fEnableNativeLogging);
|
||||
~FileOutputManager();
|
||||
|
||||
HRESULT
|
||||
Initialize(PCWSTR pwzStdOutLogFileName, PCWSTR pwzApplciationpath);
|
||||
|
||||
virtual bool GetStdOutContent(STRA* struStdOutput) override;
|
||||
virtual HRESULT Start() override;
|
||||
virtual HRESULT Stop() override;
|
||||
virtual std::wstring GetStdOutContent() override;
|
||||
void Start() override;
|
||||
void Stop() override;
|
||||
|
||||
private:
|
||||
HandleWrapper<InvalidHandleTraits> m_hLogFileHandle;
|
||||
STTIMER m_Timer;
|
||||
STRU m_wsStdOutLogFileName;
|
||||
STRU m_wsApplicationPath;
|
||||
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;
|
||||
std::wstring m_stdOutLogFileName;
|
||||
std::filesystem::path m_applicationPath;
|
||||
std::filesystem::path m_logFilePath;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,18 +10,18 @@ class IOutputManager
|
|||
{
|
||||
public:
|
||||
virtual
|
||||
HRESULT
|
||||
void
|
||||
Start() = 0;
|
||||
|
||||
virtual
|
||||
~IOutputManager() = default;
|
||||
|
||||
virtual
|
||||
bool
|
||||
GetStdOutContent(STRA* struStdOutput) = 0;
|
||||
std::wstring
|
||||
GetStdOutContent() = 0;
|
||||
|
||||
virtual
|
||||
HRESULT
|
||||
void
|
||||
Stop() = 0;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include "stdafx.h"
|
||||
#include "LoggingHelpers.h"
|
||||
#include "IOutputManager.h"
|
||||
#include "FileOutputManager.h"
|
||||
#include "PipeOutputManager.h"
|
||||
#include "NullOutputManager.h"
|
||||
|
|
@ -11,6 +10,9 @@
|
|||
#include <Windows.h>
|
||||
#include <io.h>
|
||||
#include "ntassert.h"
|
||||
#include "exceptions.h"
|
||||
#include "EventLog.h"
|
||||
#include "BaseOutputManager.h"
|
||||
|
||||
HRESULT
|
||||
LoggingHelpers::CreateLoggingProvider(
|
||||
|
|
@ -18,7 +20,7 @@ LoggingHelpers::CreateLoggingProvider(
|
|||
bool fEnableNativeLogging,
|
||||
PCWSTR pwzStdOutFileName,
|
||||
PCWSTR pwzApplicationPath,
|
||||
std::unique_ptr<IOutputManager>& outputManager
|
||||
std::unique_ptr<BaseOutputManager>& outputManager
|
||||
)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
|
@ -34,8 +36,7 @@ LoggingHelpers::CreateLoggingProvider(
|
|||
|
||||
if (fIsLoggingEnabled)
|
||||
{
|
||||
auto manager = std::make_unique<FileOutputManager>(fEnableNativeLogging);
|
||||
hr = manager->Initialize(pwzStdOutFileName, pwzApplicationPath);
|
||||
auto manager = std::make_unique<FileOutputManager>(pwzStdOutFileName, pwzApplicationPath, fEnableNativeLogging);
|
||||
outputManager = std::move(manager);
|
||||
}
|
||||
else if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &dummy))
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "IOutputManager.h"
|
||||
#include "BaseOutputManager.h"
|
||||
|
||||
class LoggingHelpers
|
||||
{
|
||||
|
|
@ -16,7 +16,7 @@ public:
|
|||
bool fEnableNativeLogging,
|
||||
PCWSTR pwzStdOutFileName,
|
||||
PCWSTR pwzApplicationPath,
|
||||
std::unique_ptr<IOutputManager>& outputManager
|
||||
std::unique_ptr<BaseOutputManager>& outputManager
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include "stdafx.h"
|
||||
|
||||
class NullOutputManager : public IOutputManager
|
||||
class NullOutputManager : public BaseOutputManager
|
||||
{
|
||||
public:
|
||||
|
||||
|
|
@ -13,19 +13,17 @@ public:
|
|||
|
||||
~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"";
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "PipeOutputManager.h"
|
||||
#include "exceptions.h"
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "Exceptions.h"
|
||||
#include "SRWExclusiveLock.h"
|
||||
#include "StdWrapper.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_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) :
|
||||
BaseOutputManager(fEnableNativeLogging),
|
||||
m_hErrReadPipe(INVALID_HANDLE_VALUE),
|
||||
m_hErrWritePipe(INVALID_HANDLE_VALUE),
|
||||
m_hErrThread(nullptr),
|
||||
m_dwStdErrReadTotal(0),
|
||||
m_disposed(FALSE),
|
||||
m_fEnableNativeRedirection(fEnableNativeLogging),
|
||||
stdoutWrapper(nullptr),
|
||||
stderrWrapper(nullptr)
|
||||
m_numBytesReadTotal(0)
|
||||
{
|
||||
InitializeSRWLock(&m_srwLock);
|
||||
}
|
||||
|
||||
PipeOutputManager::~PipeOutputManager()
|
||||
|
|
@ -37,7 +35,7 @@ PipeOutputManager::~PipeOutputManager()
|
|||
// Start redirecting stdout and stderr into a pipe
|
||||
// Continuously read the pipe on a background thread
|
||||
// until Stop is called.
|
||||
HRESULT PipeOutputManager::Start()
|
||||
void PipeOutputManager::Start()
|
||||
{
|
||||
SECURITY_ATTRIBUTES saAttr = { 0 };
|
||||
HANDLE hStdErrReadPipe;
|
||||
|
|
@ -50,17 +48,17 @@ HRESULT PipeOutputManager::Start()
|
|||
// ERROR_ACCESS_DENIED means there is a console already present.
|
||||
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_hErrWritePipe = hStdErrWritePipe;
|
||||
|
||||
stdoutWrapper = std::make_unique<StdWrapper>(stdout, STD_OUTPUT_HANDLE, hStdErrWritePipe, m_fEnableNativeRedirection);
|
||||
stderrWrapper = std::make_unique<StdWrapper>(stderr, STD_ERROR_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_enableNativeRedirection);
|
||||
|
||||
LOG_IF_FAILED(stdoutWrapper->StartRedirection());
|
||||
LOG_IF_FAILED(stderrWrapper->StartRedirection());
|
||||
|
|
@ -74,9 +72,7 @@ HRESULT PipeOutputManager::Start()
|
|||
0, // default creation flags
|
||||
nullptr); // receive thread identifier
|
||||
|
||||
RETURN_LAST_ERROR_IF_NULL(m_hErrThread);
|
||||
|
||||
return S_OK;
|
||||
THROW_LAST_ERROR_IF_NULL(m_hErrThread);
|
||||
}
|
||||
|
||||
// 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.
|
||||
// If more than 30Kb was written to the pipe, that output will
|
||||
// be thrown away.
|
||||
HRESULT PipeOutputManager::Stop()
|
||||
void PipeOutputManager::Stop()
|
||||
{
|
||||
DWORD dwThreadStatus = 0;
|
||||
STRA straStdOutput;
|
||||
|
||||
if (m_disposed)
|
||||
{
|
||||
return S_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
SRWExclusiveLock lock(m_srwLock);
|
||||
|
||||
if (m_disposed)
|
||||
{
|
||||
return S_OK;
|
||||
return;
|
||||
}
|
||||
|
||||
m_disposed = true;
|
||||
|
|
@ -108,7 +103,7 @@ HRESULT PipeOutputManager::Stop()
|
|||
if (m_hErrWritePipe != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
// 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);
|
||||
m_hErrWritePipe = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
|
@ -163,33 +158,22 @@ HRESULT PipeOutputManager::Stop()
|
|||
|
||||
// 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
|
||||
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
|
||||
if (printf(straStdOutput.QueryStr()) != -1)
|
||||
if (wprintf(m_stdOutContent.c_str()) != -1)
|
||||
{
|
||||
// Need to flush contents for the new stdout and stderr
|
||||
_flushall();
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
bool PipeOutputManager::GetStdOutContent(STRA* straStdOutput)
|
||||
std::wstring PipeOutputManager::GetStdOutContent()
|
||||
{
|
||||
bool fLogged = false;
|
||||
|
||||
// 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;
|
||||
return m_stdOutContent;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -211,13 +195,13 @@ PipeOutputManager::ReadStdErrHandleInternal()
|
|||
{
|
||||
// Fill a maximum of MAX_PIPE_READ_SIZE into a buffer.
|
||||
if (ReadFile(m_hErrReadPipe,
|
||||
&m_pzFileContents[m_dwStdErrReadTotal],
|
||||
MAX_PIPE_READ_SIZE - m_dwStdErrReadTotal,
|
||||
&m_pipeContents[m_numBytesReadTotal],
|
||||
MAX_PIPE_READ_SIZE - m_numBytesReadTotal,
|
||||
&dwNumBytesRead,
|
||||
nullptr))
|
||||
{
|
||||
m_dwStdErrReadTotal += dwNumBytesRead;
|
||||
if (m_dwStdErrReadTotal >= MAX_PIPE_READ_SIZE)
|
||||
m_numBytesReadTotal += dwNumBytesRead;
|
||||
if (m_numBytesReadTotal >= MAX_PIPE_READ_SIZE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "IOutputManager.h"
|
||||
#include "StdWrapper.h"
|
||||
#include "stringu.h"
|
||||
#include "BaseOutputManager.h"
|
||||
|
||||
class PipeOutputManager : public IOutputManager
|
||||
class PipeOutputManager : public BaseOutputManager
|
||||
{
|
||||
// Timeout to be used if a thread never exits
|
||||
#define PIPE_OUTPUT_THREAD_TIMEOUT 2000
|
||||
|
|
@ -19,9 +19,9 @@ public:
|
|||
PipeOutputManager(bool fEnableNativeLogging);
|
||||
~PipeOutputManager();
|
||||
|
||||
HRESULT Start() override;
|
||||
HRESULT Stop() override;
|
||||
bool GetStdOutContent(STRA* straStdOutput) override;
|
||||
void Start() override;
|
||||
void Stop() override;
|
||||
std::wstring GetStdOutContent() override;
|
||||
|
||||
// Thread functions
|
||||
void ReadStdErrHandleInternal();
|
||||
|
|
@ -32,13 +32,7 @@ private:
|
|||
|
||||
HANDLE m_hErrReadPipe;
|
||||
HANDLE m_hErrWritePipe;
|
||||
STRU m_struLogFilePath;
|
||||
HANDLE m_hErrThread;
|
||||
CHAR m_pzFileContents[MAX_PIPE_READ_SIZE] = { 0 };
|
||||
DWORD m_dwStdErrReadTotal;
|
||||
SRWLOCK m_srwLock {};
|
||||
BOOL m_disposed;
|
||||
BOOL m_fEnableNativeRedirection;
|
||||
std::unique_ptr<StdWrapper> stdoutWrapper;
|
||||
std::unique_ptr<StdWrapper> stderrWrapper;
|
||||
CHAR m_pipeContents[MAX_PIPE_READ_SIZE] = { 0 };
|
||||
DWORD m_numBytesReadTotal;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ StdWrapper::StdWrapper(FILE* stdStream, DWORD stdHandleNumber, HANDLE handleToRe
|
|||
: m_previousFileDescriptor(0),
|
||||
m_stdStream(stdStream),
|
||||
m_stdHandleNumber(stdHandleNumber),
|
||||
m_fEnableNativeRedirection(fEnableNativeRedirection),
|
||||
m_enableNativeRedirection(fEnableNativeRedirection),
|
||||
m_handleToRedirectTo(handleToRedirectTo),
|
||||
m_redirectedFile(nullptr)
|
||||
{
|
||||
|
|
@ -48,7 +48,7 @@ StdWrapper::StartRedirection()
|
|||
m_previousFileDescriptor = _dup(_fileno(m_stdStream));
|
||||
}
|
||||
|
||||
if (!m_fEnableNativeRedirection)
|
||||
if (!m_enableNativeRedirection)
|
||||
{
|
||||
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))));
|
||||
|
||||
if (!m_fEnableNativeRedirection)
|
||||
if (!m_enableNativeRedirection)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ private:
|
|||
int m_previousFileDescriptor;
|
||||
FILE* m_stdStream;
|
||||
DWORD m_stdHandleNumber;
|
||||
BOOL m_fEnableNativeRedirection;
|
||||
BOOL m_enableNativeRedirection;
|
||||
HANDLE m_handleToRedirectTo;
|
||||
FILE* m_redirectedFile;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "StringHelpers.h"
|
||||
#include "exceptions.h"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ bool ends_with(const std::wstring &source, const std::wstring &suffix, bool igno
|
|||
[[nodiscard]]
|
||||
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>
|
||||
[[nodiscard]]
|
||||
std::wstring format(const std::wstring& format, Args ... args)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#include "iapplication.h"
|
||||
#include "ntassert.h"
|
||||
#include "SRWExclusiveLock.h"
|
||||
#include "SRWSharedLock.h"
|
||||
|
||||
class APPLICATION : public IAPPLICATION
|
||||
{
|
||||
|
|
@ -16,6 +15,26 @@ public:
|
|||
APPLICATION(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)
|
||||
: m_fStopCalled(false),
|
||||
|
|
@ -28,12 +47,6 @@ public:
|
|||
m_applicationVirtualPath = ToVirtualPath(m_applicationConfigPath);
|
||||
}
|
||||
|
||||
APPLICATION_STATUS
|
||||
QueryStatus() override
|
||||
{
|
||||
SRWSharedLock stateLock(m_stateLock);
|
||||
return m_fStopCalled ? APPLICATION_STATUS::RECYCLED : APPLICATION_STATUS::RUNNING;
|
||||
}
|
||||
|
||||
VOID
|
||||
Stop(bool fServerInitiated) override
|
||||
|
|
|
|||
|
|
@ -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_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_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)
|
||||
|
|
|
|||
|
|
@ -6,12 +6,6 @@
|
|||
#include <memory>
|
||||
#include "irequesthandler.h"
|
||||
|
||||
enum APPLICATION_STATUS
|
||||
{
|
||||
RUNNING,
|
||||
RECYCLED,
|
||||
};
|
||||
|
||||
struct APPLICATION_PARAMETER
|
||||
{
|
||||
LPCSTR pzName;
|
||||
|
|
@ -28,10 +22,6 @@ public:
|
|||
virtual
|
||||
~IAPPLICATION() = 0 { };
|
||||
|
||||
virtual
|
||||
APPLICATION_STATUS
|
||||
QueryStatus() = 0;
|
||||
|
||||
virtual
|
||||
VOID
|
||||
ReferenceApplication() = 0;
|
||||
|
|
@ -42,7 +32,7 @@ public:
|
|||
|
||||
virtual
|
||||
HRESULT
|
||||
CreateHandler(
|
||||
TryCreateHandler(
|
||||
_In_ IHttpContext *pHttpContext,
|
||||
_Outptr_ IREQUEST_HANDLER **pRequestHandler) = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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_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_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_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_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_INPROCESS_START_SUCCESS_MSG L"Application '%s' started the coreclr in-process successfully."
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ IN_PROCESS_APPLICATION::ExecuteApplication()
|
|||
QueryApplicationPhysicalPath().c_str(),
|
||||
m_pLoggerProvider));
|
||||
|
||||
LOG_IF_FAILED(m_pLoggerProvider->Start());
|
||||
m_pLoggerProvider->TryStartRedirection();
|
||||
}
|
||||
|
||||
// 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.
|
||||
m_clrThread.detach();
|
||||
|
||||
LOG_IF_FAILED(m_pLoggerProvider->Stop());
|
||||
m_pLoggerProvider->TryStopRedirection();
|
||||
|
||||
if (m_fStopCalled)
|
||||
{
|
||||
|
|
@ -391,8 +391,6 @@ IN_PROCESS_APPLICATION::ExecuteClr(const std::shared_ptr<ExecuteClrContext>& con
|
|||
VOID
|
||||
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
|
||||
// this is required because thread might be abandoned
|
||||
HandleWrapper<ModuleHandleTraits> moduleHandle;
|
||||
|
|
@ -441,14 +439,11 @@ IN_PROCESS_APPLICATION::SetEnvironmentVariablesOnWorkerProcess()
|
|||
VOID
|
||||
IN_PROCESS_APPLICATION::UnexpectedThreadExit(const ExecuteClrContext& context) const
|
||||
{
|
||||
STRA straStdErrOutput;
|
||||
STRU struStdMsg;
|
||||
auto hasStdOut = m_pLoggerProvider->GetStdOutContent(&straStdErrOutput) &&
|
||||
SUCCEEDED(struStdMsg.CopyA(straStdErrOutput.QueryStr()));
|
||||
auto content = m_pLoggerProvider->GetStdOutContent();
|
||||
|
||||
if (context.m_exceptionCode != 0)
|
||||
{
|
||||
if (hasStdOut)
|
||||
if (!content.empty())
|
||||
{
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_INPROCESS_THREAD_EXCEPTION,
|
||||
|
|
@ -456,7 +451,7 @@ IN_PROCESS_APPLICATION::UnexpectedThreadExit(const ExecuteClrContext& context) c
|
|||
QueryApplicationId().c_str(),
|
||||
QueryApplicationPhysicalPath().c_str(),
|
||||
context.m_exceptionCode,
|
||||
struStdMsg.QueryStr());
|
||||
content.c_str());
|
||||
}
|
||||
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
|
||||
// or there was an exception.
|
||||
//
|
||||
if (hasStdOut)
|
||||
|
||||
if (!content.empty())
|
||||
{
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT,
|
||||
|
|
@ -484,7 +480,7 @@ IN_PROCESS_APPLICATION::UnexpectedThreadExit(const ExecuteClrContext& context) c
|
|||
QueryApplicationId().c_str(),
|
||||
QueryApplicationPhysicalPath().c_str(),
|
||||
context.m_exitCode,
|
||||
struStdMsg.QueryStr());
|
||||
content.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
|
||||
#include <thread>
|
||||
#include "InProcessApplicationBase.h"
|
||||
#include "IOutputManager.h"
|
||||
#include "InProcessOptions.h"
|
||||
#include "BaseOutputManager.h"
|
||||
|
||||
class IN_PROCESS_HANDLER;
|
||||
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;
|
||||
|
||||
std::unique_ptr<IOutputManager> m_pLoggerProvider;
|
||||
std::unique_ptr<BaseOutputManager> m_pLoggerProvider;
|
||||
|
||||
inline static const LPCSTR s_exeLocationParameterName = "InProcessExeLocation";
|
||||
|
||||
|
|
|
|||
|
|
@ -59,8 +59,7 @@ IN_PROCESS_HANDLER::OnExecuteRequestHandler()
|
|||
|
||||
return RQ_NOTIFICATION_FINISH_REQUEST;
|
||||
}
|
||||
else if (m_pApplication->QueryStatus() != APPLICATION_STATUS::RUNNING || m_pApplication->
|
||||
QueryBlockCallbacksIntoManaged())
|
||||
else if (m_pApplication->QueryBlockCallbacksIntoManaged())
|
||||
{
|
||||
return ServerShutdownMessage();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ RESPONSE_HEADER_HASH * FORWARDING_HANDLER::sm_pResponseHeaderHash = NULL;
|
|||
|
||||
FORWARDING_HANDLER::FORWARDING_HANDLER(
|
||||
_In_ IHttpContext *pW3Context,
|
||||
_In_ OUT_OF_PROCESS_APPLICATION *pApplication
|
||||
) : IREQUEST_HANDLER(),
|
||||
_In_ std::unique_ptr<OUT_OF_PROCESS_APPLICATION, IAPPLICATION_DELETER> pApplication
|
||||
) : REQUEST_HANDLER(),
|
||||
m_Signature(FORWARDING_HANDLER_SIGNATURE),
|
||||
m_RequestStatus(FORWARDER_START),
|
||||
m_fClientDisconnected(FALSE),
|
||||
|
|
@ -46,7 +46,7 @@ FORWARDING_HANDLER::FORWARDING_HANDLER(
|
|||
m_fServerResetConn(FALSE),
|
||||
m_cRefs(1),
|
||||
m_pW3Context(pW3Context),
|
||||
m_pApplication(pApplication)
|
||||
m_pApplication(std::move(pApplication))
|
||||
{
|
||||
LOG_TRACE(L"FORWARDING_HANDLER::FORWARDING_HANDLER");
|
||||
|
||||
|
|
@ -100,7 +100,6 @@ FORWARDING_HANDLER::OnExecuteRequestHandler()
|
|||
IHttpRequest *pRequest = m_pW3Context->GetRequest();
|
||||
IHttpResponse *pResponse = m_pW3Context->GetResponse();
|
||||
IHttpConnection *pClientConnection = NULL;
|
||||
OUT_OF_PROCESS_APPLICATION *pApplication = NULL;
|
||||
PROTOCOL_CONFIG *pProtocol = &sm_ProtocolConfig;
|
||||
SERVER_PROCESS *pServerProcess = NULL;
|
||||
|
||||
|
|
@ -128,14 +127,13 @@ FORWARDING_HANDLER::OnExecuteRequestHandler()
|
|||
goto Failure;
|
||||
}
|
||||
|
||||
pApplication = static_cast<OUT_OF_PROCESS_APPLICATION*> (m_pApplication);
|
||||
if (pApplication == NULL)
|
||||
if (m_pApplication == NULL)
|
||||
{
|
||||
hr = E_INVALIDARG;
|
||||
goto Failure;
|
||||
}
|
||||
|
||||
hr = pApplication->GetProcess(&pServerProcess);
|
||||
hr = m_pApplication->GetProcess(&pServerProcess);
|
||||
if (FAILED_LOG(hr))
|
||||
{
|
||||
fFailedToStartKestrel = TRUE;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class FORWARDING_HANDLER : public REQUEST_HANDLER
|
|||
public:
|
||||
FORWARDING_HANDLER(
|
||||
_In_ IHttpContext *pW3Context,
|
||||
_In_ OUT_OF_PROCESS_APPLICATION *pApplication
|
||||
_In_ std::unique_ptr<OUT_OF_PROCESS_APPLICATION, IAPPLICATION_DELETER> pApplication
|
||||
);
|
||||
|
||||
~FORWARDING_HANDLER();
|
||||
|
|
@ -238,6 +238,6 @@ private:
|
|||
|
||||
mutable LONG m_cRefs;
|
||||
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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ OUT_OF_PROCESS_APPLICATION::CreateHandler(
|
|||
SetWebsocketStatus(pHttpContext);
|
||||
}
|
||||
|
||||
pHandler = new FORWARDING_HANDLER(pHttpContext, this);
|
||||
pHandler = new FORWARDING_HANDLER(pHttpContext, ::ReferenceApplication(this));
|
||||
*pRequestHandler = pHandler;
|
||||
return S_OK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
|
||||
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||
|
|
@ -68,6 +69,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
|||
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||
|
||||
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]
|
||||
|
|
@ -222,6 +227,48 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
|||
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)
|
||||
{
|
||||
using (var stream = File.Open(logPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
|||
|
||||
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]
|
||||
|
|
|
|||
|
|
@ -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}'.";
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return $"Failed to start application '/LM/W3SVC/1/ROOT', ErrorCode '{code}'.";
|
||||
|
|
|
|||
|
|
@ -33,9 +33,8 @@ namespace FileOutManagerStartupTests
|
|||
PCWSTR expected = L"test";
|
||||
|
||||
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);
|
||||
|
||||
|
|
@ -69,89 +68,84 @@ namespace FileOutManagerOutputTests
|
|||
{
|
||||
TEST(FileOutManagerOutputTest, StdOut)
|
||||
{
|
||||
PCSTR expected = "test";
|
||||
PCWSTR expected = L"test";
|
||||
|
||||
auto tempDirectory = TempDirectory();
|
||||
|
||||
FileOutputManager* pManager = new FileOutputManager;
|
||||
pManager->Initialize(L"", tempDirectory.path().c_str());
|
||||
FileOutputManager* pManager = new FileOutputManager(L"", tempDirectory.path());
|
||||
{
|
||||
FileManagerWrapper wrapper(pManager);
|
||||
|
||||
fprintf(stdout, expected);
|
||||
fwprintf(stdout, expected);
|
||||
pManager->Stop();
|
||||
|
||||
STRA straContent;
|
||||
ASSERT_TRUE(pManager->GetStdOutContent(&straContent));
|
||||
auto output = pManager->GetStdOutContent();
|
||||
ASSERT_FALSE(output.empty());
|
||||
|
||||
ASSERT_STREQ(straContent.QueryStr(), expected);
|
||||
ASSERT_STREQ(output.c_str(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FileOutManagerOutputTest, StdErr)
|
||||
{
|
||||
PCSTR expected = "test";
|
||||
PCWSTR expected = L"test";
|
||||
|
||||
auto tempDirectory = TempDirectory();
|
||||
|
||||
FileOutputManager* pManager = new FileOutputManager;
|
||||
pManager->Initialize(L"", tempDirectory.path().c_str());
|
||||
FileOutputManager* pManager = new FileOutputManager(L"", tempDirectory.path().c_str());
|
||||
{
|
||||
FileManagerWrapper wrapper(pManager);
|
||||
|
||||
fprintf(stderr, expected);
|
||||
fwprintf(stderr, expected);
|
||||
pManager->Stop();
|
||||
|
||||
STRA straContent;
|
||||
ASSERT_TRUE(pManager->GetStdOutContent(&straContent));
|
||||
auto output = pManager->GetStdOutContent();
|
||||
ASSERT_FALSE(output.empty());
|
||||
|
||||
ASSERT_STREQ(straContent.QueryStr(), expected);
|
||||
ASSERT_STREQ(output.c_str(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FileOutManagerOutputTest, CapAt30KB)
|
||||
{
|
||||
PCSTR expected = "hello world";
|
||||
PCWSTR expected = L"hello world";
|
||||
|
||||
auto tempDirectory = TempDirectory();
|
||||
|
||||
FileOutputManager* pManager = new FileOutputManager;
|
||||
pManager->Initialize(L"", tempDirectory.path().c_str());
|
||||
FileOutputManager* pManager = new FileOutputManager(L"", tempDirectory.path());
|
||||
{
|
||||
FileManagerWrapper wrapper(pManager);
|
||||
|
||||
for (int i = 0; i < 3000; i++)
|
||||
{
|
||||
printf(expected);
|
||||
wprintf(expected);
|
||||
}
|
||||
pManager->Stop();
|
||||
STRA straContent;
|
||||
ASSERT_TRUE(pManager->GetStdOutContent(&straContent));
|
||||
auto output = pManager->GetStdOutContent();
|
||||
ASSERT_FALSE(output.empty());
|
||||
|
||||
ASSERT_EQ(straContent.QueryCCH(), 30000);
|
||||
ASSERT_EQ(output.size(), 30000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(FileOutManagerOutputTest, StartStopRestoresCorrectly)
|
||||
{
|
||||
PCSTR expected = "test";
|
||||
PCWSTR expected = L"test";
|
||||
|
||||
auto tempDirectory = TempDirectory();
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
FileOutputManager* pManager = new FileOutputManager;
|
||||
pManager->Initialize(L"", tempDirectory.path().c_str());
|
||||
FileOutputManager* pManager = new FileOutputManager(L"", tempDirectory.path());
|
||||
{
|
||||
FileManagerWrapper wrapper(pManager);
|
||||
|
||||
printf(expected);
|
||||
wprintf(expected);
|
||||
pManager->Stop();
|
||||
STRA straContent;
|
||||
ASSERT_TRUE(pManager->GetStdOutContent(&straContent));
|
||||
auto output = pManager->GetStdOutContent();
|
||||
ASSERT_FALSE(output.empty());
|
||||
|
||||
ASSERT_STREQ(straContent.QueryStr(), expected);
|
||||
ASSERT_STREQ(output.c_str(), expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,40 +26,49 @@ namespace PipeOutputManagerTests
|
|||
TEST(PipeManagerOutputTest, StdOut)
|
||||
{
|
||||
PCWSTR expected = L"test";
|
||||
STRA output;
|
||||
|
||||
PipeOutputManager* pManager = new PipeOutputManager(true);
|
||||
|
||||
ASSERT_EQ(S_OK, pManager->Start());
|
||||
pManager->Start();
|
||||
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);
|
||||
ASSERT_STREQ(output.QueryStr(), "test");
|
||||
TEST(PipeManagerOutputTest, StdOutMultiToWide)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
TEST(PipeManagerOutputTest, StdErr)
|
||||
{
|
||||
PCWSTR expected = L"test";
|
||||
STRA output;
|
||||
|
||||
PipeOutputManager* pManager = new PipeOutputManager();
|
||||
|
||||
ASSERT_EQ(S_OK, pManager->Start());
|
||||
pManager->Start();
|
||||
fwprintf(stderr, expected);
|
||||
ASSERT_EQ(S_OK, pManager->Stop());
|
||||
pManager->Stop();
|
||||
|
||||
pManager->GetStdOutContent(&output);
|
||||
ASSERT_STREQ(output.QueryStr(), "test");
|
||||
auto output = pManager->GetStdOutContent();
|
||||
ASSERT_STREQ(output.c_str(), expected);
|
||||
delete pManager;
|
||||
}
|
||||
|
||||
TEST(PipeManagerOutputTest, CheckMaxPipeSize)
|
||||
{
|
||||
std::wstring test;
|
||||
STRA output;
|
||||
for (int i = 0; i < 3000; i++)
|
||||
{
|
||||
test.append(L"hello world");
|
||||
|
|
@ -67,14 +76,15 @@ namespace PipeOutputManagerTests
|
|||
|
||||
PipeOutputManager* pManager = new PipeOutputManager();
|
||||
|
||||
ASSERT_EQ(S_OK, pManager->Start());
|
||||
pManager->Start();
|
||||
wprintf(test.c_str());
|
||||
ASSERT_EQ(S_OK, pManager->Stop());
|
||||
pManager->Stop();
|
||||
|
||||
pManager->GetStdOutContent(&output);
|
||||
ASSERT_EQ(output.QueryCCH(), (DWORD)30000);
|
||||
auto output = pManager->GetStdOutContent();
|
||||
ASSERT_EQ(output.size(), (DWORD)30000);
|
||||
delete pManager;
|
||||
}
|
||||
|
||||
TEST(PipeManagerOutputTest, SetInvalidHandlesForErrAndOut)
|
||||
{
|
||||
auto m_fdPreviousStdOut = _dup(_fileno(stdout));
|
||||
|
|
@ -86,7 +96,7 @@ namespace PipeOutputManagerTests
|
|||
PCWSTR expected = L"test";
|
||||
|
||||
PipeOutputManager* pManager = new PipeOutputManager();
|
||||
ASSERT_EQ(S_OK, pManager->Start());
|
||||
pManager->Start();
|
||||
|
||||
_dup2(m_fdPreviousStdOut, _fileno(stdout));
|
||||
_dup2(m_fdPreviousStdErr, _fileno(stderr));
|
||||
|
|
@ -104,17 +114,16 @@ namespace PipeOutputManagerTests
|
|||
auto stdoutBefore = _fileno(stdout);
|
||||
auto stderrBefore = _fileno(stderr);
|
||||
PCWSTR expected = L"test";
|
||||
STRA output;
|
||||
|
||||
PipeOutputManager* pManager = new PipeOutputManager();
|
||||
|
||||
ASSERT_EQ(S_OK, pManager->Start());
|
||||
pManager->Start();
|
||||
fwprintf(stdout, expected);
|
||||
|
||||
ASSERT_EQ(S_OK, pManager->Stop());
|
||||
pManager->Stop();
|
||||
|
||||
pManager->GetStdOutContent(&output);
|
||||
ASSERT_STREQ(output.QueryStr(), "test");
|
||||
auto output = pManager->GetStdOutContent();
|
||||
ASSERT_STREQ(output.c_str(), expected);
|
||||
ASSERT_EQ(stdoutBefore, _fileno(stdout));
|
||||
ASSERT_EQ(stderrBefore, _fileno(stderr));
|
||||
delete pManager;
|
||||
|
|
@ -131,16 +140,15 @@ namespace PipeOutputManagerTests
|
|||
auto stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
auto stderrHandle = GetStdHandle(STD_ERROR_HANDLE);
|
||||
PCWSTR expected = L"test";
|
||||
STRA output;
|
||||
|
||||
PipeOutputManager* pManager = new PipeOutputManager();
|
||||
|
||||
ASSERT_EQ(S_OK, pManager->Start());
|
||||
pManager->Start();
|
||||
fwprintf(stderr, expected);
|
||||
ASSERT_EQ(S_OK, pManager->Stop());
|
||||
pManager->Stop();
|
||||
|
||||
pManager->GetStdOutContent(&output);
|
||||
ASSERT_STREQ(output.QueryStr(), "test");
|
||||
auto output = pManager->GetStdOutContent();
|
||||
ASSERT_STREQ(output.c_str(), expected);
|
||||
ASSERT_EQ(stdoutBefore, _fileno(stdout));
|
||||
|
||||
ASSERT_EQ(stderrBefore, _fileno(stderr));
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@ namespace IISTestSite
|
|||
{
|
||||
// Semicolons are appended to env variables; removing them.
|
||||
case "CheckLargeStdOutWrites":
|
||||
Console.WriteLine(new string('a', 4096));
|
||||
Console.WriteLine(new string('a', 30000));
|
||||
break;
|
||||
case "CheckLargeStdErrWrites":
|
||||
Console.Error.WriteLine(new string('a', 4096));
|
||||
Console.Error.WriteLine(new string('a', 30000));
|
||||
Console.Error.Flush();
|
||||
break;
|
||||
case "CheckLogFile":
|
||||
|
|
@ -35,17 +35,16 @@ namespace IISTestSite
|
|||
Console.Error.Flush();
|
||||
break;
|
||||
case "CheckOversizedStdErrWrites":
|
||||
Console.WriteLine(new string('a', 5000));
|
||||
Console.WriteLine(new string('a', 31000));
|
||||
break;
|
||||
case "CheckOversizedStdOutWrites":
|
||||
Console.Error.WriteLine(new string('a', 4096));
|
||||
Console.Error.WriteLine(new string('a', 31000));
|
||||
Console.Error.Flush();
|
||||
break;
|
||||
case "Hang":
|
||||
Thread.Sleep(Timeout.Infinite);
|
||||
break;
|
||||
case "HangOnStop":
|
||||
|
||||
var host = new WebHostBuilder()
|
||||
.UseIIS()
|
||||
.UseStartup<Startup>()
|
||||
|
|
@ -60,6 +59,7 @@ namespace IISTestSite
|
|||
Console.Title = "Test";
|
||||
Console.WriteLine($"Is Console redirection: {Console.IsOutputRedirected}");
|
||||
Console.BackgroundColor = ConsoleColor.Blue;
|
||||
Console.WriteLine("彡⾔");
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue