diff --git a/build/dependencies.props b/build/dependencies.props index d8c5e54b45..1027e444c9 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -52,6 +52,6 @@ 2.3.1 2.4.0 - + diff --git a/src/AspNetCoreModuleV1/AspNetCore/Inc/resource.h b/src/AspNetCoreModuleV1/AspNetCore/Inc/resource.h index 899e9a5c93..6330be8879 100644 --- a/src/AspNetCoreModuleV1/AspNetCore/Inc/resource.h +++ b/src/AspNetCoreModuleV1/AspNetCore/Inc/resource.h @@ -11,7 +11,7 @@ #define ASPNETCORE_EVENT_RAPID_FAIL_COUNT_EXCEEDED_MSG L"Maximum rapid fail count per minute of '%d' exceeded." #define ASPNETCORE_EVENT_PROCESS_START_INTERNAL_ERROR_MSG L"Application '%s' failed to parse processPath and arguments due to internal error, ErrorCode = '0x%x'." #define ASPNETCORE_EVENT_PROCESS_START_POSTCREATE_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s'but failed to get its status, ErrorCode = '0x%x'." -#define ASPNETCORE_EVENT_PROCESS_START_ERROR_MSG L"Application '%s' with physical root '%s' failed to start process with commandline '%s', ErrorCode = '0x%x : %x." +#define ASPNETCORE_EVENT_PROCESS_START_ERROR_MSG L"Application '%s' with physical root '%s' failed to start process with commandline '%s', ErrorCode = '0x%x' : %x." #define ASPNETCORE_EVENT_PROCESS_START_WRONGPORT_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s' but failed to listen on the given port '%d'" #define ASPNETCORE_EVENT_PROCESS_START_NOTREADY_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s' but either crashed or did not reponse or did not listen on the given port '%d', ErrorCode = '0x%x'" #define ASPNETCORE_EVENT_INVALID_STDOUT_LOG_FILE_MSG L"Warning: Could not create stdoutLogFile %s, ErrorCode = %d." diff --git a/src/AspNetCoreModuleV1/AspNetCore/aspnetcore_msg.mc b/src/AspNetCoreModuleV1/AspNetCore/aspnetcore_msg.mc index 0bbc4fa1c2..73eb713f1c 100644 --- a/src/AspNetCoreModuleV1/AspNetCore/aspnetcore_msg.mc +++ b/src/AspNetCoreModuleV1/AspNetCore/aspnetcore_msg.mc @@ -1,6 +1,7 @@ ;/*++ ; -;Copyright (c) 2014 Microsoft Corporation +; Copyright (c) .NET Foundation. All rights reserved. +; Licensed under the MIT License. See License.txt in the project root for license information. ; ;Module Name: ; diff --git a/src/AspNetCoreModuleV1/AspNetCore/aspnetcoremodule.rc b/src/AspNetCoreModuleV1/AspNetCore/aspnetcoremodule.rc index 6d771d50bd..7790f5067f 100644 --- a/src/AspNetCoreModuleV1/AspNetCore/aspnetcoremodule.rc +++ b/src/AspNetCoreModuleV1/AspNetCore/aspnetcoremodule.rc @@ -3,6 +3,7 @@ #include #include "version.h" #include "resource.h" +#include "Aspnetcore_msg.rc" ///////////////////////////////////////////////////////////////////////////// // English (United States) resources @@ -75,10 +76,10 @@ BEGIN BEGIN BLOCK "040904b0" BEGIN - VALUE "CompanyName", "Microsoft" + VALUE "CompanyName", "Microsoft Corporation" VALUE "FileDescription", FileDescription VALUE "FileVersion", FileVersionStr - VALUE "InternalName", "aspnetcore.dll" + VALUE "InternalName", "aspnetcore" VALUE "LegalCopyright", "Copyright (C) Microsoft Corporation" VALUE "OriginalFilename", "aspnetcore.dll" VALUE "ProductName", "ASP.NET Core Module" diff --git a/src/AspNetCoreModuleV1/AspNetCore/src/serverprocess.cxx b/src/AspNetCoreModuleV1/AspNetCore/src/serverprocess.cxx index 76da27a99b..b3fd02432a 100644 --- a/src/AspNetCoreModuleV1/AspNetCore/src/serverprocess.cxx +++ b/src/AspNetCoreModuleV1/AspNetCore/src/serverprocess.cxx @@ -132,7 +132,7 @@ SERVER_PROCESS::SetupListenPort( pEnvironmentVarTable->FindKey(ASPNETCORE_PORT_ENV_STR, &pEntry); if (pEntry != NULL) { - if (pEntry->QueryValue() != NULL || pEntry->QueryValue()[0] != L'\0') + if (pEntry->QueryValue() != NULL && pEntry->QueryValue()[0] != L'\0') { m_dwPort = (DWORD)_wtoi(pEntry->QueryValue()); if(m_dwPort >MAX_PORT || m_dwPort < MIN_PORT) @@ -150,6 +150,8 @@ SERVER_PROCESS::SetupListenPort( // user set the env variable but did not give value, let's set it up // pEnvironmentVarTable->DeleteKey(ASPNETCORE_PORT_ENV_STR); + pEntry->Dereference(); + pEntry = NULL; } } @@ -1068,8 +1070,8 @@ Finished: { if (!fDonePrepareCommandLine) strEventMsg.SafeSnwprintf( - m_struAppFullPath.QueryStr(), ASPNETCORE_EVENT_PROCESS_START_INTERNAL_ERROR_MSG, + m_struAppFullPath.QueryStr(), hr); else strEventMsg.SafeSnwprintf( diff --git a/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp b/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp index 6c78cc16a6..49d1a29093 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp @@ -10,6 +10,8 @@ #include "GlobalVersionUtility.h" #include "HandleWrapper.h" #include "file_utility.h" +#include "LoggingHelpers.h" +#include "resources.h" const PCWSTR HandlerResolver::s_pwzAspnetcoreInProcessRequestHandlerName = L"aspnetcorev2_inprocess.dll"; const PCWSTR HandlerResolver::s_pwzAspnetcoreOutOfProcessRequestHandlerName = L"aspnetcorev2_outofprocess.dll"; @@ -46,6 +48,7 @@ HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, STRU if (pConfiguration->QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS) { std::unique_ptr options; + std::unique_ptr outputManager; RETURN_IF_FAILED(HOSTFXR_OPTIONS::Create( NULL, @@ -56,14 +59,35 @@ HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, STRU RETURN_IF_FAILED(location.Copy(options->GetExeLocation())); - if (FAILED_LOG(hr = FindNativeAssemblyFromHostfxr(options.get(), pstrHandlerDllName, &struFileName))) - { - EventLog::Error( - ASPNETCORE_EVENT_INPROCESS_RH_MISSING, - ASPNETCORE_EVENT_INPROCESS_RH_MISSING_MSG, - struFileName.IsEmpty() ? s_pwzAspnetcoreInProcessRequestHandlerName : struFileName.QueryStr()); + RETURN_IF_FAILED(LoggingHelpers::CreateLoggingProvider( + pConfiguration->QueryStdoutLogEnabled(), + !m_pServer.IsCommandLineLaunch(), + pConfiguration->QueryStdoutLogFile()->QueryStr(), + pApplication.GetApplicationPhysicalPath(), + outputManager)); + + outputManager->Start(); + + hr = FindNativeAssemblyFromHostfxr(options.get(), pstrHandlerDllName, &struFileName); + outputManager->Stop(); + + if (FAILED(hr) && m_hHostFxrDll != NULL) + { + STRA content; + STRU struStdMsg; + + outputManager->GetStdOutContent(&content); + if (content.QueryCCH() > 0) + { + struStdMsg.CopyA(content.QueryStr()); + } + + EventLog::Error( + ASPNETCORE_EVENT_GENERAL_ERROR, + ASPNETCORE_EVENT_INPROCESS_RH_ERROR_MSG, + struFileName.IsEmpty() ? s_pwzAspnetcoreInProcessRequestHandlerName : struFileName.QueryStr(), + struStdMsg.QueryStr()); - return hr; } } else @@ -188,12 +212,23 @@ HandlerResolver::FindNativeAssemblyFromHostfxr( BOOL fFound = FALSE; DWORD dwBufferSize = 1024 * 10; DWORD dwRequiredBufferSize = 0; + STRA output; + DBG_ASSERT(struFilename != NULL); RETURN_LAST_ERROR_IF_NULL(m_hHostFxrDll = LoadLibraryW(hostfxrOptions->GetHostFxrLocation())); auto pFnHostFxrSearchDirectories = reinterpret_cast(GetProcAddress(m_hHostFxrDll, "hostfxr_get_native_search_directories")); + if (pFnHostFxrSearchDirectories == nullptr) + { + EventLog::Error( + ASPNETCORE_EVENT_GENERAL_ERROR, + ASPNETCORE_EVENT_HOSTFXR_DLL_INVALID_VERSION_MSG, + hostfxrOptions->GetHostFxrLocation() + ); + RETURN_IF_FAILED(E_FAIL); + } RETURN_LAST_ERROR_IF_NULL(pFnHostFxrSearchDirectories); RETURN_IF_FAILED(struNativeSearchPaths.Resize(dwBufferSize)); diff --git a/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.cpp b/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.cpp index 10adc6e2b4..bf62cd9e87 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.cpp @@ -29,7 +29,7 @@ ASPNETCORE_SHIM_CONFIG::Populate( CS_ASPNETCORE_PROCESS_EXE_PATH, &m_struProcessPath)); - // Swallow this error for backward compatability + // Swallow this error for backward compatibility // Use default behavior for empty string GetElementStringProperty(pAspNetCoreElement, CS_ASPNETCORE_HOSTING_MODEL, @@ -50,7 +50,7 @@ ASPNETCORE_SHIM_CONFIG::Populate( ASPNETCORE_EVENT_UNKNOWN_HOSTING_MODEL_ERROR, ASPNETCORE_EVENT_UNKNOWN_HOSTING_MODEL_ERROR_MSG, strHostingModel.QueryStr()); - RETURN_IF_FAILED(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); + RETURN_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } RETURN_IF_FAILED(GetElementStringProperty(pAspNetCoreElement, @@ -62,5 +62,13 @@ ASPNETCORE_SHIM_CONFIG::Populate( RETURN_IF_FAILED(ConfigUtility::FindHandlerVersion(pAspNetCoreElement, m_struHandlerVersion)); } + + RETURN_IF_FAILED(GetElementBoolProperty(pAspNetCoreElement, + CS_ASPNETCORE_STDOUT_LOG_ENABLED, + &m_fStdoutLogEnabled)); + RETURN_IF_FAILED(GetElementStringProperty(pAspNetCoreElement, + CS_ASPNETCORE_STDOUT_LOG_FILE, + &m_struStdoutLogFile)); + return S_OK; } diff --git a/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.h b/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.h index 1ae97f79af..e80071b1c9 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.h +++ b/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.h @@ -12,6 +12,8 @@ #define CS_ASPNETCORE_PROCESS_EXE_PATH L"processPath" #define CS_ASPNETCORE_PROCESS_ARGUMENTS L"arguments" #define CS_ASPNETCORE_HOSTING_MODEL L"hostingModel" +#define CS_ASPNETCORE_STDOUT_LOG_ENABLED L"stdoutLogEnabled" +#define CS_ASPNETCORE_STDOUT_LOG_FILE L"stdoutLogFile" enum APP_HOSTING_MODEL { @@ -56,6 +58,18 @@ public: return &m_struHandlerVersion; } + BOOL + QueryStdoutLogEnabled() + { + return m_fStdoutLogEnabled; + } + + STRU* + QueryStdoutLogFile() + { + return &m_struStdoutLogFile; + } + ASPNETCORE_SHIM_CONFIG() : m_hostingModel(HOSTING_UNKNOWN) { @@ -67,5 +81,6 @@ private: STRU m_struProcessPath; APP_HOSTING_MODEL m_hostingModel; STRU m_struHandlerVersion; + BOOL m_fStdoutLogEnabled; + STRU m_struStdoutLogFile; }; - diff --git a/src/AspNetCoreModuleV2/CommonLib/CommonLib.vcxproj b/src/AspNetCoreModuleV2/CommonLib/CommonLib.vcxproj index 12e6ff1691..47a5373ee1 100644 --- a/src/AspNetCoreModuleV2/CommonLib/CommonLib.vcxproj +++ b/src/AspNetCoreModuleV2/CommonLib/CommonLib.vcxproj @@ -200,6 +200,7 @@ + @@ -207,7 +208,12 @@ + + + + + @@ -220,11 +226,15 @@ + + + + diff --git a/src/AspNetCoreModuleV2/CommonLib/EventLog.cpp b/src/AspNetCoreModuleV2/CommonLib/EventLog.cpp index 7d10a8abf9..24992232de 100644 --- a/src/AspNetCoreModuleV2/CommonLib/EventLog.cpp +++ b/src/AspNetCoreModuleV2/CommonLib/EventLog.cpp @@ -27,7 +27,7 @@ EventLog::LogEvent( ); } - DebugPrintf(dwEventInfoType == EVENTLOG_ERROR_TYPE ? ASPNETCORE_DEBUG_FLAG_ERROR : ASPNETCORE_DEBUG_FLAG_INFO, "Event Log: %S", pstrMsg); + DebugPrintf(dwEventInfoType == EVENTLOG_ERROR_TYPE ? ASPNETCORE_DEBUG_FLAG_ERROR : ASPNETCORE_DEBUG_FLAG_INFO, "Event Log: %S \r\nEnd Event Log Message.", pstrMsg); } VOID diff --git a/src/AspNetCoreModuleV2/CommonLib/FileOutputManager.cpp b/src/AspNetCoreModuleV2/CommonLib/FileOutputManager.cpp new file mode 100644 index 0000000000..6e16ac2acd --- /dev/null +++ b/src/AspNetCoreModuleV2/CommonLib/FileOutputManager.cpp @@ -0,0 +1,190 @@ +// 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 "FileOutputManager.h" +#include "sttimer.h" +#include "exceptions.h" +#include "debugutil.h" +#include "SRWExclusiveLock.h" +#include "file_utility.h" +#include "StdWrapper.h" + +extern HINSTANCE g_hModule; + +FileOutputManager::FileOutputManager() : + FileOutputManager(/* fEnableNativeLogging */ true) { } + +FileOutputManager::FileOutputManager(bool fEnableNativeLogging) : + m_hLogFileHandle(INVALID_HANDLE_VALUE), + m_disposed(false), + stdoutWrapper(nullptr), + stderrWrapper(nullptr), + m_fEnableNativeRedirection(fEnableNativeLogging) +{ + InitializeSRWLock(&m_srwLock); +} + +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 +FileOutputManager::Start() +{ + SYSTEMTIME systemTime; + SECURITY_ATTRIBUTES saAttr = { 0 }; + STRU struPath; + + // Concatenate the log file name and application path + RETURN_IF_FAILED(FILE_UTILITY::ConvertPathToFullPath( + m_wsStdOutLogFileName.QueryStr(), + m_wsApplicationPath.QueryStr(), + &struPath)); + + RETURN_IF_FAILED(FILE_UTILITY::EnsureDirectoryPathExist(struPath.QueryStr())); + + // Get the module name and add it to the log file name + // as two log files will be created, one from the shim + // and one from the request handler. + WCHAR path[MAX_PATH]; + RETURN_LAST_ERROR_IF(!GetModuleFileName(g_hModule, path, sizeof(path))); + std::filesystem::path fsPath(path); + + // TODO fix string as it is incorrect + GetSystemTime(&systemTime); + + RETURN_IF_FAILED( + m_struLogFilePath.SafeSnwprintf(L"%s_%d%02d%02d%02d%02d%02d_%d_%s.log", + struPath.QueryStr(), + systemTime.wYear, + systemTime.wMonth, + systemTime.wDay, + systemTime.wHour, + systemTime.wMinute, + systemTime.wSecond, + GetCurrentProcessId(), + fsPath.filename().stem().c_str())); + + 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(), + FILE_READ_DATA | FILE_WRITE_DATA, + FILE_SHARE_READ, + &saAttr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + RETURN_LAST_ERROR_IF(m_hLogFileHandle == INVALID_HANDLE_VALUE); + + stdoutWrapper = std::make_unique(stdout, STD_OUTPUT_HANDLE, m_hLogFileHandle, m_fEnableNativeRedirection); + stderrWrapper = std::make_unique(stderr, STD_ERROR_HANDLE, m_hLogFileHandle, m_fEnableNativeRedirection); + + stdoutWrapper->StartRedirection(); + stderrWrapper->StartRedirection(); + + return S_OK; +} + + +HRESULT +FileOutputManager::Stop() +{ + STRA straStdOutput; + CHAR pzFileContents[MAX_FILE_READ_SIZE] = { 0 }; + DWORD dwNumBytesRead; + LARGE_INTEGER li = { 0 }; + DWORD dwFilePointer = 0; + HANDLE handle = NULL; + WIN32_FIND_DATA fileData; + + if (m_disposed) + { + return S_OK; + } + + SRWExclusiveLock lock(m_srwLock); + + if (m_disposed) + { + return S_OK; + } + + m_disposed = true; + + if (m_hLogFileHandle == INVALID_HANDLE_VALUE) + { + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + FlushFileBuffers(m_hLogFileHandle); + + if (stdoutWrapper != nullptr) + { + RETURN_IF_FAILED(stdoutWrapper->StopRedirection()); + } + + if (stderrWrapper != nullptr) + { + RETURN_IF_FAILED(stderrWrapper->StopRedirection()); + } + + // delete empty log file + handle = FindFirstFile(m_struLogFilePath.QueryStr(), &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())); + } + + // 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)); + + if (li.LowPart == 0 || li.HighPart > 0) + { + RETURN_IF_FAILED(HRESULT_FROM_WIN32(ERROR_FILE_INVALID)); + } + + dwFilePointer = SetFilePointer(m_hLogFileHandle, 0, NULL, FILE_BEGIN); + + RETURN_LAST_ERROR_IF(dwFilePointer == INVALID_SET_FILE_POINTER); + + RETURN_LAST_ERROR_IF(!ReadFile(m_hLogFileHandle, pzFileContents, MAX_FILE_READ_SIZE, &dwNumBytesRead, NULL)); + + m_straFileContent.Copy(pzFileContents, dwNumBytesRead); + + // printf will fail in in full IIS + if (printf(m_straFileContent.QueryStr()) != -1) + { + // Need to flush contents for the new stdout and stderr + _flushall(); + } + + return S_OK; +} + +bool FileOutputManager::GetStdOutContent(STRA* struStdOutput) +{ + struStdOutput->Copy(m_straFileContent); + return m_straFileContent.QueryCCH() > 0; +} diff --git a/src/AspNetCoreModuleV2/RequestHandlerLib/FileOutputManager.h b/src/AspNetCoreModuleV2/CommonLib/FileOutputManager.h similarity index 70% rename from src/AspNetCoreModuleV2/RequestHandlerLib/FileOutputManager.h rename to src/AspNetCoreModuleV2/CommonLib/FileOutputManager.h index b1a43e6b0d..94f6e19176 100644 --- a/src/AspNetCoreModuleV2/RequestHandlerLib/FileOutputManager.h +++ b/src/AspNetCoreModuleV2/CommonLib/FileOutputManager.h @@ -6,12 +6,17 @@ #include "sttimer.h" #include "IOutputManager.h" #include "HandleWrapper.h" +#include "StdWrapper.h" +#include "stringa.h" +#include "stringu.h" class FileOutputManager : public IOutputManager { #define FILE_FLUSH_TIMEOUT 3000 + #define MAX_FILE_READ_SIZE 30000 public: FileOutputManager(); + FileOutputManager(bool fEnableNativeLogging); ~FileOutputManager(); HRESULT @@ -27,9 +32,10 @@ private: STRU m_wsStdOutLogFileName; STRU m_wsApplicationPath; STRU m_struLogFilePath; - int m_fdPreviousStdOut; - int m_fdPreviousStdErr; + STRA m_straFileContent; BOOL m_disposed; - SRWLOCK m_srwLock; + BOOL m_fEnableNativeRedirection; + SRWLOCK m_srwLock{}; + std::unique_ptr stdoutWrapper; + std::unique_ptr stderrWrapper; }; - diff --git a/src/AspNetCoreModuleV2/CommonLib/HandleWrapper.h b/src/AspNetCoreModuleV2/CommonLib/HandleWrapper.h index 6764f4c14d..d6b50f2381 100644 --- a/src/AspNetCoreModuleV2/CommonLib/HandleWrapper.h +++ b/src/AspNetCoreModuleV2/CommonLib/HandleWrapper.h @@ -4,6 +4,7 @@ #pragma once #include +#include struct InvalidHandleTraits { diff --git a/src/AspNetCoreModuleV2/RequestHandlerLib/IOutputManager.h b/src/AspNetCoreModuleV2/CommonLib/IOutputManager.h similarity index 94% rename from src/AspNetCoreModuleV2/RequestHandlerLib/IOutputManager.h rename to src/AspNetCoreModuleV2/CommonLib/IOutputManager.h index b7e04df059..6b733dd0e6 100644 --- a/src/AspNetCoreModuleV2/RequestHandlerLib/IOutputManager.h +++ b/src/AspNetCoreModuleV2/CommonLib/IOutputManager.h @@ -1,9 +1,11 @@ // 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" - #pragma once + +#include "stdafx.h" +#include "stringa.h" + class IOutputManager { public: diff --git a/src/AspNetCoreModuleV2/RequestHandlerLib/LoggingHelpers.cpp b/src/AspNetCoreModuleV2/CommonLib/LoggingHelpers.cpp similarity index 62% rename from src/AspNetCoreModuleV2/RequestHandlerLib/LoggingHelpers.cpp rename to src/AspNetCoreModuleV2/CommonLib/LoggingHelpers.cpp index a133172db4..69de017e7f 100644 --- a/src/AspNetCoreModuleV2/RequestHandlerLib/LoggingHelpers.cpp +++ b/src/AspNetCoreModuleV2/CommonLib/LoggingHelpers.cpp @@ -7,14 +7,18 @@ #include "FileOutputManager.h" #include "PipeOutputManager.h" #include "NullOutputManager.h" +#include "debugutil.h" +#include +#include +#include "ntassert.h" HRESULT LoggingHelpers::CreateLoggingProvider( bool fIsLoggingEnabled, - bool fEnablePipe, + bool fEnableNativeLogging, PCWSTR pwzStdOutFileName, PCWSTR pwzApplicationPath, - _Out_ IOutputManager** outputManager + std::unique_ptr& outputManager ) { HRESULT hr = S_OK; @@ -25,17 +29,17 @@ LoggingHelpers::CreateLoggingProvider( { if (fIsLoggingEnabled) { - FileOutputManager* manager = new FileOutputManager; + auto manager = std::make_unique(fEnableNativeLogging); hr = manager->Initialize(pwzStdOutFileName, pwzApplicationPath); - *outputManager = manager; + outputManager = std::move(manager); } - else if (fEnablePipe) + else if (!GetConsoleWindow()) { - *outputManager = new PipeOutputManager; + outputManager = std::make_unique(fEnableNativeLogging); } else { - *outputManager = new NullOutputManager; + outputManager = std::make_unique(); } } catch (std::bad_alloc&) diff --git a/src/AspNetCoreModuleV2/RequestHandlerLib/LoggingHelpers.h b/src/AspNetCoreModuleV2/CommonLib/LoggingHelpers.h similarity index 81% rename from src/AspNetCoreModuleV2/RequestHandlerLib/LoggingHelpers.h rename to src/AspNetCoreModuleV2/CommonLib/LoggingHelpers.h index f11a4ff5f5..0c06f759cf 100644 --- a/src/AspNetCoreModuleV2/RequestHandlerLib/LoggingHelpers.h +++ b/src/AspNetCoreModuleV2/CommonLib/LoggingHelpers.h @@ -13,10 +13,10 @@ public: HRESULT CreateLoggingProvider( bool fLoggingEnabled, - bool fEnablePipe, + bool fEnableNativeLogging, PCWSTR pwzStdOutFileName, PCWSTR pwzApplicationPath, - _Out_ IOutputManager** outputManager + std::unique_ptr& outputManager ); }; diff --git a/src/AspNetCoreModuleV2/RequestHandlerLib/NullOutputManager.h b/src/AspNetCoreModuleV2/CommonLib/NullOutputManager.h similarity index 100% rename from src/AspNetCoreModuleV2/RequestHandlerLib/NullOutputManager.h rename to src/AspNetCoreModuleV2/CommonLib/NullOutputManager.h diff --git a/src/AspNetCoreModuleV2/RequestHandlerLib/PipeOutputManager.cpp b/src/AspNetCoreModuleV2/CommonLib/PipeOutputManager.cpp similarity index 50% rename from src/AspNetCoreModuleV2/RequestHandlerLib/PipeOutputManager.cpp rename to src/AspNetCoreModuleV2/CommonLib/PipeOutputManager.cpp index 6fdcdb078b..54fb162fe4 100644 --- a/src/AspNetCoreModuleV2/RequestHandlerLib/PipeOutputManager.cpp +++ b/src/AspNetCoreModuleV2/CommonLib/PipeOutputManager.cpp @@ -5,63 +5,74 @@ #include "PipeOutputManager.h" #include "exceptions.h" #include "SRWExclusiveLock.h" +#include "StdWrapper.h" +#include "ntassert.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); -PipeOutputManager::PipeOutputManager() : - m_dwStdErrReadTotal(0), +PipeOutputManager::PipeOutputManager() + : PipeOutputManager( /* fEnableNativeLogging */ true) +{ +} + +PipeOutputManager::PipeOutputManager(bool fEnableNativeLogging) : m_hErrReadPipe(INVALID_HANDLE_VALUE), m_hErrWritePipe(INVALID_HANDLE_VALUE), - m_hErrThread(NULL), + m_hErrThread(nullptr), + m_dwStdErrReadTotal(0), m_disposed(FALSE), - m_fdPreviousStdOut(-1), - m_fdPreviousStdErr(-1) + m_fEnableNativeRedirection(fEnableNativeLogging), + stdoutWrapper(nullptr), + stderrWrapper(nullptr) { InitializeSRWLock(&m_srwLock); } PipeOutputManager::~PipeOutputManager() { - Stop(); + PipeOutputManager::Stop(); } +// Start redirecting stdout and stderr into a pipe +// Continuously read the pipe on a background thread +// until Stop is called. HRESULT PipeOutputManager::Start() { SECURITY_ATTRIBUTES saAttr = { 0 }; HANDLE hStdErrReadPipe; HANDLE hStdErrWritePipe; - m_fdPreviousStdOut = _dup(_fileno(stdout)); - LOG_IF_DUPFAIL(m_fdPreviousStdOut); - - m_fdPreviousStdErr = _dup(_fileno(stderr)); - LOG_IF_DUPFAIL(m_fdPreviousStdErr); - RETURN_LAST_ERROR_IF(!CreatePipe(&hStdErrReadPipe, &hStdErrWritePipe, &saAttr, 0 /*nSize*/)); - // TODO this still doesn't redirect calls in native, like wprintf - RETURN_LAST_ERROR_IF(!SetStdHandle(STD_ERROR_HANDLE, hStdErrWritePipe)); - - RETURN_LAST_ERROR_IF(!SetStdHandle(STD_OUTPUT_HANDLE, hStdErrWritePipe)); - m_hErrReadPipe = hStdErrReadPipe; m_hErrWritePipe = hStdErrWritePipe; - // Read the stderr handle on a separate thread until we get 4096 bytes. + stdoutWrapper = std::make_unique(stdout, STD_OUTPUT_HANDLE, hStdErrWritePipe, m_fEnableNativeRedirection); + stderrWrapper = std::make_unique(stderr, STD_ERROR_HANDLE, hStdErrWritePipe, m_fEnableNativeRedirection); + + LOG_IF_FAILED(stdoutWrapper->StartRedirection()); + LOG_IF_FAILED(stderrWrapper->StartRedirection()); + + // Read the stderr handle on a separate thread until we get 30Kb. m_hErrThread = CreateThread( - NULL, // default security attributes + nullptr, // default security attributes 0, // default stack size - (LPTHREAD_START_ROUTINE)ReadStdErrHandle, + reinterpret_cast(ReadStdErrHandle), this, // thread function arguments 0, // default creation flags - NULL); // receive thread identifier + nullptr); // receive thread identifier RETURN_LAST_ERROR_IF_NULL(m_hErrThread); return S_OK; } +// Stop redirecting stdout and stderr into a pipe +// This closes the background thread reading from the pipe +// 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() { DWORD dwThreadStatus = 0; @@ -71,57 +82,53 @@ HRESULT PipeOutputManager::Stop() { return S_OK; } + SRWExclusiveLock lock(m_srwLock); if (m_disposed) { return S_OK; } + m_disposed = true; - fflush(stdout); - fflush(stderr); - - // Restore the original stdout and stderr handles of the process, - // as the application has either finished startup or has exited. - - // If stdout/stderr were not set, we need to set it to NUL: - // such that other calls to Console.WriteLine don't use an invalid handle - FILE *stream; - - if (m_fdPreviousStdOut >= 0) - { - LOG_LAST_ERROR_IF(SetStdHandle(STD_OUTPUT_HANDLE, (HANDLE)_get_osfhandle(m_fdPreviousStdOut))); - } - else - { - LOG_IF_ERRNO(freopen_s(&stream, "NUL:", "w", stdout)); - } - - if (m_fdPreviousStdErr >= 0) - { - LOG_LAST_ERROR_IF(SetStdHandle(STD_ERROR_HANDLE, (HANDLE)_get_osfhandle(m_fdPreviousStdErr))); - } - else - { - LOG_IF_ERRNO(freopen_s(&stream, "NUL:", "w", stderr)); - } - + // Both pipe wrappers duplicate the pipe writer handle + // meaning we are fine to close the handle too. if (m_hErrWritePipe != INVALID_HANDLE_VALUE) { + // Flush the pipe writer before closing to capture all output + RETURN_LAST_ERROR_IF(!FlushFileBuffers(m_hErrWritePipe)); CloseHandle(m_hErrWritePipe); m_hErrWritePipe = INVALID_HANDLE_VALUE; } + // Tell each pipe wrapper to stop redirecting output and restore the original values + if (stdoutWrapper != nullptr) + { + LOG_IF_FAILED(stdoutWrapper->StopRedirection()); + } + + if (stderrWrapper != nullptr) + { + LOG_IF_FAILED(stderrWrapper->StopRedirection()); + } + + // Forces ReadFile to cancel, causing the read loop to complete. + // Don't check return value as IO may or may not be completed already. + if (m_hErrThread != nullptr) + { + CancelSynchronousIo(m_hErrThread); + } + // GetExitCodeThread returns 0 on failure; thread status code is invalid. - if (m_hErrThread != NULL && + if (m_hErrThread != nullptr && !LOG_LAST_ERROR_IF(GetExitCodeThread(m_hErrThread, &dwThreadStatus) == 0) && dwThreadStatus == STILL_ACTIVE) { - // wait for graceful shutdown, i.e., the exit of the background thread or timeout + // Wait for graceful shutdown, i.e., the exit of the background thread or timeout if (WaitForSingleObject(m_hErrThread, PIPE_OUTPUT_THREAD_TIMEOUT) != WAIT_OBJECT_0) { - // if the thread is still running, we need kill it first before exit to avoid AV + // If the thread is still running, we need kill it first before exit to avoid AV if (!LOG_LAST_ERROR_IF(GetExitCodeThread(m_hErrThread, &dwThreadStatus) == 0) && dwThreadStatus == STILL_ACTIVE) { @@ -131,42 +138,38 @@ HRESULT PipeOutputManager::Stop() } } - if (m_hErrThread != NULL) + if (m_hErrThread != nullptr) { CloseHandle(m_hErrThread); - m_hErrThread = NULL; + m_hErrThread = nullptr; } - if (m_hErrReadPipe != INVALID_HANDLE_VALUE) { CloseHandle(m_hErrReadPipe); m_hErrReadPipe = INVALID_HANDLE_VALUE; } + // 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)) { - printf(straStdOutput.QueryStr()); - // Need to flush contents for the new stdout and stderr - _flushall(); + // printf will fail in in full IIS + if (printf(straStdOutput.QueryStr()) != -1) + { + // Need to flush contents for the new stdout and stderr + _flushall(); + } } return S_OK; } -VOID -PipeOutputManager::ReadStdErrHandle( - LPVOID pContext -) -{ - PipeOutputManager *pLoggingProvider = (PipeOutputManager*)pContext; - DBG_ASSERT(pLoggingProvider != NULL); - pLoggingProvider->ReadStdErrHandleInternal(); -} - bool PipeOutputManager::GetStdOutContent(STRA* straStdOutput) { bool fLogged = false; + + // TODO consider returning the file contents rather than copying. if (m_dwStdErrReadTotal > 0) { if (SUCCEEDED(straStdOutput->Copy(m_pzFileContents, m_dwStdErrReadTotal))) @@ -178,15 +181,29 @@ bool PipeOutputManager::GetStdOutContent(STRA* straStdOutput) return fLogged; } -VOID -PipeOutputManager::ReadStdErrHandleInternal( - VOID +void +PipeOutputManager::ReadStdErrHandle( + LPVOID pContext ) { + auto pLoggingProvider = static_cast(pContext); + DBG_ASSERT(pLoggingProvider != NULL); + pLoggingProvider->ReadStdErrHandleInternal(); +} + +void +PipeOutputManager::ReadStdErrHandleInternal() +{ + // If ReadFile ever returns false, exit the thread DWORD dwNumBytesRead = 0; while (true) { - if (ReadFile(m_hErrReadPipe, &m_pzFileContents[m_dwStdErrReadTotal], MAX_PIPE_READ_SIZE - m_dwStdErrReadTotal, &dwNumBytesRead, NULL)) + // 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, + &dwNumBytesRead, + nullptr)) { m_dwStdErrReadTotal += dwNumBytesRead; if (m_dwStdErrReadTotal >= MAX_PIPE_READ_SIZE) @@ -194,19 +211,21 @@ PipeOutputManager::ReadStdErrHandleInternal( break; } } - else if (GetLastError() == ERROR_BROKEN_PIPE) + else { return; } } - char tempBuffer[MAX_PIPE_READ_SIZE]; + // Using std::string as a wrapper around new char[] so we don't need to call delete + // Also don't allocate on stack as stack size is 128KB by default. + std::string tempBuffer; + tempBuffer.resize(MAX_PIPE_READ_SIZE); + + // After reading the maximum amount of data, keep reading in a loop until Stop is called on the output manager. while (true) { - if (ReadFile(m_hErrReadPipe, tempBuffer, MAX_PIPE_READ_SIZE, &dwNumBytesRead, NULL)) - { - } - else if (GetLastError() == ERROR_BROKEN_PIPE) + if (!ReadFile(m_hErrReadPipe, tempBuffer.data(), MAX_PIPE_READ_SIZE, &dwNumBytesRead, nullptr)) { return; } diff --git a/src/AspNetCoreModuleV2/RequestHandlerLib/PipeOutputManager.h b/src/AspNetCoreModuleV2/CommonLib/PipeOutputManager.h similarity index 53% rename from src/AspNetCoreModuleV2/RequestHandlerLib/PipeOutputManager.h rename to src/AspNetCoreModuleV2/CommonLib/PipeOutputManager.h index 3a15c2e68e..0b51ff4038 100644 --- a/src/AspNetCoreModuleV2/RequestHandlerLib/PipeOutputManager.h +++ b/src/AspNetCoreModuleV2/CommonLib/PipeOutputManager.h @@ -4,35 +4,41 @@ #pragma once #include "IOutputManager.h" +#include "StdWrapper.h" +#include "stringu.h" class PipeOutputManager : public IOutputManager { + // Timeout to be used if a thread never exits #define PIPE_OUTPUT_THREAD_TIMEOUT 2000 - #define MAX_PIPE_READ_SIZE 4096 + + // Max event log message is ~32KB, limit pipe size just below that. + #define MAX_PIPE_READ_SIZE 30000 public: PipeOutputManager(); + PipeOutputManager(bool fEnableNativeLogging); ~PipeOutputManager(); - virtual HRESULT Start() override; - virtual HRESULT Stop() override; - virtual bool GetStdOutContent(STRA* struStdOutput) override; + HRESULT Start() override; + HRESULT Stop() override; + bool GetStdOutContent(STRA* straStdOutput) override; // Thread functions - VOID ReadStdErrHandleInternal(VOID); + void ReadStdErrHandleInternal(); - static - VOID ReadStdErrHandle(LPVOID pContext); + static void ReadStdErrHandle(LPVOID pContext); 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; - int m_fdPreviousStdOut; - int m_fdPreviousStdErr; + SRWLOCK m_srwLock {}; BOOL m_disposed; + BOOL m_fEnableNativeRedirection; + std::unique_ptr stdoutWrapper; + std::unique_ptr stderrWrapper; }; - diff --git a/src/AspNetCoreModuleV2/CommonLib/StdWrapper.cpp b/src/AspNetCoreModuleV2/CommonLib/StdWrapper.cpp new file mode 100644 index 0000000000..dffaa491c5 --- /dev/null +++ b/src/AspNetCoreModuleV2/CommonLib/StdWrapper.cpp @@ -0,0 +1,148 @@ +// 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 "StdWrapper.h" +#include "exceptions.h" +#include "LoggingHelpers.h" +#include +#include + +StdWrapper::StdWrapper(FILE* stdStream, DWORD stdHandleNumber, HANDLE handleToRedirectTo, BOOL fEnableNativeRedirection) + : m_previousFileDescriptor(0), + m_stdStream(stdStream), + m_stdHandleNumber(stdHandleNumber), + m_fEnableNativeRedirection(fEnableNativeRedirection), + m_handleToRedirectTo(handleToRedirectTo), + m_redirectedFile(nullptr) +{ +} + +StdWrapper::~StdWrapper() = default; + +// Redirects stdout/stderr to the provided handle. +// Example: +// If the handleToRedirecTo = 0x24 +// Before: +// _fileno(stdout) = 1 +// GetStdHandle(STD_OUTPUT_HANDLE) = 0x20 +// After: +// _fileno(stdout) = 3 +// GetStdHandle(STD_OUTPUT_HANDLE) = 0x28 <- Duplicated from 0x24 +HRESULT +StdWrapper::StartRedirection() +{ + HANDLE stdHandle; + + // In IIS, stdout and stderr are set to null as w3wp is created with DETACHED_PROCESS, + // so fileno(m_stdStream) returns -2. + // Open a null file such that undoing the redirection succeeds and _dup2 works. + // m_previousFileDescriptor will be used for restoring stdout/stderr + if (_fileno(m_stdStream) == -2) + { + freopen_s((FILE**)&m_stdStream, "nul", "w", m_stdStream); + m_previousFileDescriptor = _dup(_fileno(m_stdStream)); + } + else + { + m_previousFileDescriptor = _dup(_fileno(m_stdStream)); + } + + if (!m_fEnableNativeRedirection) + { + RETURN_LAST_ERROR_IF(!SetStdHandle(m_stdHandleNumber, m_handleToRedirectTo)); + + return S_OK; + } + // After setting the std handle, we need to set stdout/stderr to the current + // output/error handle. + + // Duplicate the handle before opening the handle and associating a file pointer. + // If we don't by calling close on the file, the same handle value will be closed + // multiple times. + // Note, by calling duplicate handle, the new handle returned will have a different value + // than the original, but point to the same underlying file object. + RETURN_LAST_ERROR_IF(!DuplicateHandle( + /* hSourceProcessHandle*/ GetCurrentProcess(), + /* hSourceHandle */ m_handleToRedirectTo, + /* hTargetProcessHandle */ GetCurrentProcess(), + /* lpTargetHandle */&stdHandle, + /* dwDesiredAccess */ 0, // dwDesired is ignored if DUPLICATE_SAME_ACCESS is specified + /* bInheritHandle */ TRUE, + /* dwOptions */ DUPLICATE_SAME_ACCESS)); + + RETURN_LAST_ERROR_IF(!SetStdHandle(m_stdHandleNumber, stdHandle)); + + // _open_osfhandle will associate a filedescriptor with the handle. + const auto fileDescriptor = _open_osfhandle(reinterpret_cast(stdHandle), _O_WTEXT | _O_TEXT); + + if (fileDescriptor == -1) + { + RETURN_IF_FAILED(HRESULT_FROM_WIN32(ERROR_FILE_INVALID)); + } + + m_redirectedFile = _fdopen(fileDescriptor, "w"); + + if (m_redirectedFile == nullptr) + { + RETURN_IF_FAILED(HRESULT_FROM_WIN32(ERROR_FILE_INVALID)); + } + + // Set stdout/stderr to the newly created file. + const auto dup2Result = _dup2(_fileno(m_redirectedFile), _fileno(m_stdStream)); + + if (dup2Result != 0) + { + RETURN_IF_FAILED(HRESULT_FROM_WIN32(ERROR_FILE_INVALID)); + } + + // Removes buffering from the output + if (setvbuf(m_stdStream, nullptr, _IONBF, 0) != 0) + { + RETURN_IF_FAILED(HRESULT_FROM_WIN32(ERROR_FILE_INVALID)); + } + + return S_OK; +} + +// Redirects stdout/stderr back to the original stdout/stderr. +// Note, this will not restore the original handle values returned by GetStdHandle, +// rather a duplicated number. This is because the original handle value is invalid +// due to dup2 closing the file originally in stdout/stderr +HRESULT +StdWrapper::StopRedirection() const +{ + // After setting the std handle, we need to set stdout/stderr to the current + // output/error handle. + FILE * file = _fdopen(m_previousFileDescriptor, "w"); + if (file == nullptr) + { + RETURN_IF_FAILED(HRESULT_FROM_WIN32(ERROR_FILE_INVALID)); + } + + RETURN_LAST_ERROR_IF(!SetStdHandle(m_stdHandleNumber, reinterpret_cast(_get_osfhandle(m_previousFileDescriptor)))); + + if (!m_fEnableNativeRedirection) + { + return S_OK; + } + + // Set stdout/stderr to the newly created file output. + const auto dup2Result = _dup2(_fileno(file), _fileno(m_stdStream)); + if (dup2Result != 0) + { + RETURN_IF_FAILED(HRESULT_FROM_WIN32(ERROR_FILE_INVALID)); + } + + if (setvbuf(m_stdStream, nullptr, _IONBF, 0) != 0) + { + RETURN_IF_FAILED(HRESULT_FROM_WIN32(ERROR_FILE_INVALID)); + } + + if (fclose(m_redirectedFile) != 0) + { + RETURN_IF_FAILED(HRESULT_FROM_WIN32(ERROR_FILE_INVALID)); + } + + return S_OK; +} diff --git a/src/AspNetCoreModuleV2/CommonLib/StdWrapper.h b/src/AspNetCoreModuleV2/CommonLib/StdWrapper.h new file mode 100644 index 0000000000..f03b2ff060 --- /dev/null +++ b/src/AspNetCoreModuleV2/CommonLib/StdWrapper.h @@ -0,0 +1,25 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +#pragma once + +#include + +// Wraps stdout/stderr stream, modifying them to redirect to the given handle +class StdWrapper +{ +public: + StdWrapper(FILE* stdStream, DWORD stdHandleNumber, HANDLE handleToRedirectTo, BOOL fEnableNativeRedirection); + ~StdWrapper(); + HRESULT StartRedirection(); + HRESULT StopRedirection() const; + +private: + int m_previousFileDescriptor; + FILE* m_stdStream; + DWORD m_stdHandleNumber; + BOOL m_fEnableNativeRedirection; + HANDLE m_handleToRedirectTo; + FILE* m_redirectedFile; +}; + diff --git a/src/AspNetCoreModuleV2/CommonLib/aspnetcore_msg.mc b/src/AspNetCoreModuleV2/CommonLib/aspnetcore_msg.mc index c71cbfeee2..5b4c244c63 100644 --- a/src/AspNetCoreModuleV2/CommonLib/aspnetcore_msg.mc +++ b/src/AspNetCoreModuleV2/CommonLib/aspnetcore_msg.mc @@ -177,19 +177,19 @@ Language=English . Messageid=1025 -SymbolicName=ASPNETCORE_EVENT_GENERAL_INFO_MSG +SymbolicName=ASPNETCORE_EVENT_GENERAL_INFO Language=English %1 . Messageid=1026 -SymbolicName=ASPNETCORE_EVENT_GENERAL_WARNING_MSG +SymbolicName=ASPNETCORE_EVENT_GENERAL_WARNING Language=English %1 . Messageid=1027 -SymbolicName=ASPNETCORE_EVENT_GENERAL_ERROR_MSG +SymbolicName=ASPNETCORE_EVENT_GENERAL_ERROR Language=English %1 . diff --git a/src/AspNetCoreModuleV2/CommonLib/exceptions.h b/src/AspNetCoreModuleV2/CommonLib/exceptions.h index ac05f910c1..bd6f30af33 100644 --- a/src/AspNetCoreModuleV2/CommonLib/exceptions.h +++ b/src/AspNetCoreModuleV2/CommonLib/exceptions.h @@ -26,6 +26,8 @@ #endif #define OBSERVE_CAUGHT_EXCEPTION() CaughtExceptionHResult(LOCATION_INFO); +#define RETURN_HR(hr) do { HRESULT __hrRet = hr; if (FAILED(__hrRet)) { LogHResultFailed(LOCATION_INFO, __hrRet); } return __hrRet; } while (0, 0) +#define RETURN_LAST_ERROR() do { return LogLastError(LOCATION_INFO); } while (0, 0) #define RETURN_IF_FAILED(hr) do { HRESULT __hrRet = hr; if (FAILED(__hrRet)) { LogHResultFailed(LOCATION_INFO, __hrRet); return __hrRet; }} while (0, 0) #define RETURN_CAUGHT_EXCEPTION() return CaughtExceptionHResult(LOCATION_INFO); #define RETURN_LAST_ERROR_IF(condition) do { if (condition) { return LogLastError(LOCATION_INFO); }} while (0, 0) diff --git a/src/AspNetCoreModuleV2/CommonLib/resources.h b/src/AspNetCoreModuleV2/CommonLib/resources.h index 63e0e9b163..82f22c52c0 100644 --- a/src/AspNetCoreModuleV2/CommonLib/resources.h +++ b/src/AspNetCoreModuleV2/CommonLib/resources.h @@ -17,7 +17,7 @@ #define ASPNETCORE_EVENT_PROCESS_START_ERROR_MSG L"Application '%s' with physical root '%s' failed to start process with commandline '%s' at stage '%s', ErrorCode = '0x%x', assigned port %d, retryCounter '%d'." #define ASPNETCORE_EVENT_PROCESS_START_FAILURE_MSG L"Application '%s' with physical root '%s' failed to start process with commandline '%s' with multiple retries. The last try of listening port is '%d'. See previous warnings for details." #define ASPNETCORE_EVENT_PROCESS_START_STATUS_ERROR_MSG L"Application '%s' with physical root '%s' failed to start process with commandline '%s' , ErrorCode = '0x%x', processId '%d', processStatus '%d'." -#define ASPNETCORE_EVENT_PROCESS_START_PORTSETUP_ERROR_MSG L"Application '%s' with physical root '%s' failed to choose listen port '%d' given port rang '%d - %d', EorrorCode = '0x%x'. If environment variable 'ASPNETCORE_PORT' was set, try removing it such that a random port is selected instead." +#define ASPNETCORE_EVENT_PROCESS_START_PORTSETUP_ERROR_MSG L"Application '%s' with physical root '%s' failed to choose listen port '%d' given port range '%d - %d', ErrorCode = '0x%x'. If environment variable 'ASPNETCORE_PORT' was set, try removing it such that a random port is selected instead." #define ASPNETCORE_EVENT_PROCESS_START_WRONGPORT_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s' but failed to listen on the given port '%d'" #define ASPNETCORE_EVENT_PROCESS_START_NOTREADY_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s' but either crashed or did not respond or did not listen on the given port '%d', ErrorCode = '0x%x'" #define ASPNETCORE_EVENT_PROCESS_SHUTDOWN_MSG L"Application '%s' with physical root '%s' shut down process with Id '%d' listening on port '%d'" @@ -31,8 +31,8 @@ #define ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG L"Mixed hosting model is not supported. Application '%s' configured with different hostingModel value '%d' other than the one of running application(s)." #define ASPNETCORE_EVENT_UNKNOWN_HOSTING_MODEL_ERROR_MSG L"Unknown hosting model '%s'. Please specify either hostingModel=\"inprocess\" or hostingModel=\"outofprocess\" in the web.config file." #define ASPNETCORE_EVENT_ADD_APPLICATION_ERROR_MSG L"Failed to start application '%s', ErrorCode '0x%x'." -#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT_MSG L"Application '%s' with physical root '%s' hit unexpected managed background thread exit, ErrorCode = '0x%x. Last 4KB characters of captured stdout and stderr logs:\r\n%s" -#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_MSG L"Application '%s' with physical root '%s' hit unexpected managed background thread exit, ErrorCode = '0x%x. Please check the stderr logs for more information." +#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT_MSG L"Application '%s' with physical root '%s' hit unexpected managed background thread exit, ErrorCode = '0x%x'. Last 4KB characters of captured stdout and stderr logs:\r\n%s" +#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_MSG L"Application '%s' with physical root '%s' hit unexpected managed background thread exit, ErrorCode = '0x%x'. Please check the stderr logs for more information." #define ASPNETCORE_EVENT_APP_IN_SHUTDOWN_MSG L"Application shutting down." #define ASPNETCORE_EVENT_RECYCLE_APPOFFLINE_MSG L"Application '%s' was recycled after detecting the app_offline file." #define ASPNETCORE_EVENT_MONITOR_APPOFFLINE_ERROR_MSG L"Monitoring app_offline.htm failed for application '%s', ErrorCode '0x%x'. " @@ -43,9 +43,11 @@ #define ASPNETCORE_EVENT_PORTABLE_APP_DOTNET_MISSING_MSG L"Could not find dotnet.exe on the system PATH environment variable for portable application '%s'. Check that a valid path to dotnet is on the PATH and the bitness of dotnet matches the bitness of the IIS worker process. ErrorCode = '0x%x'." #define ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG L"Could not find the hostfxr directory '%s' in the dotnet directory. ErrorCode = '0x%x'." #define ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG L"Could not find hostfxr.dll in '%s'. ErrorCode = '0x%x'." +#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_APPLICATION_EXE_NOT_FOUND_LEVEL EVENTLOG_ERROR_TYPE #define ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND_MSG L"Could not find application executable in '%s'. ErrorCode = '0x%x'." #define ASPNETCORE_EVENT_INPROCESS_THREAD_EXCEPTION_MSG L"Application '%s' with physical root '%s' hit unexpected managed exception, ErrorCode = '0x%x. Please check the stderr logs for more information." #define ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG L"Invalid or unknown processPath provided in web.config: processPath = '%s', ErrorCode = '0x%x'." -#define ASPNETCORE_EVENT_INPROCESS_RH_MISSING_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." +#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." diff --git a/src/AspNetCoreModuleV2/CommonLib/sttimer.h b/src/AspNetCoreModuleV2/CommonLib/sttimer.h index 0ebbdbd822..26d79d0737 100644 --- a/src/AspNetCoreModuleV2/CommonLib/sttimer.h +++ b/src/AspNetCoreModuleV2/CommonLib/sttimer.h @@ -6,6 +6,7 @@ #ifndef _STTIMER_H #define _STTIMER_H +#include "stringu.h" class STTIMER { diff --git a/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp b/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp index 352ec7b093..43a4071cf5 100644 --- a/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp +++ b/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp @@ -102,12 +102,6 @@ IN_PROCESS_APPLICATION::StopInternal(bool fServerInitiated) } } - if (m_pLoggerProvider != NULL) - { - delete m_pLoggerProvider; - m_pLoggerProvider = NULL; - } - Finished: if (FAILED(hr)) @@ -256,24 +250,7 @@ IN_PROCESS_APPLICATION::LoadManagedApplication } { - // Set up stdout redirect - SRWExclusiveLock lock(m_stateLock); - if (m_pLoggerProvider == NULL) - { - hr = LoggingHelpers::CreateLoggingProvider( - m_pConfig->QueryStdoutLogEnabled(), - !GetConsoleWindow(), - m_pConfig->QueryStdoutLogFile()->QueryStr(), - m_pConfig->QueryApplicationPhysicalPath()->QueryStr(), - &m_pLoggerProvider); - if (FAILED(hr)) - { - goto Finished; - } - - LOG_IF_FAILED(m_pLoggerProvider->Start()); - } if (m_status != MANAGED_APPLICATION_STATUS::STARTING) { @@ -461,6 +438,20 @@ IN_PROCESS_APPLICATION::ExecuteApplication( FINISHED_IF_FAILED(SetEnvironementVariablesOnWorkerProcess()); } + LOG_INFO("Starting managed application"); + + if (m_pLoggerProvider == NULL) + { + FINISHED_IF_FAILED(hr = LoggingHelpers::CreateLoggingProvider( + m_pConfig->QueryStdoutLogEnabled(), + !m_pHttpServer.IsCommandLineLaunch(), + m_pConfig->QueryStdoutLogFile()->QueryStr(), + m_pConfig->QueryApplicationPhysicalPath()->QueryStr(), + m_pLoggerProvider)); + + LOG_IF_FAILED(m_pLoggerProvider->Start()); + } + // There can only ever be a single instance of .NET Core // loaded in the process but we need to get config information to boot it up in the // first place. This is happening in an execute request handler and everyone waits @@ -482,7 +473,7 @@ Finished: // m_status = MANAGED_APPLICATION_STATUS::SHUTDOWN; m_fShutdownCalledFromManaged = TRUE; - FreeLibrary(hModule); + m_pLoggerProvider->Stop(); if (!m_fShutdownCalledFromNative) @@ -545,7 +536,6 @@ IN_PROCESS_APPLICATION::RunDotnetApplication(DWORD argc, CONST PCWSTR* argv, hos __try { - LOG_INFO("Starting managed application"); m_ProcessExitCode = pProc(argc, argv); if (m_ProcessExitCode != 0) { diff --git a/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.h b/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.h index 8993d7fc8e..10149cf3f3 100644 --- a/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.h +++ b/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.h @@ -150,7 +150,7 @@ private: static IN_PROCESS_APPLICATION* s_Application; - IOutputManager* m_pLoggerProvider; + std::unique_ptr m_pLoggerProvider; static const LPCSTR s_exeLocationParameterName; diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/OutOfProcessRequestHandler.vcxproj b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/OutOfProcessRequestHandler.vcxproj index 2da0550413..df9c00ebb1 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/OutOfProcessRequestHandler.vcxproj +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/OutOfProcessRequestHandler.vcxproj @@ -224,7 +224,6 @@ - diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/dllmain.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/dllmain.cpp index d77c5c47d8..25e8054318 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/dllmain.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/dllmain.cpp @@ -121,28 +121,29 @@ EnsureOutOfProcessInitializtion() DBG_ASSERT(g_pHttpServer); HRESULT hr = S_OK; - BOOL fLocked = FALSE; if (g_fOutOfProcessInitializeError) { - hr = E_NOT_VALID_STATE; - goto Finished; + FINISHED(E_NOT_VALID_STATE); } - if (!g_fOutOfProcessInitialize) + if (g_fOutOfProcessInitialize) { - AcquireSRWLockExclusive(&g_srwLockRH); - fLocked = TRUE; + FINISHED(S_OK); + } + + { + auto lock = SRWExclusiveLock(g_srwLockRH); + if (g_fOutOfProcessInitializeError) { - hr = E_NOT_VALID_STATE; - goto Finished; + FINISHED(E_NOT_VALID_STATE); } if (g_fOutOfProcessInitialize) { // Done by another thread - goto Finished; + FINISHED(S_OK); } g_hWinHttpModule = GetModuleHandle(TEXT("winhttp.dll")); @@ -150,7 +151,7 @@ EnsureOutOfProcessInitializtion() g_hAspNetCoreModule = GetModuleHandle(TEXT("aspnetcorev2.dll")); hr = WINHTTP_HELPER::StaticInitialize(); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { if (hr == HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND)) { @@ -158,7 +159,7 @@ EnsureOutOfProcessInitializtion() } else { - goto Finished; + FINISHED(hr); } } @@ -167,11 +168,7 @@ EnsureOutOfProcessInitializtion() WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, WINHTTP_FLAG_ASYNC); - if (g_hWinhttpSession == NULL) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; - } + FINISHED_LAST_ERROR_IF(g_hWinhttpSession == NULL); // // Don't set non-blocking callbacks WINHTTP_OPTION_ASSURED_NON_BLOCKING_CALLBACKS, @@ -182,65 +179,33 @@ EnsureOutOfProcessInitializtion() // // Setup the callback function // - if (WinHttpSetStatusCallback(g_hWinhttpSession, + FINISHED_LAST_ERROR_IF(WinHttpSetStatusCallback(g_hWinhttpSession, FORWARDING_HANDLER::OnWinHttpCompletion, (WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS | WINHTTP_CALLBACK_STATUS_SENDING_REQUEST), - NULL) == WINHTTP_INVALID_STATUS_CALLBACK) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; - } + NULL) == WINHTTP_INVALID_STATUS_CALLBACK); // // Make sure we see the redirects (rather than winhttp doing it // automatically) // DWORD dwRedirectOption = WINHTTP_OPTION_REDIRECT_POLICY_NEVER; - if (!WinHttpSetOption(g_hWinhttpSession, + FINISHED_LAST_ERROR_IF(!WinHttpSetOption(g_hWinhttpSession, WINHTTP_OPTION_REDIRECT_POLICY, &dwRedirectOption, - sizeof(dwRedirectOption))) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; - } + sizeof(dwRedirectOption))); g_dwTlsIndex = TlsAlloc(); - if (g_dwTlsIndex == TLS_OUT_OF_INDEXES) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; - } - - hr = ALLOC_CACHE_HANDLER::StaticInitialize(); - if (FAILED(hr)) - { - goto Finished; - } - - hr = FORWARDING_HANDLER::StaticInitialize(g_fEnableReferenceCountTracing); - if (FAILED(hr)) - { - goto Finished; - } - - hr = WEBSOCKET_HANDLER::StaticInitialize(g_fEnableReferenceCountTracing); - if (FAILED(hr)) - { - goto Finished; - } + FINISHED_LAST_ERROR_IF(g_dwTlsIndex == TLS_OUT_OF_INDEXES); + FINISHED_IF_FAILED(ALLOC_CACHE_HANDLER::StaticInitialize()); + FINISHED_IF_FAILED(FORWARDING_HANDLER::StaticInitialize(g_fEnableReferenceCountTracing)); + FINISHED_IF_FAILED(WEBSOCKET_HANDLER::StaticInitialize(g_fEnableReferenceCountTracing)); } - Finished: if (FAILED(hr)) { g_fOutOfProcessInitializeError = TRUE; } - if (fLocked) - { - ReleaseSRWLockExclusive(&g_srwLockRH); - } return hr; } diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/forwarderconnection.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/forwarderconnection.cpp index 0369edcce9..ee49e99242 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/forwarderconnection.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/forwarderconnection.cpp @@ -2,12 +2,13 @@ // Licensed under the MIT License. See License.txt in the project root for license information. #include "forwarderconnection.h" +#include "exceptions.h" FORWARDER_CONNECTION::FORWARDER_CONNECTION( VOID ) : m_cRefs (1), m_hConnection (NULL) -{ +{ } HRESULT @@ -15,38 +16,19 @@ FORWARDER_CONNECTION::Initialize( DWORD dwPort ) { - HRESULT hr = S_OK; - - hr = m_ConnectionKey.Initialize( dwPort ); - if ( FAILED( hr ) ) - { - goto Finished; - } - + RETURN_IF_FAILED(m_ConnectionKey.Initialize( dwPort )); m_hConnection = WinHttpConnect(g_hWinhttpSession, L"127.0.0.1", (USHORT) dwPort, 0); - if (m_hConnection == NULL) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; - } - + RETURN_LAST_ERROR_IF_NULL(m_hConnection); // // Since WinHttp will not emit WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING // when closing WebSocket handle on Win8. Register callback at Connect level as a workaround // - if (WinHttpSetStatusCallback(m_hConnection, + RETURN_LAST_ERROR_IF (WinHttpSetStatusCallback(m_hConnection, FORWARDING_HANDLER::OnWinHttpCompletion, WINHTTP_CALLBACK_FLAG_HANDLES, - NULL) == WINHTTP_INVALID_STATUS_CALLBACK) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; - } - -Finished: - - return hr; + NULL) == WINHTTP_INVALID_STATUS_CALLBACK); + return S_OK; } diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/forwardinghandler.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/forwardinghandler.cpp index f1bcb7a2cc..e06b15f805 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/forwardinghandler.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/forwardinghandler.cpp @@ -3,6 +3,7 @@ #include "forwardinghandler.h" #include "url_utility.h" +#include "exceptions.h" // Just to be aware of the FORWARDING_HANDLER object size. C_ASSERT(sizeof(FORWARDING_HANDLER) <= 632); @@ -140,7 +141,7 @@ FORWARDING_HANDLER::OnExecuteRequestHandler() } hr = pApplication->GetProcess(&pServerProcess); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { fFailedToStartKestrel = TRUE; goto Failure; @@ -165,7 +166,7 @@ FORWARDING_HANDLER::OnExecuteRequestHandler() // // parse original url // - if (FAILED(hr = URL_UTILITY::SplitUrl(pRequest->GetRawHttpRequest()->CookedUrl.pFullUrl, + if (FAILED_LOG(hr = URL_UTILITY::SplitUrl(pRequest->GetRawHttpRequest()->CookedUrl.pFullUrl, &fSecure, &strDestination, &strUrl))) @@ -173,7 +174,7 @@ FORWARDING_HANDLER::OnExecuteRequestHandler() goto Failure; } - if (FAILED(hr = URL_UTILITY::EscapeAbsPath(pRequest, &struEscapedUrl))) + if (FAILED_LOG(hr = URL_UTILITY::EscapeAbsPath(pRequest, &struEscapedUrl))) { goto Failure; } @@ -200,7 +201,7 @@ FORWARDING_HANDLER::OnExecuteRequestHandler() hConnect, &struEscapedUrl, pServerProcess); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Failure; } @@ -222,7 +223,7 @@ FORWARDING_HANDLER::OnExecuteRequestHandler() SetConnectionModuleContext(m_pDisconnect, m_pModuleId); DBG_ASSERT(hr != HRESULT_FROM_WIN32(ERROR_ALREADY_ASSIGNED)); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Failure; } @@ -477,7 +478,7 @@ REQUEST_NOTIFICATION_STATUS InterlockedIncrement(&m_dwHandlers); } - if (FAILED(hr)) + if (FAILED_LOG(hr)) { // This failure could happen when client disconnect happens or backend server fails // after websocket upgrade @@ -514,7 +515,7 @@ REQUEST_NOTIFICATION_STATUS // failure, if there is more data available from WinHTTP, read it // or else ask if there is more. // - if (FAILED(hrCompletionStatus)) + if (FAILED_LOG(hrCompletionStatus)) { hr = hrCompletionStatus; fClientError = TRUE; @@ -522,7 +523,7 @@ REQUEST_NOTIFICATION_STATUS } hr = OnReceivingResponse(); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Failure; } @@ -533,7 +534,7 @@ REQUEST_NOTIFICATION_STATUS hr = OnSendingRequest(cbCompletion, hrCompletionStatus, &fClientError); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Failure; } @@ -730,7 +731,7 @@ HRESULT hr = sm_pAlloc->Initialize(sizeof(FORWARDING_HANDLER), 64); // nThreshold - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } @@ -743,14 +744,14 @@ HRESULT } hr = sm_pResponseHeaderHash->Initialize(); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } // Initialize PROTOCOL_CONFIG hr = sm_ProtocolConfig.Initialize(); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } @@ -786,7 +787,7 @@ HRESULT "); Finished: - if (FAILED(hr)) + if (FAILED_LOG(hr)) { StaticTerminate(); } @@ -871,12 +872,12 @@ FORWARDING_HANDLER::GetHeaders( // if (!pProtocol->QueryPreserveHostHeader()) { - if (FAILED(hr = URL_UTILITY::SplitUrl(pRequest->GetRawHttpRequest()->CookedUrl.pFullUrl, + if (FAILED_LOG(hr = URL_UTILITY::SplitUrl(pRequest->GetRawHttpRequest()->CookedUrl.pFullUrl, &fSecure, &struDestination, &struUrl)) || - FAILED(hr = strTemp.CopyW(struDestination.QueryStr())) || - FAILED(hr = pRequest->SetHeader(HttpHeaderHost, + FAILED_LOG(hr = strTemp.CopyW(struDestination.QueryStr())) || + FAILED_LOG(hr = pRequest->SetHeader(HttpHeaderHost, strTemp.QueryStr(), static_cast(strTemp.QueryCCH()), TRUE))) // fReplace @@ -917,7 +918,7 @@ FORWARDING_HANDLER::GetHeaders( pServerProcess->QueryGuid(), (USHORT)strlen(pServerProcess->QueryGuid()), TRUE); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { return hr; } @@ -933,7 +934,7 @@ FORWARDING_HANDLER::GetHeaders( HANDLE hTargetTokenHandle = NULL; hr = pServerProcess->SetWindowsAuthToken(m_pW3Context->GetUser()->GetPrimaryToken(), &hTargetTokenHandle); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { return hr; } @@ -952,7 +953,7 @@ FORWARDING_HANDLER::GetHeaders( pszHandleStr, (USHORT)strlen(pszHandleStr), TRUE); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { return hr; } @@ -966,14 +967,14 @@ FORWARDING_HANDLER::GetHeaders( pszCurrentHeader = pRequest->GetHeader(pProtocol->QueryXForwardedForName()->QueryStr(), &cchCurrentHeader); if (pszCurrentHeader != NULL) { - if (FAILED(hr = strTemp.Copy(pszCurrentHeader, cchCurrentHeader)) || - FAILED(hr = strTemp.Append(", ", 2))) + if (FAILED_LOG(hr = strTemp.Copy(pszCurrentHeader, cchCurrentHeader)) || + FAILED_LOG(hr = strTemp.Append(", ", 2))) { return hr; } } - if (FAILED(hr = m_pW3Context->GetServerVariable("REMOTE_ADDR", + if (FAILED_LOG(hr = m_pW3Context->GetServerVariable("REMOTE_ADDR", &pszFinalHeader, &cchFinalHeader))) { @@ -982,16 +983,16 @@ FORWARDING_HANDLER::GetHeaders( if (pRequest->GetRawHttpRequest()->Address.pRemoteAddress->sa_family == AF_INET6) { - if (FAILED(hr = strTemp.Append("[", 1)) || - FAILED(hr = strTemp.Append(pszFinalHeader, cchFinalHeader)) || - FAILED(hr = strTemp.Append("]", 1))) + if (FAILED_LOG(hr = strTemp.Append("[", 1)) || + FAILED_LOG(hr = strTemp.Append(pszFinalHeader, cchFinalHeader)) || + FAILED_LOG(hr = strTemp.Append("]", 1))) { return hr; } } else { - if (FAILED(hr = strTemp.Append(pszFinalHeader, cchFinalHeader))) + if (FAILED_LOG(hr = strTemp.Append(pszFinalHeader, cchFinalHeader))) { return hr; } @@ -999,21 +1000,21 @@ FORWARDING_HANDLER::GetHeaders( if (pProtocol->QueryIncludePortInXForwardedFor()) { - if (FAILED(hr = m_pW3Context->GetServerVariable("REMOTE_PORT", + if (FAILED_LOG(hr = m_pW3Context->GetServerVariable("REMOTE_PORT", &pszFinalHeader, &cchFinalHeader))) { return hr; } - if (FAILED(hr = strTemp.Append(":", 1)) || - FAILED(hr = strTemp.Append(pszFinalHeader, cchFinalHeader))) + if (FAILED_LOG(hr = strTemp.Append(":", 1)) || + FAILED_LOG(hr = strTemp.Append(pszFinalHeader, cchFinalHeader))) { return hr; } } - if (FAILED(hr = pRequest->SetHeader(pProtocol->QueryXForwardedForName()->QueryStr(), + if (FAILED_LOG(hr = pRequest->SetHeader(pProtocol->QueryXForwardedForName()->QueryStr(), strTemp.QueryStr(), static_cast(strTemp.QueryCCH()), TRUE))) // fReplace @@ -1036,19 +1037,19 @@ FORWARDING_HANDLER::GetHeaders( pszCurrentHeader = pRequest->GetHeader(pProtocol->QuerySslHeaderName()->QueryStr(), &cchCurrentHeader); if (pszCurrentHeader != NULL) { - if (FAILED(hr = strTemp.Copy(pszCurrentHeader, cchCurrentHeader)) || - FAILED(hr = strTemp.Append(", ", 2))) + if (FAILED_LOG(hr = strTemp.Copy(pszCurrentHeader, cchCurrentHeader)) || + FAILED_LOG(hr = strTemp.Append(", ", 2))) { return hr; } } - if (FAILED(hr = strTemp.Append(pszScheme))) + if (FAILED_LOG(hr = strTemp.Append(pszScheme))) { return hr; } - if (FAILED(pRequest->SetHeader(pProtocol->QuerySslHeaderName()->QueryStr(), + if (FAILED_LOG(pRequest->SetHeader(pProtocol->QuerySslHeaderName()->QueryStr(), strTemp.QueryStr(), (USHORT)strTemp.QueryCCH(), TRUE))) @@ -1067,7 +1068,7 @@ FORWARDING_HANDLER::GetHeaders( else { // Resize the buffer large enough to hold the encoded certificate info - if (FAILED(hr = strTemp.Resize( + if (FAILED_LOG(hr = strTemp.Resize( 1 + (pRequest->GetRawHttpRequest()->pSslInfo->pClientCertInfo->CertEncodedSize + 2) / 3 * 4))) { return hr; @@ -1081,7 +1082,7 @@ FORWARDING_HANDLER::GetHeaders( NULL); strTemp.SyncWithBuffer(); - if (FAILED(hr = pRequest->SetHeader( + if (FAILED_LOG(hr = pRequest->SetHeader( pProtocol->QueryClientCertName()->QueryStr(), strTemp.QueryStr(), static_cast(strTemp.QueryCCH()), @@ -1106,7 +1107,7 @@ FORWARDING_HANDLER::GetHeaders( hr = m_pW3Context->GetServerVariable("ALL_RAW", ppszHeaders, pcchHeaders); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { return hr; } @@ -1134,7 +1135,7 @@ FORWARDING_HANDLER::CreateWinHttpRequest( // we will fill them when sending the request) // pszVerb = pRequest->GetHttpMethod(); - if (FAILED(hr = strVerb.CopyA(pszVerb))) + if (FAILED_LOG(hr = strVerb.CopyA(pszVerb))) { goto Finished; } @@ -1147,7 +1148,7 @@ FORWARDING_HANDLER::CreateWinHttpRequest( "HTTP_VERSION", &pszVersion, &cchUnused); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } @@ -1235,7 +1236,7 @@ FORWARDING_HANDLER::CreateWinHttpRequest( pServerProcess, &m_pszHeaders, &m_cchHeaders); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } @@ -1514,7 +1515,7 @@ None // // Handle failure code for switch statement above. // - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Failure; } @@ -1762,7 +1763,7 @@ FORWARDING_HANDLER::OnWinHttpCompletionSendRequestOrWriteComplete( goto Finished; } } - else if (FAILED(hr)) + else if (FAILED_LOG(hr)) { *pfClientError = TRUE; goto Finished; @@ -1842,7 +1843,7 @@ FORWARDING_HANDLER::OnWinHttpCompletionStatusHeadersAvailable( } } - if (FAILED(hr = strHeaders.CopyW( + if (FAILED_LOG(hr = strHeaders.CopyW( reinterpret_cast(bufHeaderBuffer.QueryPtr())))) { goto Finished; @@ -1859,13 +1860,13 @@ FORWARDING_HANDLER::OnWinHttpCompletionStatusHeadersAvailable( if (!strHeaders.IsEmpty() && strHeaders.QueryStr()[strHeaders.QueryCCH() - 1] != '\n') { hr = strHeaders.Append("\r\n"); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } } - if (FAILED(hr = SetStatusAndHeaders( + if (FAILED_LOG(hr = SetStatusAndHeaders( strHeaders.QueryStr(), strHeaders.QueryCCH()))) { @@ -1890,7 +1891,7 @@ FORWARDING_HANDLER::OnWinHttpCompletionStatusHeadersAvailable( NULL, NULL); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { *pfAnotherCompletionExpected = FALSE; } @@ -2022,7 +2023,7 @@ FORWARDING_HANDLER::OnWinHttpCompletionStatusReadComplete( Chunk.DataChunkType = HttpDataChunkFromMemory; Chunk.FromMemory.pBuffer = m_pEntityBuffer; Chunk.FromMemory.BufferLength = dwStatusInformationLength; - if (FAILED(hr = pResponse->WriteEntityChunkByReference(&Chunk))) + if (FAILED_LOG(hr = pResponse->WriteEntityChunkByReference(&Chunk))) { goto Finished; } @@ -2036,7 +2037,7 @@ FORWARDING_HANDLER::OnWinHttpCompletionStatusReadComplete( hr = pResponse->Flush(TRUE, // fAsync TRUE, // fMoreData NULL); // pcbSent - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } @@ -2357,10 +2358,10 @@ FORWARDING_HANDLER::SetStatusAndHeaders( // // Copy the status description // - if (FAILED(hr = strHeaderValue.Copy( + if (FAILED_LOG(hr = strHeaderValue.Copy( pchStatus, (DWORD)(pchEndofHeaderValue - pchStatus) + 1)) || - FAILED(hr = pResponse->SetStatus(uStatus, + FAILED_LOG(hr = pResponse->SetStatus(uStatus, strHeaderValue.QueryStr(), 0, S_OK, @@ -2422,7 +2423,7 @@ FORWARDING_HANDLER::SetStatusAndHeaders( // // Copy the header name // - if (FAILED(hr = strHeaderName.Copy( + if (FAILED_LOG(hr = strHeaderName.Copy( pszHeaders + index, (DWORD)(pchEndofHeaderName - pszHeaders) - index))) { @@ -2458,7 +2459,7 @@ FORWARDING_HANDLER::SetStatusAndHeaders( { strHeaderValue.Reset(); } - else if (FAILED(hr = strHeaderValue.Copy( + else if (FAILED_LOG(hr = strHeaderValue.Copy( pszHeaders + index, (DWORD)(pchEndofHeaderValue - pszHeaders) - index))) { @@ -2508,7 +2509,7 @@ FORWARDING_HANDLER::SetStatusAndHeaders( static_cast(strHeaderValue.QueryCCH()), TRUE); // fReplace } - if (FAILED(hr)) + if (FAILED_LOG(hr)) { return hr; } @@ -2526,7 +2527,7 @@ FORWARDING_HANDLER::SetStatusAndHeaders( if (m_fDoReverseRewriteHeaders) { hr = DoReverseRewrite(pResponse); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { return hr; } @@ -2573,17 +2574,17 @@ FORWARDING_HANDLER::DoReverseRewrite( pszEndHost = strchr(pszStartHost, '/'); - if (FAILED(hr = strTemp.Copy(fSecure ? "https://" : "http://")) || - FAILED(hr = strTemp.Append(m_pszOriginalHostHeader))) + if (FAILED_LOG(hr = strTemp.Copy(fSecure ? "https://" : "http://")) || + FAILED_LOG(hr = strTemp.Append(m_pszOriginalHostHeader))) { return hr; } if (pszEndHost != NULL && - FAILED(hr = strTemp.Append(pszEndHost))) + FAILED_LOG(hr = strTemp.Append(pszEndHost))) { return hr; } - if (FAILED(hr = pResponse->SetHeader(HttpHeaderContentLocation, + if (FAILED_LOG(hr = pResponse->SetHeader(HttpHeaderContentLocation, strTemp.QueryStr(), static_cast(strTemp.QueryCCH()), TRUE))) @@ -2612,17 +2613,17 @@ Location: pszEndHost = strchr(pszStartHost, '/'); - if (FAILED(hr = strTemp.Copy(fSecure ? "https://" : "http://")) || - FAILED(hr = strTemp.Append(m_pszOriginalHostHeader))) + if (FAILED_LOG(hr = strTemp.Copy(fSecure ? "https://" : "http://")) || + FAILED_LOG(hr = strTemp.Append(m_pszOriginalHostHeader))) { return hr; } if (pszEndHost != NULL && - FAILED(hr = strTemp.Append(pszEndHost))) + FAILED_LOG(hr = strTemp.Append(pszEndHost))) { return hr; } - if (FAILED(hr = pResponse->SetHeader(HttpHeaderLocation, + if (FAILED_LOG(hr = pResponse->SetHeader(HttpHeaderLocation, strTemp.QueryStr(), static_cast(strTemp.QueryCCH()), TRUE))) @@ -2687,9 +2688,9 @@ SetCookie: pszEndHost++; } - if (FAILED(hr = strTemp.Copy(pszHeader, static_cast(pszStartHost - pszHeader))) || - FAILED(hr = strTemp.Append(m_pszOriginalHostHeader)) || - FAILED(hr = strTemp.Append(pszEndHost))) + if (FAILED_LOG(hr = strTemp.Copy(pszHeader, static_cast(pszStartHost - pszHeader))) || + FAILED_LOG(hr = strTemp.Append(m_pszOriginalHostHeader)) || + FAILED_LOG(hr = strTemp.Append(pszEndHost))) { return hr; } diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/outprocessapplication.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/outprocessapplication.cpp index 564adea383..8d3738d82f 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/outprocessapplication.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/outprocessapplication.cpp @@ -4,6 +4,7 @@ #include "outprocessapplication.h" #include "SRWExclusiveLock.h" +#include "exceptions.h" OUT_OF_PROCESS_APPLICATION::OUT_OF_PROCESS_APPLICATION( IHttpApplication& pApplication, @@ -30,25 +31,12 @@ HRESULT OUT_OF_PROCESS_APPLICATION::Initialize( ) { - HRESULT hr = S_OK; if (m_pProcessManager == NULL) { - m_pProcessManager = new PROCESS_MANAGER; - if (m_pProcessManager == NULL) - { - hr = E_OUTOFMEMORY; - goto Finished; - } - - hr = m_pProcessManager->Initialize(); - if (FAILED(hr)) - { - goto Finished; - } + m_pProcessManager = new PROCESS_MANAGER(); + RETURN_IF_FAILED(m_pProcessManager->Initialize()); } - -Finished: - return hr; + return S_OK; } HRESULT @@ -76,7 +64,6 @@ OUT_OF_PROCESS_APPLICATION::CreateHandler( _In_ IHttpContext *pHttpContext, _Out_ IREQUEST_HANDLER **pRequestHandler) { - HRESULT hr = S_OK; IREQUEST_HANDLER* pHandler = NULL; //add websocket check here @@ -86,14 +73,8 @@ OUT_OF_PROCESS_APPLICATION::CreateHandler( } pHandler = new FORWARDING_HANDLER(pHttpContext, this); - - if (pHandler == NULL) - { - hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY); - } - *pRequestHandler = pHandler; - return hr; + return S_OK; } VOID @@ -105,10 +86,8 @@ OUT_OF_PROCESS_APPLICATION::SetWebsocketStatus( // the websocket module may still not be enabled. PCWSTR pszTempWebsocketValue; DWORD cbLength; - HRESULT hr; - hr = pHttpContext->GetServerVariable("WEBSOCKET_VERSION", &pszTempWebsocketValue, &cbLength); - if (FAILED(hr)) + if (FAILED_LOG(pHttpContext->GetServerVariable("WEBSOCKET_VERSION", &pszTempWebsocketValue, &cbLength))) { m_fWebSocketSupported = WEBSOCKET_STATUS::WEBSOCKET_NOT_SUPPORTED; } diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/processmanager.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/processmanager.cpp index b0dd2991e2..71d106fb77 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/processmanager.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/processmanager.cpp @@ -3,6 +3,8 @@ #include "processmanager.h" #include "EventLog.h" +#include "exceptions.h" +#include "SRWSharedLock.h" volatile BOOL PROCESS_MANAGER::sm_fWSAStartupDone = FALSE; @@ -11,28 +13,21 @@ PROCESS_MANAGER::Initialize( VOID ) { - HRESULT hr = S_OK; WSADATA wsaData; int result; - BOOL fLocked = FALSE; if( !sm_fWSAStartupDone ) { - AcquireSRWLockExclusive( &m_srwLock ); - fLocked = TRUE; + auto lock = SRWExclusiveLock(m_srwLock); if( !sm_fWSAStartupDone ) { if( (result = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0 ) { - hr = HRESULT_FROM_WIN32( result ); - goto Finished; + RETURN_HR(HRESULT_FROM_WIN32( result )); } sm_fWSAStartupDone = TRUE; } - - ReleaseSRWLockExclusive( &m_srwLock ); - fLocked = FALSE; } m_dwRapidFailTickStart = GetTickCount(); @@ -51,55 +46,14 @@ PROCESS_MANAGER::Initialize( CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); - if( m_hNULHandle == INVALID_HANDLE_VALUE ) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; - } + RETURN_LAST_ERROR_IF( m_hNULHandle == INVALID_HANDLE_VALUE ); } -Finished: - - if(fLocked) - { - ReleaseSRWLockExclusive( &m_srwLock ); - } - - return hr; + return S_OK; } PROCESS_MANAGER::~PROCESS_MANAGER() { - AcquireSRWLockExclusive(&m_srwLock); - - //if( m_ppServerProcessList != NULL ) - //{ - // for( DWORD i = 0; i < m_dwProcessesPerApplication; ++i ) - // { - // if( m_ppServerProcessList[i] != NULL ) - // { - // m_ppServerProcessList[i]->DereferenceServerProcess(); - // m_ppServerProcessList[i] = NULL; - // } - // } - - // delete[] m_ppServerProcessList; - // m_ppServerProcessList = NULL; - //} - - //if( m_hNULHandle != NULL ) - //{ - // CloseHandle( m_hNULHandle ); - // m_hNULHandle = NULL; - //} - - //if( sm_fWSAStartupDone ) - //{ - // WSACleanup(); - // sm_fWSAStartupDone = FALSE; - //} - - ReleaseSRWLockExclusive(&m_srwLock); } HRESULT @@ -109,31 +63,22 @@ PROCESS_MANAGER::GetProcess( _Out_ SERVER_PROCESS **ppServerProcess ) { - HRESULT hr = S_OK; - BOOL fSharedLock = FALSE; - BOOL fExclusiveLock = FALSE; DWORD dwProcessIndex = 0; - SERVER_PROCESS *pSelectedServerProcess = NULL; + std::unique_ptr pSelectedServerProcess; if (InterlockedCompareExchange(&m_lStopping, 1L, 1L) == 1L) { - return hr = E_APPLICATION_EXITING; + RETURN_IF_FAILED(E_APPLICATION_EXITING); } if (!m_fServerProcessListReady) { - AcquireSRWLockExclusive(&m_srwLock); - fExclusiveLock = TRUE; + auto lock = SRWExclusiveLock(m_srwLock); if (!m_fServerProcessListReady) { m_dwProcessesPerApplication = pConfig->QueryProcessesPerApplication(); m_ppServerProcessList = new SERVER_PROCESS*[m_dwProcessesPerApplication]; - if (m_ppServerProcessList == NULL) - { - hr = E_OUTOFMEMORY; - goto Finished; - } for (DWORD i = 0; i < m_dwProcessesPerApplication; ++i) { @@ -141,35 +86,30 @@ PROCESS_MANAGER::GetProcess( } } m_fServerProcessListReady = TRUE; - ReleaseSRWLockExclusive(&m_srwLock); - fExclusiveLock = FALSE; } - AcquireSRWLockShared(&m_srwLock); - fSharedLock = TRUE; - - // - // round robin through to the next available process. - // - dwProcessIndex = (DWORD)InterlockedIncrement64((LONGLONG*)&m_dwRouteToProcessIndex); - dwProcessIndex = dwProcessIndex % m_dwProcessesPerApplication; - - if (m_ppServerProcessList[dwProcessIndex] != NULL && - m_ppServerProcessList[dwProcessIndex]->IsReady()) { - *ppServerProcess = m_ppServerProcessList[dwProcessIndex]; - goto Finished; - } + auto lock = SRWSharedLock(m_srwLock); - ReleaseSRWLockShared(&m_srwLock); - fSharedLock = FALSE; + // + // round robin through to the next available process. + // + dwProcessIndex = (DWORD)InterlockedIncrement64((LONGLONG*)&m_dwRouteToProcessIndex); + dwProcessIndex = dwProcessIndex % m_dwProcessesPerApplication; + + if (m_ppServerProcessList[dwProcessIndex] != NULL && + m_ppServerProcessList[dwProcessIndex]->IsReady()) + { + *ppServerProcess = m_ppServerProcessList[dwProcessIndex]; + return S_OK; + } + } // should make the lock per process so that we can start processes simultaneously ? if (m_ppServerProcessList[dwProcessIndex] == NULL || !m_ppServerProcessList[dwProcessIndex]->IsReady()) { - AcquireSRWLockExclusive(&m_srwLock); - fExclusiveLock = TRUE; + auto lock = SRWExclusiveLock(m_srwLock); if (m_ppServerProcessList[dwProcessIndex] != NULL) { @@ -186,7 +126,7 @@ PROCESS_MANAGER::GetProcess( // server is already up and ready to serve requests. //m_ppServerProcessList[dwProcessIndex]->ReferenceServerProcess(); *ppServerProcess = m_ppServerProcessList[dwProcessIndex]; - goto Finished; + return S_OK; } } @@ -200,22 +140,14 @@ PROCESS_MANAGER::GetProcess( ASPNETCORE_EVENT_RAPID_FAIL_COUNT_EXCEEDED_MSG, pConfig->QueryRapidFailsPerMinute()); - hr = HRESULT_FROM_WIN32(ERROR_SERVER_DISABLED); - goto Finished; + RETURN_HR(HRESULT_FROM_WIN32(ERROR_SERVER_DISABLED)); } if (m_ppServerProcessList[dwProcessIndex] == NULL) { - pSelectedServerProcess = new SERVER_PROCESS(); - if (pSelectedServerProcess == NULL) - { - hr = E_OUTOFMEMORY; - goto Finished; - } - - - hr = pSelectedServerProcess->Initialize( + pSelectedServerProcess = std::make_unique(); + RETURN_IF_FAILED(pSelectedServerProcess->Initialize( this, //ProcessManager pConfig->QueryProcessPath(), // pConfig->QueryArguments(), // @@ -231,50 +163,18 @@ PROCESS_MANAGER::GetProcess( pConfig->QueryApplicationPhysicalPath(), // physical path pConfig->QueryApplicationPath(), // app path pConfig->QueryApplicationVirtualPath() // App relative virtual path - ); - if (FAILED(hr)) - { - goto Finished; - } - - hr = pSelectedServerProcess->StartProcess(); - if (FAILED(hr)) - { - goto Finished; - } + )); + RETURN_IF_FAILED(pSelectedServerProcess->StartProcess()); } if (!pSelectedServerProcess->IsReady()) { - hr = HRESULT_FROM_WIN32(ERROR_CREATE_FAILED); - goto Finished; + RETURN_HR(HRESULT_FROM_WIN32(ERROR_CREATE_FAILED)); } - m_ppServerProcessList[dwProcessIndex] = pSelectedServerProcess; - pSelectedServerProcess = NULL; - + m_ppServerProcessList[dwProcessIndex] = pSelectedServerProcess.release(); } *ppServerProcess = m_ppServerProcessList[dwProcessIndex]; -Finished: - - if (fSharedLock) - { - ReleaseSRWLockShared(&m_srwLock); - fSharedLock = FALSE; - } - - if (fExclusiveLock) - { - ReleaseSRWLockExclusive(&m_srwLock); - fExclusiveLock = FALSE; - } - - if (pSelectedServerProcess != NULL) - { - delete pSelectedServerProcess; - pSelectedServerProcess = NULL; - } - - return hr; + return S_OK; } diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/protocolconfig.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/protocolconfig.cpp index 0f75a9d072..d0e44b5bd3 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/protocolconfig.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/protocolconfig.cpp @@ -2,41 +2,25 @@ // Licensed under the MIT License. See License.txt in the project root for license information. #include "protocolconfig.h" +#include "exceptions.h" HRESULT PROTOCOL_CONFIG::Initialize() { - HRESULT hr; - STRU strTemp; - m_fKeepAlive = TRUE; m_msTimeout = 120000; m_fPreserveHostHeader = TRUE; m_fReverseRewriteHeaders = FALSE; - if (FAILED(hr = m_strXForwardedForName.CopyW(L"X-Forwarded-For"))) - { - goto Finished; - } - - if (FAILED(hr = m_strSslHeaderName.CopyW(L"X-Forwarded-Proto"))) - { - goto Finished; - } - - if (FAILED(hr = m_strClientCertName.CopyW(L"MS-ASPNETCORE-CLIENTCERT"))) - { - goto Finished; - } + RETURN_IF_FAILED(m_strXForwardedForName.CopyW(L"X-Forwarded-For")); + RETURN_IF_FAILED(m_strSslHeaderName.CopyW(L"X-Forwarded-Proto")); + RETURN_IF_FAILED(m_strClientCertName.CopyW(L"MS-ASPNETCORE-CLIENTCERT")); m_fIncludePortInXForwardedFor = TRUE; m_dwMinResponseBuffer = 0; // no response buffering m_dwResponseBufferLimit = 4096*1024; m_dwMaxResponseHeaderSize = 65536; - -Finished: - - return hr; + return S_OK; } VOID diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/responseheaderhash.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/responseheaderhash.cpp index d8c77eccb0..55dcc2fbd4 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/responseheaderhash.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/responseheaderhash.cpp @@ -2,8 +2,9 @@ // Licensed under the MIT License. See License.txt in the project root for license information. #include "responseheaderhash.h" +#include "exceptions.h" -HEADER_RECORD RESPONSE_HEADER_HASH::sm_rgHeaders[] = +HEADER_RECORD RESPONSE_HEADER_HASH::sm_rgHeaders[] = { { "Cache-Control", HttpHeaderCacheControl }, { "Connection", HttpHeaderConnection }, @@ -65,8 +66,6 @@ Return Value: --*/ { - HRESULT hr; - // // 31 response headers. // Make sure to update the number of buckets it new headers @@ -79,20 +78,13 @@ Return Value: // Known collisions are "Age" colliding with "Expire" and "Location" // colliding with both "Expire" and "Age". // - hr = HASH_TABLE::Initialize(79); - if (FAILED(hr)) - { - return hr; - } + RETURN_IF_FAILED(HASH_TABLE::Initialize(79)); for ( DWORD Index = 0; Index < _countof(sm_rgHeaders); ++Index ) { - if (FAILED(hr = InsertRecord(&sm_rgHeaders[Index]))) - { - return hr; - } + RETURN_IF_FAILED(InsertRecord(&sm_rgHeaders[Index])); } - + return S_OK; } diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp index 03abd7fd23..494911472a 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp @@ -6,6 +6,7 @@ #include #include "EventLog.h" #include "file_utility.h" +#include "exceptions.h" //#include //extern BOOL g_fNsiApiNotSupported; @@ -45,13 +46,13 @@ SERVER_PROCESS::Initialize( m_pProcessManager->ReferenceProcessManager(); m_fDebuggerAttached = FALSE; - if (FAILED(hr = m_ProcessPath.Copy(*pszProcessExePath)) || - FAILED(hr = m_struLogFile.Copy(*pstruStdoutLogFile))|| - FAILED(hr = m_struPhysicalPath.Copy(*pszAppPhysicalPath))|| - FAILED(hr = m_struAppFullPath.Copy(*pszAppPath))|| - FAILED(hr = m_struAppVirtualPath.Copy(*pszAppVirtualPath))|| - FAILED(hr = m_Arguments.Copy(*pszArguments)) || - FAILED(hr = SetupJobObject())) + if (FAILED_LOG(hr = m_ProcessPath.Copy(*pszProcessExePath)) || + FAILED_LOG(hr = m_struLogFile.Copy(*pstruStdoutLogFile))|| + FAILED_LOG(hr = m_struPhysicalPath.Copy(*pszAppPhysicalPath))|| + FAILED_LOG(hr = m_struAppFullPath.Copy(*pszAppPath))|| + FAILED_LOG(hr = m_struAppVirtualPath.Copy(*pszAppVirtualPath))|| + FAILED_LOG(hr = m_Arguments.Copy(*pszArguments)) || + FAILED_LOG(hr = SetupJobObject())) { goto Finished; } @@ -155,7 +156,7 @@ SERVER_PROCESS::SetupListenPort( pEnvironmentVarTable->FindKey(ASPNETCORE_PORT_ENV_STR, &pEntry); if (pEntry != NULL) { - if (pEntry->QueryValue() != NULL || pEntry->QueryValue()[0] != L'\0') + if (pEntry->QueryValue() != NULL && pEntry->QueryValue()[0] != L'\0') { m_dwPort = (DWORD)_wtoi(pEntry->QueryValue()); if (m_dwPort >MAX_PORT || m_dwPort < MIN_PORT) @@ -174,13 +175,13 @@ SERVER_PROCESS::SetupListenPort( // user set the env variable but did not give value, let's set it up // pEnvironmentVarTable->DeleteKey(ASPNETCORE_PORT_ENV_STR); + pEntry->Dereference(); + pEntry = NULL; } - pEntry->Dereference(); - pEntry = NULL; } WCHAR buffer[15]; - if (FAILED(hr = GetRandomPort(&m_dwPort))) + if (FAILED_LOG(hr = GetRandomPort(&m_dwPort))) { goto Finished; } @@ -198,9 +199,9 @@ SERVER_PROCESS::SetupListenPort( goto Finished; } - if (FAILED(hr = pEntry->Initialize(ASPNETCORE_PORT_ENV_STR, buffer)) || - FAILED(hr = pEnvironmentVarTable->InsertRecord(pEntry)) || - FAILED(hr = m_struPort.Copy(buffer))) + if (FAILED_LOG(hr = pEntry->Initialize(ASPNETCORE_PORT_ENV_STR, buffer)) || + FAILED_LOG(hr = pEnvironmentVarTable->InsertRecord(pEntry)) || + FAILED_LOG(hr = m_struPort.Copy(buffer))) { goto Finished; } @@ -212,7 +213,7 @@ Finished: pEntry = NULL; } - if (FAILED(hr)) + if (FAILED_LOG(hr)) { EventLog::Error( ASPNETCORE_EVENT_PROCESS_START_SUCCESS, @@ -252,8 +253,8 @@ SERVER_PROCESS::SetupAppPath( goto Finished; } - if (FAILED(hr = pEntry->Initialize(ASPNETCORE_APP_PATH_ENV_STR, m_struAppVirtualPath.QueryStr())) || - FAILED(hr = pEnvironmentVarTable->InsertRecord(pEntry))) + if (FAILED_LOG(hr = pEntry->Initialize(ASPNETCORE_APP_PATH_ENV_STR, m_struAppVirtualPath.QueryStr())) || + FAILED_LOG(hr = pEnvironmentVarTable->InsertRecord(pEntry))) { goto Finished; } @@ -311,7 +312,7 @@ SERVER_PROCESS::SetupAppToken( fRpcStringAllocd = TRUE; - if (FAILED(hr = m_straGuid.Copy(pszLogUuid))) + if (FAILED_LOG(hr = m_straGuid.Copy(pszLogUuid))) { goto Finished; } @@ -324,9 +325,9 @@ SERVER_PROCESS::SetupAppToken( goto Finished; } - if (FAILED(strAppToken.CopyA(m_straGuid.QueryStr())) || - FAILED(hr = pEntry->Initialize(ASPNETCORE_APP_TOKEN_ENV_STR, strAppToken.QueryStr())) || - FAILED(hr = pEnvironmentVarTable->InsertRecord(pEntry))) + if (FAILED_LOG(strAppToken.CopyA(m_straGuid.QueryStr())) || + FAILED_LOG(hr = pEntry->Initialize(ASPNETCORE_APP_TOKEN_ENV_STR, strAppToken.QueryStr())) || + FAILED_LOG(hr = pEnvironmentVarTable->InsertRecord(pEntry))) { goto Finished; } @@ -382,7 +383,7 @@ SERVER_PROCESS::OutputEnvironmentVariables pszEqualChar = wcschr(pszCurrentVariable, L'='); if (pszEqualChar != NULL) { - if (FAILED(hr = strEnvVar.Copy(pszCurrentVariable, (DWORD)(pszEqualChar - pszCurrentVariable) + 1))) + if (FAILED_LOG(hr = strEnvVar.Copy(pszCurrentVariable, (DWORD)(pszEqualChar - pszCurrentVariable) + 1))) { goto Finished; } @@ -390,7 +391,7 @@ SERVER_PROCESS::OutputEnvironmentVariables if (pEntry != NULL) { // same env variable is defined in configuration, use it - if (FAILED(hr = strEnvVar.Append(pEntry->QueryValue()))) + if (FAILED_LOG(hr = strEnvVar.Append(pEntry->QueryValue()))) { goto Finished; } @@ -453,9 +454,9 @@ SERVER_PROCESS::SetupCommandLine( if ((wcsstr(pszPath, L":") == NULL) && (wcsstr(pszPath, L"%") == NULL)) { // let's check whether it is a relative path - if (FAILED(hr = strRelativePath.Copy(m_struPhysicalPath.QueryStr())) || - FAILED(hr = strRelativePath.Append(L"\\")) || - FAILED(hr = strRelativePath.Append(pszPath))) + if (FAILED_LOG(hr = strRelativePath.Copy(m_struPhysicalPath.QueryStr())) || + FAILED_LOG(hr = strRelativePath.Append(L"\\")) || + FAILED_LOG(hr = strRelativePath.Append(pszPath))) { goto Finished; } @@ -482,9 +483,9 @@ SERVER_PROCESS::SetupCommandLine( pszPath = pszFullPath; } } - if (FAILED(hr = pstrCommandLine->Copy(pszPath)) || - FAILED(hr = pstrCommandLine->Append(L" ")) || - FAILED(hr = pstrCommandLine->Append(m_Arguments.QueryStr()))) + if (FAILED_LOG(hr = pstrCommandLine->Copy(pszPath)) || + FAILED_LOG(hr = pstrCommandLine->Append(L" ")) || + FAILED_LOG(hr = pstrCommandLine->Append(m_Arguments.QueryStr()))) { goto Finished; } @@ -566,7 +567,7 @@ SERVER_PROCESS::PostStartCheck( } // register call back with the created process - if (FAILED(hr = RegisterProcessWait(&m_hProcessWaitHandle, m_hProcessHandle))) + if (FAILED_LOG(hr = RegisterProcessWait(&m_hProcessWaitHandle, m_hProcessHandle))) { goto Finished; } @@ -595,7 +596,7 @@ SERVER_PROCESS::PostStartCheck( if (!fProcessMatch) { // could be the scenario that backend creates child process - if (FAILED(hr = GetChildProcessHandles())) + if (FAILED_LOG(hr = GetChildProcessHandles())) { goto Finished; } @@ -617,7 +618,7 @@ SERVER_PROCESS::PostStartCheck( fDebuggerAttached = FALSE; } - if (FAILED(hr = RegisterProcessWait(&m_hChildProcessWaitHandles[i], + if (FAILED_LOG(hr = RegisterProcessWait(&m_hChildProcessWaitHandles[i], m_hChildProcessHandles[i]))) { goto Finished; @@ -678,7 +679,7 @@ SERVER_PROCESS::PostStartCheck( hr = CheckIfServerIsUp(m_dwPort, &dwActualProcessId, &fReady); - if ((FAILED(hr) || fReady == FALSE)) + if ((FAILED_LOG(hr) || fReady == FALSE)) { strEventMsg.SafeSnwprintf( ASPNETCORE_EVENT_PROCESS_START_NOTREADY_ERROR_MSG, @@ -705,7 +706,7 @@ SERVER_PROCESS::PostStartCheck( } hr = m_pForwarderConnection->Initialize(m_dwPort); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } @@ -726,7 +727,7 @@ SERVER_PROCESS::PostStartCheck( Finished: m_fDebuggerAttached = fDebuggerAttached; - if (FAILED(hr)) + if (FAILED_LOG(hr)) { if (m_pForwarderConnection != NULL) { @@ -773,13 +774,13 @@ SERVER_PROCESS::StartProcess( // // generate process command line. // - if (FAILED(hr = SetupCommandLine(&m_struCommandLine))) + if (FAILED_LOG(hr = SetupCommandLine(&m_struCommandLine))) { pStrStage = L"SetupCommandLine"; goto Failure; } - if (FAILED(hr = ENVIRONMENT_VAR_HELPERS::InitEnvironmentVariablesTable( + if (FAILED_LOG(hr = ENVIRONMENT_VAR_HELPERS::InitEnvironmentVariablesTable( m_pEnvironmentVarTable, m_fWindowsAuthEnabled, m_fBasicAuthEnabled, @@ -790,7 +791,7 @@ SERVER_PROCESS::StartProcess( goto Failure; } - if (FAILED(hr = ENVIRONMENT_VAR_HELPERS::AddWebsocketEnabledToEnvironmentVariables( + if (FAILED_LOG(hr = ENVIRONMENT_VAR_HELPERS::AddWebsocketEnabledToEnvironmentVariables( pHashTable, m_fWebSocketSupported ))) @@ -803,7 +804,7 @@ SERVER_PROCESS::StartProcess( // // setup the the port that the backend process will listen on // - if (FAILED(hr = SetupListenPort(pHashTable, &fCriticalError))) + if (FAILED_LOG(hr = SetupListenPort(pHashTable, &fCriticalError))) { pStrStage = L"SetupListenPort"; goto Failure; @@ -812,7 +813,7 @@ SERVER_PROCESS::StartProcess( // // get app path // - if (FAILED(hr = SetupAppPath(pHashTable))) + if (FAILED_LOG(hr = SetupAppPath(pHashTable))) { pStrStage = L"SetupAppPath"; goto Failure; @@ -821,7 +822,7 @@ SERVER_PROCESS::StartProcess( // // generate new guid for each process // - if (FAILED(hr = SetupAppToken(pHashTable))) + if (FAILED_LOG(hr = SetupAppToken(pHashTable))) { pStrStage = L"SetupAppToken"; goto Failure; @@ -830,7 +831,7 @@ SERVER_PROCESS::StartProcess( // // setup environment variables for new process // - if (FAILED(hr = OutputEnvironmentVariables(&mszNewEnvironment, pHashTable))) + if (FAILED_LOG(hr = OutputEnvironmentVariables(&mszNewEnvironment, pHashTable))) { pStrStage = L"OutputEnvironmentVariables"; goto Failure; @@ -861,7 +862,7 @@ SERVER_PROCESS::StartProcess( m_hProcessHandle = processInformation.hProcess; m_dwProcessId = processInformation.dwProcessId; - if (FAILED(hr = SetupJobObject())) + if (FAILED_LOG(hr = SetupJobObject())) { pStrStage = L"SetupJobObject"; goto Failure; @@ -890,7 +891,7 @@ SERVER_PROCESS::StartProcess( // // need to make sure the server is up and listening on the port specified. // - if (FAILED(hr = PostStartCheck())) + if (FAILED_LOG(hr = PostStartCheck())) { pStrStage = L"PostStartCheck"; goto Failure; @@ -944,7 +945,7 @@ SERVER_PROCESS::StartProcess( } Finished: - if (FAILED(hr) || m_fReady == FALSE) + if (FAILED_LOG(hr) || m_fReady == FALSE) { if (m_hStdoutHandle != NULL) { @@ -1034,7 +1035,7 @@ SERVER_PROCESS::SetupStdHandles( m_struLogFile.QueryStr(), m_struPhysicalPath.QueryStr(), &struPath); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } @@ -1049,13 +1050,13 @@ SERVER_PROCESS::SetupStdHandles( systemTime.wMinute, systemTime.wSecond, GetCurrentProcessId()); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } hr = FILE_UTILITY::EnsureDirectoryPathExist(struPath.QueryStr()); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } @@ -1086,7 +1087,7 @@ SERVER_PROCESS::SetupStdHandles( m_Timer.InitializeTimer(STTIMER::TimerCallback, &m_struFullLogFile, 3000, 3000); Finished: - if (FAILED(hr)) + if (FAILED_LOG(hr)) { pStartupInfo->dwFlags = STARTF_USESTDHANDLES; pStartupInfo->hStdInput = INVALID_HANDLE_VALUE; @@ -1305,7 +1306,7 @@ Finished: hThread = NULL; } - if (FAILED(hr)) + if (FAILED_LOG(hr)) { TerminateBackendProcess(); } @@ -1874,7 +1875,7 @@ SERVER_PROCESS::RegisterProcessWait( Finished: - if (FAILED(hr)) + if (FAILED_LOG(hr)) { *phWaitHandle = NULL; DereferenceServerProcess(); @@ -1981,10 +1982,10 @@ SERVER_PROCESS::SendShutdownHttpMessage( VOID ) } // set up the shutdown headers - if (FAILED(hr = strHeaders.Append(L"MS-ASPNETCORE-EVENT:shutdown \r\n")) || - FAILED(hr = strAppToken.Append(L"MS-ASPNETCORE-TOKEN:")) || - FAILED(hr = strAppToken.AppendA(m_straGuid.QueryStr())) || - FAILED(hr = strHeaders.Append(strAppToken.QueryStr()))) + if (FAILED_LOG(hr = strHeaders.Append(L"MS-ASPNETCORE-EVENT:shutdown \r\n")) || + FAILED_LOG(hr = strAppToken.Append(L"MS-ASPNETCORE-TOKEN:")) || + FAILED_LOG(hr = strAppToken.AppendA(m_straGuid.QueryStr())) || + FAILED_LOG(hr = strHeaders.Append(strAppToken.QueryStr()))) { goto Finished; } @@ -2072,7 +2073,7 @@ SERVER_PROCESS::SendShutDownSignalInternal( { ReferenceServerProcess(); - if (FAILED(SendShutdownHttpMessage())) + if (FAILED_LOG(SendShutdownHttpMessage())) { // // failed to send shutdown http message diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/url_utility.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/url_utility.cpp index c5b3a06e86..64118b61b6 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/url_utility.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/url_utility.cpp @@ -5,6 +5,7 @@ #include #include "debugutil.h" +#include "exceptions.h" // static HRESULT @@ -38,8 +39,6 @@ Return Value: --*/ { - HRESULT hr; - // // First determine if the target is secure // @@ -55,12 +54,12 @@ Return Value: } else { - return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + RETURN_HR(HRESULT_FROM_WIN32(ERROR_INVALID_DATA)); } if (*pszDestinationUrl == L'\0') { - return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + RETURN_HR(HRESULT_FROM_WIN32(ERROR_INVALID_DATA)); } // @@ -69,20 +68,14 @@ Return Value: LPCWSTR pszSlash = wcschr(pszDestinationUrl, L'/'); if (pszSlash == NULL) { - if (FAILED(hr = pstrUrl->Copy(L"/", 1)) || - FAILED(hr = pstrDestination->Copy(pszDestinationUrl))) - { - return hr; - } + RETURN_IF_FAILED(pstrUrl->Copy(L"/", 1)); + RETURN_IF_FAILED(pstrDestination->Copy(pszDestinationUrl)); } else { - if (FAILED(hr = pstrUrl->Copy(pszSlash)) || - FAILED(hr = pstrDestination->Copy(pszDestinationUrl, - (DWORD)(pszSlash - pszDestinationUrl)))) - { - return hr; - } + RETURN_IF_FAILED(pstrUrl->Copy(pszSlash)); + RETURN_IF_FAILED(pstrDestination->Copy(pszDestinationUrl, + (DWORD)(pszSlash - pszDestinationUrl))); } return S_OK; @@ -102,34 +95,28 @@ URL_UTILITY::EscapeAbsPath( STRU * strEscapedUrl ) { - HRESULT hr = S_OK; STRU strAbsPath; LPCWSTR pszAbsPath = NULL; LPCWSTR pszFindStr = NULL; - hr = strAbsPath.Copy( pRequest->GetRawHttpRequest()->CookedUrl.pAbsPath, - pRequest->GetRawHttpRequest()->CookedUrl.AbsPathLength / sizeof(WCHAR) ); - if(FAILED(hr)) - { - goto Finished; - } + RETURN_IF_FAILED(strAbsPath.Copy( pRequest->GetRawHttpRequest()->CookedUrl.pAbsPath, + pRequest->GetRawHttpRequest()->CookedUrl.AbsPathLength / sizeof(WCHAR) )); pszAbsPath = strAbsPath.QueryStr(); pszFindStr = wcschr(pszAbsPath, L'?'); while(pszFindStr != NULL) { - strEscapedUrl->Append( pszAbsPath, pszFindStr - pszAbsPath); - strEscapedUrl->Append(L"%3F"); + RETURN_IF_FAILED(strEscapedUrl->Append( pszAbsPath, pszFindStr - pszAbsPath)); + RETURN_IF_FAILED(strEscapedUrl->Append(L"%3F")); pszAbsPath = pszFindStr + 1; pszFindStr = wcschr(pszAbsPath, L'?'); } - strEscapedUrl->Append(pszAbsPath); - strEscapedUrl->Append(pRequest->GetRawHttpRequest()->CookedUrl.pQueryString, - pRequest->GetRawHttpRequest()->CookedUrl.QueryStringLength / sizeof(WCHAR)); + RETURN_IF_FAILED(strEscapedUrl->Append(pszAbsPath)); + RETURN_IF_FAILED(strEscapedUrl->Append(pRequest->GetRawHttpRequest()->CookedUrl.pQueryString, + pRequest->GetRawHttpRequest()->CookedUrl.QueryStringLength / sizeof(WCHAR))); -Finished: - return hr; + return S_OK; } diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/websockethandler.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/websockethandler.cpp index f58d599f98..5eac6001e9 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/websockethandler.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/websockethandler.cpp @@ -28,6 +28,7 @@ This prevents the need for data buffering at the Asp.Net Core Module level. --*/ #include "websockethandler.h" +#include "exceptions.h" SRWLOCK WEBSOCKET_HANDLER::sm_RequestsListLock; @@ -86,7 +87,7 @@ HRESULT WEBSOCKET_HANDLER::StaticInitialize( BOOL fEnableReferenceCountTracing ) -/*++ +/*++ Routine Description: @@ -145,7 +146,7 @@ WEBSOCKET_HANDLER::InsertRequest( } } -//static +//static VOID WEBSOCKET_HANDLER::RemoveRequest( VOID @@ -244,11 +245,11 @@ WEBSOCKET_HANDLER::ProcessRequest( Routine Description: Entry point to WebSocket Handler: - + This routine is called after the 101 response was successfully sent to - the client. - This routine get's a websocket handle to winhttp, - websocket handle to IIS's websocket context, and initiates IO + the client. + This routine get's a websocket handle to winhttp, + websocket handle to IIS's websocket context, and initiates IO in these two endpoints. @@ -267,10 +268,10 @@ Routine Description: // // Cache the points to IHttpContext3 // - hr = HttpGetExtendedInterface(g_pHttpServer, - pHttpContext, + hr = HttpGetExtendedInterface(g_pHttpServer, + pHttpContext, &_pHttpContext); - if (FAILED (hr)) + if (FAILED_LOG(hr)) { goto Finished; } @@ -338,7 +339,7 @@ Routine Description: // Initiate Read on IIS // hr = DoIisWebSocketReceive(); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } @@ -348,7 +349,7 @@ Routine Description: // hr = DoWinHttpWebSocketReceive(); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } @@ -356,7 +357,7 @@ Routine Description: Finished: LeaveCriticalSection(&_RequestLock); - if (FAILED (hr)) + if (FAILED_LOG(hr)) { DebugPrintf (ASPNETCORE_DEBUG_FLAG_ERROR, "Process Request Failed with HR=%08x", hr); @@ -399,7 +400,7 @@ Routine Description: OnReadIoCompletion, this, NULL); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { DecrementOutstandingIo(); DebugPrintf(ASPNETCORE_DEBUG_FLAG_ERROR, @@ -498,7 +499,7 @@ Routine Description: // hr = strCloseReason.CopyA((PCSTR)&_WinHttpReceiveBuffer, dwReceived); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } @@ -549,13 +550,13 @@ Routine Description: NULL); } - if (FAILED(hr)) + if (FAILED_LOG(hr)) { DecrementOutstandingIo(); } Finished: - if (FAILED(hr)) + if (FAILED_LOG(hr)) { DebugPrintf(ASPNETCORE_DEBUG_FLAG_ERROR, "WEBSOCKET_HANDLER::DoIisWebSocketSend failed with %08x", hr); @@ -592,10 +593,10 @@ Routine Description: // // Get Close status from IIS. // - hr = _pWebSocketContext->GetCloseStatus(&uStatus, + hr = _pWebSocketContext->GetCloseStatus(&uStatus, &pszReason); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } @@ -604,7 +605,7 @@ Routine Description: // Convert status to UTF8 // hr = strCloseReason.CopyWToUTF8Unescaped(pszReason); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } @@ -663,7 +664,7 @@ Routine Description: } Finished: - if (FAILED(hr)) + if (FAILED_LOG(hr)) { DebugPrintf(ASPNETCORE_DEBUG_FLAG_ERROR, "WEBSOCKET_HANDLER::DoWinHttpWebSocketSend failed with %08x", hr); @@ -691,7 +692,7 @@ WEBSOCKET_HANDLER::OnReadIoCompletion( --*/ { - WEBSOCKET_HANDLER * pHandler = (WEBSOCKET_HANDLER *) + WEBSOCKET_HANDLER * pHandler = (WEBSOCKET_HANDLER *) pvCompletionContext; pHandler->OnIisReceiveComplete( @@ -721,7 +722,7 @@ WEBSOCKET_HANDLER::OnWriteIoCompletion( --*/ { - WEBSOCKET_HANDLER * pHandler = (WEBSOCKET_HANDLER *) + WEBSOCKET_HANDLER * pHandler = (WEBSOCKET_HANDLER *) pvCompletionContext; UNREFERENCED_PARAMETER(fUTF8Encoded); @@ -776,18 +777,18 @@ Routine Description: // hr = DoIisWebSocketReceive(); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { goto Finished; } Finished: - if (fLocked) + if (fLocked) { LeaveCriticalSection(&_RequestLock); } - if (FAILED (hr)) + if (FAILED_LOG(hr)) { Cleanup (cleanupReason); @@ -848,7 +849,7 @@ Routine Description: Issue send on the Client(IIS) if the receive was successful. - If the receive completed with zero bytes, that + If the receive completed with zero bytes, that indicates that the server has disconnected the connection. Issue cleanup for the websocket handler. --*/ @@ -877,18 +878,18 @@ Routine Description: pCompletionStatus->eBufferType ); - if (FAILED (hr)) + if (FAILED_LOG(hr)) { cleanupReason = ClientDisconnect; goto Finished; } Finished: - if (fLocked) + if (fLocked) { LeaveCriticalSection(&_RequestLock); } - if (FAILED (hr)) + if (FAILED_LOG(hr)) { Cleanup (cleanupReason); @@ -917,7 +918,7 @@ Routine Description: Completion callback executed when a send completes from the client. - If send was successful,issue read on the + If send was successful,issue read on the server endpoint, to continue the readloop. --*/ @@ -930,7 +931,7 @@ Routine Description: DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO, "WEBSOCKET_HANDLER::OnIisSendComplete"); - if (FAILED(hrCompletion)) + if (FAILED_LOG(hrCompletion)) { hr = hrCompletion; cleanupReason = ClientDisconnect; @@ -957,7 +958,7 @@ Routine Description: // Write Completed, initiate next read from backend server. // hr = DoWinHttpWebSocketReceive(); - if (FAILED(hr)) + if (FAILED_LOG(hr)) { cleanupReason = ServerDisconnect; goto Finished; @@ -969,7 +970,7 @@ Finished: { LeaveCriticalSection(&_RequestLock); } - if (FAILED (hr)) + if (FAILED_LOG(hr)) { Cleanup (cleanupReason); @@ -1016,7 +1017,7 @@ Routine Description: DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO, "WEBSOCKET_HANDLER::OnIisReceiveComplete"); - if (FAILED(hrCompletion)) + if (FAILED_LOG(hrCompletion)) { cleanupReason = ClientDisconnect; hr = hrCompletion; @@ -1029,7 +1030,7 @@ Routine Description: } EnterCriticalSection(&_RequestLock); - + fLocked = TRUE; if (_fCleanupInProgress) { @@ -1049,7 +1050,7 @@ Routine Description: // hr = DoWinHttpWebSocketSend(cbIO, BufferType); - if (FAILED (hr)) + if (FAILED_LOG(hr)) { cleanupReason = ServerDisconnect; goto Finished; @@ -1060,7 +1061,7 @@ Finished: { LeaveCriticalSection(&_RequestLock); } - if (FAILED (hr)) + if (FAILED_LOG(hr)) { Cleanup (cleanupReason); diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/winhttphelper.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/winhttphelper.cpp index 8d14ffc417..8f6c5d3720 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/winhttphelper.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/winhttphelper.cpp @@ -2,6 +2,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. #include "winhttphelper.h" +#include "exceptions.h" PFN_WINHTTP_WEBSOCKET_COMPLETE_UPGRADE WINHTTP_HELPER::sm_pfnWinHttpWebSocketCompleteUpgrade; @@ -24,8 +25,6 @@ WINHTTP_HELPER::StaticInitialize( VOID ) { - HRESULT hr = S_OK; - // // Initialize the function pointers for WinHttp Websocket API's. // @@ -35,54 +34,29 @@ WINHTTP_HELPER::StaticInitialize( } HMODULE hWinHttp = GetModuleHandleA("winhttp.dll"); - if (hWinHttp == NULL) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; - } + RETURN_LAST_ERROR_IF (hWinHttp == NULL); - sm_pfnWinHttpWebSocketCompleteUpgrade = (PFN_WINHTTP_WEBSOCKET_COMPLETE_UPGRADE) + sm_pfnWinHttpWebSocketCompleteUpgrade = (PFN_WINHTTP_WEBSOCKET_COMPLETE_UPGRADE) GetProcAddress(hWinHttp, "WinHttpWebSocketCompleteUpgrade"); - if (sm_pfnWinHttpWebSocketCompleteUpgrade == NULL) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; - } + RETURN_LAST_ERROR_IF (sm_pfnWinHttpWebSocketCompleteUpgrade == NULL); sm_pfnWinHttpWebSocketQueryCloseStatus = (PFN_WINHTTP_WEBSOCKET_QUERY_CLOSE_STATUS) GetProcAddress(hWinHttp, "WinHttpWebSocketQueryCloseStatus"); - if (sm_pfnWinHttpWebSocketQueryCloseStatus == NULL) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; - } + RETURN_LAST_ERROR_IF (sm_pfnWinHttpWebSocketQueryCloseStatus == NULL); sm_pfnWinHttpWebSocketReceive = (PFN_WINHTTP_WEBSOCKET_RECEIVE) GetProcAddress(hWinHttp, "WinHttpWebSocketReceive"); - if (sm_pfnWinHttpWebSocketReceive == NULL) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; - } + RETURN_LAST_ERROR_IF (sm_pfnWinHttpWebSocketReceive == NULL); sm_pfnWinHttpWebSocketSend = (PFN_WINHTTP_WEBSOCKET_SEND) GetProcAddress(hWinHttp, "WinHttpWebSocketSend"); - if (sm_pfnWinHttpWebSocketSend == NULL) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; - } + RETURN_LAST_ERROR_IF (sm_pfnWinHttpWebSocketSend == NULL); sm_pfnWinHttpWebSocketShutdown = (PFN_WINHTTP_WEBSOCKET_SHUTDOWN) GetProcAddress(hWinHttp, "WinHttpWebSocketShutdown"); - if (sm_pfnWinHttpWebSocketShutdown == NULL) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; - } + RETURN_LAST_ERROR_IF (sm_pfnWinHttpWebSocketShutdown == NULL); -Finished: - return hr; + return S_OK; } @@ -170,6 +144,4 @@ WINHTTP_HELPER::GetBufferTypeFromFlags( *pBufferType = WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE; } } - - return; } diff --git a/src/AspNetCoreModuleV2/RequestHandlerLib/FileOutputManager.cpp b/src/AspNetCoreModuleV2/RequestHandlerLib/FileOutputManager.cpp deleted file mode 100644 index 29041f7ab5..0000000000 --- a/src/AspNetCoreModuleV2/RequestHandlerLib/FileOutputManager.cpp +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -#include "stdafx.h" -#include "FileOutputManager.h" -#include "sttimer.h" -#include "exceptions.h" -#include "debugutil.h" -#include "SRWExclusiveLock.h" -#include "file_utility.h" - -FileOutputManager::FileOutputManager() : - m_hLogFileHandle(INVALID_HANDLE_VALUE), - m_fdPreviousStdOut(-1), - m_fdPreviousStdErr(-1), - m_disposed(false) -{ - InitializeSRWLock(&m_srwLock); -} - -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; -} - -bool FileOutputManager::GetStdOutContent(STRA* struStdOutput) -{ - // - // Ungraceful shutdown, try to log an error message. - // This will be a common place for errors as it means the hostfxr_main returned - // or there was an exception. - // - CHAR pzFileContents[4096] = { 0 }; - DWORD dwNumBytesRead; - LARGE_INTEGER li = { 0 }; - BOOL fLogged = FALSE; - DWORD dwFilePointer = 0; - - if (m_hLogFileHandle != INVALID_HANDLE_VALUE) - { - if (GetFileSizeEx(m_hLogFileHandle, &li) && li.LowPart > 0 && li.HighPart == 0) - { - if (li.LowPart > 4096) - { - dwFilePointer = SetFilePointer(m_hLogFileHandle, -4096, NULL, FILE_END); - } - else - { - dwFilePointer = SetFilePointer(m_hLogFileHandle, 0, NULL, FILE_BEGIN); - } - - if (dwFilePointer != INVALID_SET_FILE_POINTER) - { - // Read file fails. - if (ReadFile(m_hLogFileHandle, pzFileContents, 4096, &dwNumBytesRead, NULL)) - { - if (SUCCEEDED(struStdOutput->Copy(pzFileContents, dwNumBytesRead))) - { - fLogged = TRUE; - } - } - } - } - } - - return fLogged; -} - -HRESULT -FileOutputManager::Start() -{ - SYSTEMTIME systemTime; - SECURITY_ATTRIBUTES saAttr = { 0 }; - STRU struPath; - - RETURN_IF_FAILED(FILE_UTILITY::ConvertPathToFullPath( - m_wsStdOutLogFileName.QueryStr(), - m_wsApplicationPath.QueryStr(), - &struPath)); - - RETURN_IF_FAILED(FILE_UTILITY::EnsureDirectoryPathExist(struPath.QueryStr())); - - GetSystemTime(&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())); - - m_fdPreviousStdOut = _dup(_fileno(stdout)); - m_fdPreviousStdErr = _dup(_fileno(stderr)); - - m_hLogFileHandle = CreateFileW(m_struLogFilePath.QueryStr(), - FILE_READ_DATA | FILE_WRITE_DATA, - FILE_SHARE_READ, - &saAttr, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL, - NULL); - - if (m_hLogFileHandle == INVALID_HANDLE_VALUE) - { - return LOG_IF_FAILED(HRESULT_FROM_WIN32(GetLastError())); - } - - // There are a few options for redirecting stdout/stderr, - // but there are issues with most of them. - // AllocConsole() - // *stdout = *m_pStdFile; - // *stderr = *m_pStdFile; - // Calling _dup2 on stderr fails on IIS. IIS sets stderr to -2 - // _dup2(_fileno(m_pStdFile), _fileno(stdout)); - // _dup2(_fileno(m_pStdFile), _fileno(stderr)); - // If we were okay setting stdout and stderr to separate files, we could use: - // _wfreopen_s(&m_pStdFile, struLogFileName.QueryStr(), L"w+", stdout); - // _wfreopen_s(&m_pStdFile, struLogFileName.QueryStr(), L"w+", stderr); - // Calling SetStdHandle works for redirecting managed logs, however you cannot - // capture native logs (including hostfxr failures). - - RETURN_LAST_ERROR_IF(!SetStdHandle(STD_OUTPUT_HANDLE, m_hLogFileHandle)); - - RETURN_LAST_ERROR_IF(!SetStdHandle(STD_ERROR_HANDLE, m_hLogFileHandle)); - - // Periodically flush the log content to file - m_Timer.InitializeTimer(STTIMER::TimerCallback, &m_struLogFilePath, 3000, 3000); - - LOG_INFOF("Created log file for inprocess application: %S", - m_struLogFilePath.QueryStr()); - - return S_OK; -} - - -HRESULT -FileOutputManager::Stop() -{ - if (m_disposed) - { - return S_OK; - } - SRWExclusiveLock lock(m_srwLock); - if (m_disposed) - { - return S_OK; - } - m_disposed = true; - - HANDLE handle = NULL; - WIN32_FIND_DATA fileData; - - if (m_hLogFileHandle != INVALID_HANDLE_VALUE) - { - m_Timer.CancelTimer(); - } - - // delete empty log file - handle = FindFirstFile(m_struLogFilePath.QueryStr(), &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())); - } - - if (m_fdPreviousStdOut >= 0) - { - LOG_LAST_ERROR_IF(!SetStdHandle(STD_OUTPUT_HANDLE, (HANDLE)_get_osfhandle(m_fdPreviousStdOut))); - LOG_INFOF("Restoring original stdout: %d", m_fdPreviousStdOut); - } - - if (m_fdPreviousStdErr >= 0) - { - LOG_LAST_ERROR_IF(!SetStdHandle(STD_ERROR_HANDLE, (HANDLE)_get_osfhandle(m_fdPreviousStdErr))); - LOG_INFOF("Restoring original stderr: %d", m_fdPreviousStdOut); - } - - return S_OK; -} diff --git a/src/AspNetCoreModuleV2/RequestHandlerLib/RequestHandlerLib.vcxproj b/src/AspNetCoreModuleV2/RequestHandlerLib/RequestHandlerLib.vcxproj index 56025b071a..a2c89e2b40 100644 --- a/src/AspNetCoreModuleV2/RequestHandlerLib/RequestHandlerLib.vcxproj +++ b/src/AspNetCoreModuleV2/RequestHandlerLib/RequestHandlerLib.vcxproj @@ -193,22 +193,14 @@ - - - - - - - - diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs index 8c07bec312..743944d971 100644 --- a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs @@ -44,6 +44,11 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS } public override void Dispose() + { + Dispose(gracefulShutdown: false); + } + + public override void Dispose(bool gracefulShutdown) { Stop(); @@ -128,6 +133,8 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS } } + + private void GetLogsFromFile() { try diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployerBase.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployerBase.cs index be6f39edec..d0745b4f01 100644 --- a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployerBase.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployerBase.cs @@ -151,5 +151,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS .GetOrAdd("add", "name", ancmVersion) .SetAttributeValue("image", GetAncmLocation(DeploymentParameters.AncmVersion)); } + + public abstract void Dispose(bool gracefulShutdown); } } diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeploymentParameterExtensions.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeploymentParameterExtensions.cs index f1e90f1a24..485c61cf4c 100644 --- a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeploymentParameterExtensions.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeploymentParameterExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.IO; using System.Linq; using System.Xml.Linq; @@ -50,5 +51,14 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS .SetAttributeValue("enabled", "true"); }); } + + public static void EnableLogging(this IISDeploymentParameters deploymentParameters, string path) + { + deploymentParameters.WebConfigActionList.Add( + WebConfigHelpers.AddOrModifyAspNetCoreSection("stdoutLogEnabled", "true")); + + deploymentParameters.WebConfigActionList.Add( + WebConfigHelpers.AddOrModifyAspNetCoreSection("stdoutLogFile", Path.Combine(path, "std"))); + } } } diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeploymentParameters.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeploymentParameters.cs index 30e3f13c25..3b2dfc4804 100644 --- a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeploymentParameters.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeploymentParameters.cs @@ -37,7 +37,6 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS ServerConfigActionList = tempParameters.ServerConfigActionList; WebConfigBasedEnvironmentVariables = tempParameters.WebConfigBasedEnvironmentVariables; HandlerSettings = tempParameters.HandlerSettings; - GracefulShutdown = tempParameters.GracefulShutdown; } } @@ -49,7 +48,5 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS public IDictionary HandlerSettings { get; set; } = new Dictionary(); - public bool GracefulShutdown { get; set; } - } } diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISExpressDeployer.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISExpressDeployer.cs index fd2aca9b4c..797c46dde9 100644 --- a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISExpressDeployer.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISExpressDeployer.cs @@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS private const string FailedToInitializeBindingsMessage = "Failed to initialize site bindings"; private const string UnableToStartIISExpressMessage = "Unable to start iisexpress."; private const int MaximumAttempts = 5; - private readonly TimeSpan ShutdownTimeSpan = TimeSpan.FromSeconds(60); + private readonly TimeSpan ShutdownTimeSpan = Debugger.IsAttached ? TimeSpan.FromMinutes(60) : TimeSpan.FromMinutes(1); private static readonly Regex UrlDetectorRegex = new Regex(@"^\s*Successfully registered URL ""(?[^""]+)"" for site.*$"); private Process _hostProcess; @@ -381,10 +381,15 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS } public override void Dispose() + { + Dispose(gracefulShutdown: false); + } + + public override void Dispose(bool gracefulShutdown) { using (Logger.BeginScope("Dispose")) { - if (IISDeploymentParameters.GracefulShutdown) + if (gracefulShutdown) { GracefullyShutdownProcess(_hostProcess); } diff --git a/test/Common.FunctionalTests/Inprocess/EventLogTests.cs b/test/Common.FunctionalTests/Inprocess/EventLogTests.cs index 3139f3b251..77abc90190 100644 --- a/test/Common.FunctionalTests/Inprocess/EventLogTests.cs +++ b/test/Common.FunctionalTests/Inprocess/EventLogTests.cs @@ -23,8 +23,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests public async Task CheckStartupEventLogMessage() { var deploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true); + var deploymentResult = await DeployAsync(deploymentParameters); - await Helpers.AssertStarts(deploymentResult); + await deploymentResult.AssertStarts(); StopServer(); @@ -35,9 +36,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests public async Task CheckShutdownEventLogMessage() { var deploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true); - deploymentParameters.GracefulShutdown = true; var deploymentResult = await DeployAsync(deploymentParameters); - await Helpers.AssertStarts(deploymentResult); + await deploymentResult.AssertStarts(); StopServer(); diff --git a/test/Common.FunctionalTests/Inprocess/LoggingTests.cs b/test/Common.FunctionalTests/Inprocess/LoggingTests.cs index 0a19a90133..7ef360a487 100644 --- a/test/Common.FunctionalTests/Inprocess/LoggingTests.cs +++ b/test/Common.FunctionalTests/Inprocess/LoggingTests.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -47,20 +46,14 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests StopServer(); - var fileInDirectory = Directory.GetFiles(pathToLogs).Single(); - + var fileInDirectory = Directory.GetFiles(pathToLogs).Single(fileName => fileName.Contains("inprocess")); var contents = File.ReadAllText(fileInDirectory); Assert.NotNull(contents); Assert.Contains("TEST MESSAGE", contents); - Assert.DoesNotContain(TestSink.Writes, context => context.Message.Contains("TEST MESSAGE")); - // TODO we should check that debug logs are restored during graceful shutdown. - // The IIS Express deployer doesn't support graceful shutdown. - //Assert.Contains(TestSink.Writes, context => context.Message.Contains("Restoring original stdout: ")); } finally { - RetryHelper.RetryOperation( () => Directory.Delete(pathToLogs, true), e => Logger.LogWarning($"Failed to delete directory : {e.Message}"), @@ -132,7 +125,6 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests public async Task CheckStdoutLoggingToPipe_DoesNotCrashProcess(string path) { var deploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true); - deploymentParameters.GracefulShutdown = true; var deploymentResult = await DeployAsync(deploymentParameters); await Helpers.AssertStarts(deploymentResult, path); @@ -151,7 +143,6 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests public async Task CheckStdoutLoggingToPipeWithFirstWrite(string path) { var deploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true); - deploymentParameters.GracefulShutdown = true; var firstWriteString = path + path; diff --git a/test/Common.FunctionalTests/MultiApplicationTests.cs b/test/Common.FunctionalTests/MultiApplicationTests.cs index 009c61f373..e0adccfc54 100644 --- a/test/Common.FunctionalTests/MultiApplicationTests.cs +++ b/test/Common.FunctionalTests/MultiApplicationTests.cs @@ -44,6 +44,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests { var parameters = _fixture.GetBaseDeploymentParameters(HostingModel.InProcess, publish: true); parameters.ServerConfigActionList.Add(DuplicateApplication); + var result = await DeployAsync(parameters); var result1 = await result.HttpClient.GetAsync("/app1/HelloWorld"); var result2 = await result.HttpClient.GetAsync("/app2/HelloWorld"); diff --git a/test/Common.FunctionalTests/OutOfProcess/AspNetCorePortTests.cs b/test/Common.FunctionalTests/OutOfProcess/AspNetCorePortTests.cs index 89824163ed..3b86662717 100644 --- a/test/Common.FunctionalTests/OutOfProcess/AspNetCorePortTests.cs +++ b/test/Common.FunctionalTests/OutOfProcess/AspNetCorePortTests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Net; using System.Net.Sockets; using System.Threading.Tasks; @@ -16,6 +17,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests [Collection(PublishedSitesCollection.Name)] public class AspNetCorePortTests : IISFunctionalTestBase { + // Port range allowed by ANCM config + private const int _minPort = 1025; + private const int _maxPort = 48000; + private static readonly Random _random = new Random(); private readonly PublishedSitesFixture _fixture; @@ -31,9 +36,14 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests .WithApplicationTypes(ApplicationType.Portable) .WithAllAncmVersions(); + public static IEnumerable InvalidTestVariants + => from v in TestVariants.Select(v => v.Single()) + from s in new string[] { (_minPort - 1).ToString(), (_maxPort + 1).ToString(), "noninteger" } + select new object[] { v, s }; + [ConditionalTheory] [MemberData(nameof(TestVariants))] - public async Task EnvVarInWebConfig(TestVariant variant) + public async Task EnvVarInWebConfig_Valid(TestVariant variant) { // Must publish to set env vars in web.config var deploymentParameters = _fixture.GetBaseDeploymentParameters(variant, publish: true); @@ -42,18 +52,44 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests var deploymentResult = await DeployAsync(deploymentParameters); - var response = await deploymentResult.HttpClient.GetAsync("/ServerAddresses"); - var responseText = await response.Content.ReadAsStringAsync(); + var responseText = await deploymentResult.HttpClient.GetStringAsync("/ServerAddresses"); Assert.Equal(port, new Uri(responseText).Port); } + [ConditionalTheory] + [MemberData(nameof(TestVariants))] + public async Task EnvVarInWebConfig_Empty(TestVariant variant) + { + // Must publish to set env vars in web.config + var deploymentParameters = _fixture.GetBaseDeploymentParameters(variant, publish: true); + deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_PORT"] = string.Empty; + + var deploymentResult = await DeployAsync(deploymentParameters); + + var responseText = await deploymentResult.HttpClient.GetStringAsync("/ServerAddresses"); + + // If env var is empty, ANCM should assign a random port (same as no env var) + Assert.InRange(new Uri(responseText).Port, _minPort, _maxPort); + } + + [ConditionalTheory] + [MemberData(nameof(InvalidTestVariants))] + public async Task EnvVarInWebConfig_Invalid(TestVariant variant, string port) + { + // Must publish to set env vars in web.config + var deploymentParameters = _fixture.GetBaseDeploymentParameters(variant, publish: true); + deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_PORT"] = port; + + var deploymentResult = await DeployAsync(deploymentParameters); + + var response = await deploymentResult.HttpClient.GetAsync("/ServerAddresses"); + + Assert.Equal(HttpStatusCode.BadGateway, response.StatusCode); + } + private static int GetUnusedRandomPort() { - // Port range allowed by ANCM config - const int minPort = 1025; - const int maxPort = 48000; - // Large number of retries to prevent test failures due to port collisions, but not infinite // to prevent infinite loop in case Bind() fails repeatedly for some other reason. const int retries = 100; @@ -62,7 +98,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests for (var i = 0; i < retries; i++) { - var port = _random.Next(minPort, maxPort); + var port = _random.Next(_minPort, _maxPort); using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { diff --git a/test/Common.FunctionalTests/Utilities/EventLogHelpers.cs b/test/Common.FunctionalTests/Utilities/EventLogHelpers.cs index 9d8d79fb6c..c449863e4b 100644 --- a/test/Common.FunctionalTests/Utilities/EventLogHelpers.cs +++ b/test/Common.FunctionalTests/Utilities/EventLogHelpers.cs @@ -1,6 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Linq; +using System.Text; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.Extensions.Logging.Testing; @@ -14,17 +16,16 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests { Assert.True(deploymentResult.HostProcess.HasExited); - var eventLogRegex = new Regex($"Event Log: {expectedRegexMatchString}"); + var builder = new StringBuilder(); - int count = 0; foreach (var context in testSink.Writes) { - if (eventLogRegex.IsMatch(context.Message)) - { - count++; - } + builder.Append(context.Message); } - Assert.Equal(1, count); + + var eventLogRegex = new Regex($"Event Log: (.*?){expectedRegexMatchString}(.*?)End Event Log Message.", RegexOptions.Singleline); + + Assert.Matches(eventLogRegex, builder.ToString()); } } } diff --git a/test/Common.FunctionalTests/Utilities/FunctionalTestsBase.cs b/test/Common.FunctionalTests/Utilities/FunctionalTestsBase.cs index a9b6b3a4fe..b9e80dd27f 100644 --- a/test/Common.FunctionalTests/Utilities/FunctionalTestsBase.cs +++ b/test/Common.FunctionalTests/Utilities/FunctionalTestsBase.cs @@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting { } - protected ApplicationDeployer _deployer; + protected IISDeployerBase _deployer; protected ApplicationDeployer CreateDeployer(IISDeploymentParameters parameters) { @@ -38,18 +38,18 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting protected virtual async Task DeployAsync(IISDeploymentParameters parameters) { - _deployer = CreateDeployer(parameters); + _deployer = (IISDeployerBase)CreateDeployer(parameters); return (IISDeploymentResult)await _deployer.DeployAsync(); } public override void Dispose() { - StopServer(); + StopServer(false); } - public void StopServer() + public void StopServer(bool gracefulShutdown = true) { - _deployer?.Dispose(); + _deployer?.Dispose(gracefulShutdown); _deployer = null; } } diff --git a/test/Common.FunctionalTests/Utilities/SkipIfDebugAttribute.cs b/test/Common.FunctionalTests/Utilities/SkipIfDebugAttribute.cs new file mode 100644 index 0000000000..6cdd725392 --- /dev/null +++ b/test/Common.FunctionalTests/Utilities/SkipIfDebugAttribute.cs @@ -0,0 +1,22 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Server.IntegrationTesting; +using Microsoft.AspNetCore.Testing.xunit; + +namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +{ + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] + public sealed class SkipIfDebugAttribute : Attribute, ITestCondition + { + public bool IsMet => + #if DEBUG + false; + #else + true; + #endif + + public string SkipReason => "Test cannot be run in Debug mode."; + } +} diff --git a/test/CommonLibTests/FileOutputManagerTests.cpp b/test/CommonLibTests/FileOutputManagerTests.cpp index ae1085408c..bba9e22704 100644 --- a/test/CommonLibTests/FileOutputManagerTests.cpp +++ b/test/CommonLibTests/FileOutputManagerTests.cpp @@ -34,6 +34,7 @@ namespace FileOutManagerStartupTests auto tempDirectory = TempDirectory(); FileOutputManager* pManager = new FileOutputManager; + pManager->Initialize(fileNamePrefix.c_str(), tempDirectory.path().c_str()); { FileManagerWrapper wrapper(pManager); @@ -47,19 +48,17 @@ namespace FileOutManagerStartupTests ASSERT_EQ(filename.substr(0, fileNamePrefix.size()), fileNamePrefix); std::wstring content = Helpers::ReadFileContent(std::wstring(p.path())); - ASSERT_EQ(content.length(), DWORD(4)); - ASSERT_STREQ(content.c_str(), expected); } } }; - TEST_F(FileOutputManagerTest, DISABLED_WriteToFileCheckContentsWritten) + TEST_F(FileOutputManagerTest, WriteToFileCheckContentsWritten) { Test(L"", stdout); Test(L"log", stdout); } - TEST_F(FileOutputManagerTest, DISABLED_WriteToFileCheckContentsWrittenErr) + TEST_F(FileOutputManagerTest, WriteToFileCheckContentsWrittenErr) { Test(L"", stderr); Test(L"log", stderr); @@ -68,8 +67,7 @@ namespace FileOutManagerStartupTests namespace FileOutManagerOutputTests { - - TEST(FileOutManagerOutputTest, DISABLED_StdErr) + TEST(FileOutManagerOutputTest, StdOut) { PCSTR expected = "test"; @@ -80,7 +78,9 @@ namespace FileOutManagerOutputTests { FileManagerWrapper wrapper(pManager); - printf(expected, stderr); + fprintf(stdout, expected); + pManager->Stop(); + STRA straContent; ASSERT_TRUE(pManager->GetStdOutContent(&straContent)); @@ -88,7 +88,7 @@ namespace FileOutManagerOutputTests } } - TEST(FileOutManagerOutputTest, DISABLED_CheckFileOutput) + TEST(FileOutManagerOutputTest, StdErr) { PCSTR expected = "test"; @@ -99,7 +99,9 @@ namespace FileOutManagerOutputTests { FileManagerWrapper wrapper(pManager); - printf(expected); + fprintf(stderr, expected); + pManager->Stop(); + STRA straContent; ASSERT_TRUE(pManager->GetStdOutContent(&straContent)); @@ -107,9 +109,9 @@ namespace FileOutManagerOutputTests } } - TEST(FileOutManagerOutputTest, DISABLED_CapAt4KB) + TEST(FileOutManagerOutputTest, CapAt30KB) { - PCSTR expected = "test"; + PCSTR expected = "hello world"; auto tempDirectory = TempDirectory(); @@ -118,16 +120,39 @@ namespace FileOutManagerOutputTests { FileManagerWrapper wrapper(pManager); - for (int i = 0; i < 1200; i++) + for (int i = 0; i < 3000; i++) { printf(expected); } - + pManager->Stop(); STRA straContent; ASSERT_TRUE(pManager->GetStdOutContent(&straContent)); - ASSERT_EQ(straContent.QueryCCH(), 4096); + ASSERT_EQ(straContent.QueryCCH(), 30000); + } + } + + + TEST(FileOutManagerOutputTest, StartStopRestoresCorrectly) + { + PCSTR expected = "test"; + + auto tempDirectory = TempDirectory(); + + for (int i = 0; i < 10; i++) + { + FileOutputManager* pManager = new FileOutputManager; + pManager->Initialize(L"", tempDirectory.path().c_str()); + { + FileManagerWrapper wrapper(pManager); + + printf(expected); + pManager->Stop(); + STRA straContent; + ASSERT_TRUE(pManager->GetStdOutContent(&straContent)); + + ASSERT_STREQ(straContent.QueryStr(), expected); + } } } } - diff --git a/test/CommonLibTests/PipeOutputManagerTests.cpp b/test/CommonLibTests/PipeOutputManagerTests.cpp index 6cc09e7678..20609adf72 100644 --- a/test/CommonLibTests/PipeOutputManagerTests.cpp +++ b/test/CommonLibTests/PipeOutputManagerTests.cpp @@ -23,14 +23,58 @@ public: namespace PipeOutputManagerTests { - TEST(PipeManagerOutputTest, NotifyStartupCompleteCallsDispose) + TEST(PipeManagerOutputTest, StdOut) { PCWSTR expected = L"test"; + STRA output; + + PipeOutputManager* pManager = new PipeOutputManager(true); - PipeOutputManager* pManager = new PipeOutputManager(); ASSERT_EQ(S_OK, pManager->Start()); + fwprintf(stdout, expected); + + ASSERT_EQ(S_OK, pManager->Stop()); + + pManager->GetStdOutContent(&output); + ASSERT_STREQ(output.QueryStr(), "test"); + delete pManager; } + TEST(PipeManagerOutputTest, StdErr) + { + PCWSTR expected = L"test"; + STRA output; + + PipeOutputManager* pManager = new PipeOutputManager(); + + ASSERT_EQ(S_OK, pManager->Start()); + fwprintf(stderr, expected); + ASSERT_EQ(S_OK, pManager->Stop()); + + pManager->GetStdOutContent(&output); + ASSERT_STREQ(output.QueryStr(), "test"); + delete pManager; + } + + TEST(PipeManagerOutputTest, CheckMaxPipeSize) + { + std::wstring test; + STRA output; + for (int i = 0; i < 3000; i++) + { + test.append(L"hello world"); + } + + PipeOutputManager* pManager = new PipeOutputManager(); + + ASSERT_EQ(S_OK, pManager->Start()); + wprintf(test.c_str()); + ASSERT_EQ(S_OK, pManager->Stop()); + + pManager->GetStdOutContent(&output); + ASSERT_EQ(output.QueryCCH(), (DWORD)30000); + delete pManager; + } TEST(PipeManagerOutputTest, SetInvalidHandlesForErrAndOut) { auto m_fdPreviousStdOut = _dup(_fileno(stdout)); @@ -50,6 +94,61 @@ namespace PipeOutputManagerTests // Test will fail if we didn't redirect stdout back to a file descriptor. // This is because gtest relies on console output to know if a test succeeded or failed. // If the output still points to a file/pipe, the test (and all other tests after it) will fail. + delete pManager; + } + + TEST(PipeManagerOutputTest, CreateDeleteMultipleTimesStdOutWorks) + { + for (int i = 0; i < 10; i++) + { + auto stdoutBefore = _fileno(stdout); + auto stderrBefore = _fileno(stderr); + PCWSTR expected = L"test"; + STRA output; + + PipeOutputManager* pManager = new PipeOutputManager(); + + ASSERT_EQ(S_OK, pManager->Start()); + fwprintf(stdout, expected); + + ASSERT_EQ(S_OK, pManager->Stop()); + + pManager->GetStdOutContent(&output); + ASSERT_STREQ(output.QueryStr(), "test"); + ASSERT_EQ(stdoutBefore, _fileno(stdout)); + ASSERT_EQ(stderrBefore, _fileno(stderr)); + delete pManager; + } + // When this returns, we get an AV from gtest. + } + + TEST(PipeManagerOutputTest, CreateDeleteKeepOriginalStdErr) + { + for (int i = 0; i < 10; i++) + { + auto stdoutBefore = _fileno(stdout); + auto stderrBefore = _fileno(stderr); + 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()); + fwprintf(stderr, expected); + ASSERT_EQ(S_OK, pManager->Stop()); + + pManager->GetStdOutContent(&output); + ASSERT_STREQ(output.QueryStr(), "test"); + ASSERT_EQ(stdoutBefore, _fileno(stdout)); + + ASSERT_EQ(stderrBefore, _fileno(stderr)); + + delete pManager; + } + + wprintf(L"Hello!"); } } diff --git a/test/IIS.FunctionalTests/Inprocess/StdOutRedirectionTests.cs b/test/IIS.FunctionalTests/Inprocess/StdOutRedirectionTests.cs new file mode 100644 index 0000000000..e0cb1cef5d --- /dev/null +++ b/test/IIS.FunctionalTests/Inprocess/StdOutRedirectionTests.cs @@ -0,0 +1,171 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; +using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests; +using Microsoft.AspNetCore.Server.IntegrationTesting; +using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; +using Microsoft.AspNetCore.Testing.xunit; +using Newtonsoft.Json; +using Xunit; + +namespace IIS.FunctionalTests.Inprocess +{ + [Collection(PublishedSitesCollection.Name)] + public class StdOutRedirectionTests : IISFunctionalTestBase + { + private readonly PublishedSitesFixture _fixture; + private readonly string _logFolderPath; + + public StdOutRedirectionTests(PublishedSitesFixture fixture) + { + _logFolderPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + _fixture = fixture; + } + + public override void Dispose() + { + base.Dispose(); + if (Directory.Exists(_logFolderPath)) + { + Directory.Delete(_logFolderPath, true); + } + } + + [ConditionalFact] + [SkipIfDebug] + public async Task FrameworkNotFoundExceptionLogged_Pipe() + { + var deploymentParameters = _fixture.GetBaseDeploymentParameters(_fixture.StartupExceptionWebsite, publish: true); + + var deploymentResult = await DeployAsync(deploymentParameters); + + InvalidateRuntimeConfig(deploymentResult); + + var response = await deploymentResult.HttpClient.GetAsync("/"); + Assert.False(response.IsSuccessStatusCode); + + StopServer(); + + EventLogHelpers.VerifyEventLogEvent(deploymentResult, TestSink, + "The specified framework 'Microsoft.NETCore.App', version '2.9.9' was not found."); + } + + [ConditionalFact] + [SkipIfDebug] + public async Task FrameworkNotFoundExceptionLogged_File() + { + var deploymentParameters = + _fixture.GetBaseDeploymentParameters(_fixture.StartupExceptionWebsite, publish: true); + + deploymentParameters.EnableLogging(_logFolderPath); + + var deploymentResult = await DeployAsync(deploymentParameters); + + InvalidateRuntimeConfig(deploymentResult); + + var response = await deploymentResult.HttpClient.GetAsync("/"); + Assert.False(response.IsSuccessStatusCode); + + StopServer(); + + var fileInDirectory = Directory.GetFiles(_logFolderPath).Single(); + var contents = File.ReadAllText(fileInDirectory); + var expectedString = "The specified framework 'Microsoft.NETCore.App', version '2.9.9' was not found."; + EventLogHelpers.VerifyEventLogEvent(deploymentResult, TestSink, expectedString); + Assert.Contains(expectedString, contents); + } + + [ConditionalFact] + [SkipIfDebug] + public async Task EnableCoreHostTraceLogging_TwoLogFilesCreated() + { + var deploymentParameters = + _fixture.GetBaseDeploymentParameters(_fixture.StartupExceptionWebsite, publish: true); + deploymentParameters.EnvironmentVariables["COREHOST_TRACE"] = "1"; + + deploymentParameters.EnableLogging(_logFolderPath); + + var deploymentResult = await DeployAsync(deploymentParameters); + + var response = await deploymentResult.HttpClient.GetAsync("/"); + Assert.False(response.IsSuccessStatusCode); + + StopServer(); + + var filesInDirectory = Directory.GetFiles(_logFolderPath); + Assert.Equal(2, filesInDirectory.Length); + foreach (var file in filesInDirectory) + { + var contents = File.ReadAllText(file); + EventLogHelpers.VerifyEventLogEvent(deploymentResult, TestSink, "Invoked hostfxr"); + Assert.Contains("Invoked hostfxr", contents); + } + } + + [ConditionalTheory] + [SkipIfDebug] + [InlineData("CheckLargeStdErrWrites")] + [InlineData("CheckLargeStdOutWrites")] + [InlineData("CheckOversizedStdErrWrites")] + [InlineData("CheckOversizedStdOutWrites")] + public async Task EnableCoreHostTraceLogging_PipeCaptureNativeLogs(string path) + { + var deploymentParameters = _fixture.GetBaseDeploymentParameters(_fixture.StartupExceptionWebsite, publish: true); + deploymentParameters.EnvironmentVariables["COREHOST_TRACE"] = "1"; + deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_INPROCESS_STARTUP_VALUE"] = path; + + var deploymentResult = await DeployAsync(deploymentParameters); + + var response = await deploymentResult.HttpClient.GetAsync("/"); + + Assert.False(response.IsSuccessStatusCode); + + StopServer(); + + EventLogHelpers.VerifyEventLogEvent(deploymentResult, TestSink, "Invoked hostfxr"); + } + + [ConditionalTheory] + [SkipIfDebug] + [InlineData("CheckLargeStdErrWrites")] + [InlineData("CheckLargeStdOutWrites")] + [InlineData("CheckOversizedStdErrWrites")] + [InlineData("CheckOversizedStdOutWrites")] + public async Task EnableCoreHostTraceLogging_FileCaptureNativeLogs(string path) + { + var deploymentParameters = + _fixture.GetBaseDeploymentParameters(_fixture.StartupExceptionWebsite, publish: true); + deploymentParameters.EnvironmentVariables["COREHOST_TRACE"] = "1"; + deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_INPROCESS_STARTUP_VALUE"] = path; + + deploymentParameters.EnableLogging(_logFolderPath); + + var deploymentResult = await DeployAsync(deploymentParameters); + + var response = await deploymentResult.HttpClient.GetAsync("/"); + Assert.False(response.IsSuccessStatusCode); + + StopServer(); + + var fileInDirectory = Directory.GetFiles(_logFolderPath).First(); + var contents = File.ReadAllText(fileInDirectory); + + EventLogHelpers.VerifyEventLogEvent(deploymentResult, TestSink, "Invoked hostfxr"); + Assert.Contains("Invoked hostfxr", contents); + } + + private static void InvalidateRuntimeConfig(IISDeploymentResult deploymentResult) + { + var path = Path.Combine(deploymentResult.ContentRoot, "StartupExceptionWebSite.runtimeconfig.json"); + dynamic depsFileContent = JsonConvert.DeserializeObject(File.ReadAllText(path)); + depsFileContent["runtimeOptions"]["framework"]["version"] = "2.9.9"; + var output = JsonConvert.SerializeObject(depsFileContent); + File.WriteAllText(path, output); + } + } +} diff --git a/test/IISExpress.FunctionalTests/InProcess/ShutdownTests.cs b/test/IISExpress.FunctionalTests/InProcess/ShutdownTests.cs index d9dbcd2b6e..1e5af7c13f 100644 --- a/test/IISExpress.FunctionalTests/InProcess/ShutdownTests.cs +++ b/test/IISExpress.FunctionalTests/InProcess/ShutdownTests.cs @@ -42,22 +42,21 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests public async Task GracefulShutdown_DoesNotCrashProcess() { var parameters = _fixture.GetBaseDeploymentParameters(publish: true); - parameters.GracefulShutdown = true; var result = await DeployAsync(parameters); var response = await result.HttpClient.GetAsync("/HelloWorld"); - StopServer(); + StopServer(gracefulShutdown: true); Assert.True(result.HostProcess.ExitCode == 0); } [ConditionalFact] - public async Task ForcefulShutdown_DoesrashProcess() + public async Task ForcefulShutdown_DoesCrashProcess() { var parameters = _fixture.GetBaseDeploymentParameters(publish: true); var result = await DeployAsync(parameters); var response = await result.HttpClient.GetAsync("/HelloWorld"); - StopServer(); + StopServer(gracefulShutdown: false); Assert.True(result.HostProcess.ExitCode == 1); } } diff --git a/version.props b/version.props index e37dbcf611..958113fee1 100644 --- a/version.props +++ b/version.props @@ -8,8 +8,8 @@ a- $(FeatureBranchVersionPrefix)$(VersionSuffix)-$([System.Text.RegularExpressions.Regex]::Replace('$(FeatureBranchVersionSuffix)', '[^\w-]', '-')) $(VersionSuffix)-$(BuildNumber) - 8 - 1 + 12 + 2 0 2.0.0