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

[automated] Merge branch 'release/2.2' => 'master'
This commit is contained in:
Pavel Krymets 2018-07-11 15:25:26 -07:00 committed by GitHub
commit 3e16ca9c18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 937 additions and 876 deletions

View File

@ -40,7 +40,7 @@
</ItemGroup>
<PropertyGroup>
<IISExpressArguments>/config:"$(IISExpressAppHostConfig)"</IISExpressArguments>
<IISExpressArguments>/config:"$(IISExpressAppHostConfig)" /systray:false</IISExpressArguments>
<IISArguments>-h "$(IISAppHostConfig)"</IISArguments>
<AncmPath>$(NativePlatform)\aspnetcore.dll</AncmPath>

View File

@ -226,11 +226,9 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="Inc\applicationinfo.h" />
<ClInclude Include="Inc\appoffline.h" />
<ClInclude Include="Inc\aspnetcore_shim_config.h" />
<ClInclude Include="inc\globalmodule.h" />
<ClInclude Include="Inc\applicationmanager.h" />
<ClInclude Include="Inc\filewatcher.h" />
<ClInclude Include="Inc\proxymodule.h" />
<ClInclude Include="Inc\precomp.hxx" />
</ItemGroup>
@ -239,7 +237,6 @@
<ClCompile Include="Src\applicationmanager.cxx" />
<ClCompile Include="src\aspnetcore_shim_config.cpp" />
<ClCompile Include="Src\dllmain.cpp" />
<ClCompile Include="Src\filewatcher.cxx" />
<ClCompile Include="src\globalmodule.cpp" />
<ClCompile Include="src\precomp.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>

View File

@ -3,17 +3,12 @@
#pragma once
#include <httpserv.h>
#include "hostfxroptions.h"
#include "appoffline.h"
#include "filewatcher.h"
#include "hashtable.h"
#include "hashfn.h"
#include "aspnetcore_shim_config.h"
#include "iapplication.h"
#include "SRWSharedLock.h"
#include "ntassert.h"
#define API_BUFFER_TOO_SMALL 0x80008098
@ -37,11 +32,8 @@ public:
APPLICATION_INFO() :
m_pServer(NULL),
m_cRefs(1),
m_fAppOfflineFound(FALSE),
m_fValid(FALSE),
m_fDoneAppCreation(FALSE),
m_pAppOfflineHtm(NULL),
m_pFileWatcherEntry(NULL),
m_fAppCreationAttempted(FALSE),
m_pConfiguration(NULL),
m_pfnAspNetCoreCreateApplication(NULL)
{
@ -60,8 +52,7 @@ public:
HRESULT
Initialize(
_In_ IHttpServer *pServer,
_In_ IHttpApplication *pApplication,
_In_ FILE_WATCHER *pFileWatcher
_In_ IHttpApplication *pApplication
);
VOID
@ -79,18 +70,6 @@ public:
}
}
APP_OFFLINE_HTM*
QueryAppOfflineHtm()
{
return m_pAppOfflineHtm;
}
BOOL
AppOfflineFound()
{
return m_fAppOfflineFound;
}
BOOL
IsValid()
{
@ -103,33 +82,22 @@ public:
m_fValid = TRUE;
}
VOID
UpdateAppOfflineFileHandle();
HRESULT
StartMonitoringAppOffline();
ASPNETCORE_SHIM_CONFIG*
QueryConfig()
{
return m_pConfiguration;
}
//
// ExtractApplication will increase the reference counter of the application
// Caller is responsible for dereference the application.
// Otherwise memory leak
//
VOID
ExtractApplication(IAPPLICATION** ppApplication)
std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER>
ExtractApplication() const
{
SRWSharedLock lock(m_srwLock);
if (m_pApplication != NULL)
{
m_pApplication->ReferenceApplication();
}
*ppApplication = m_pApplication;
return ReferenceApplication(m_pApplication);
}
VOID
@ -152,11 +120,8 @@ private:
mutable LONG m_cRefs;
STRU m_struInfoKey;
BOOL m_fAppOfflineFound;
BOOL m_fValid;
BOOL m_fDoneAppCreation;
APP_OFFLINE_HTM *m_pAppOfflineHtm;
FILE_WATCHER_ENTRY *m_pFileWatcherEntry;
BOOL m_fAppCreationAttempted;
ASPNETCORE_SHIM_CONFIG *m_pConfiguration;
IAPPLICATION *m_pApplication;
SRWLOCK m_srwLock;

View File

@ -5,7 +5,7 @@
#include "applicationinfo.h"
#include "multisz.h"
#include "filewatcher.h"
#include "exceptions.h"
#define DEFAULT_HASH_BUCKETS 17
@ -83,11 +83,6 @@ public:
~APPLICATION_MANAGER()
{
if (m_pFileWatcher != NULL)
{
delete m_pFileWatcher;
m_pFileWatcher = NULL;
}
if(m_pApplicationInfoHash != NULL)
{
@ -97,41 +92,18 @@ public:
}
}
FILE_WATCHER*
GetFileWatcher()
{
return m_pFileWatcher;
}
HRESULT Initialize()
{
HRESULT hr = S_OK;
if(m_pApplicationInfoHash == NULL)
{
m_pApplicationInfoHash = new APPLICATION_INFO_HASH();
hr = m_pApplicationInfoHash->Initialize(DEFAULT_HASH_BUCKETS);
if(FAILED(hr))
try
{
goto Finished;
m_pApplicationInfoHash = new APPLICATION_INFO_HASH();
}
CATCH_RETURN();
RETURN_IF_FAILED(m_pApplicationInfoHash->Initialize(DEFAULT_HASH_BUCKETS));
}
if( m_pFileWatcher == NULL )
{
m_pFileWatcher = new FILE_WATCHER;
if(m_pFileWatcher == NULL)
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto Finished;
}
m_pFileWatcher->Create();
}
Finished:
return hr;
return S_OK;
}
private:
@ -139,13 +111,11 @@ private:
// we currently limit the size of m_pstrErrorInfo to 5000, be careful if you want to change its payload
//
APPLICATION_MANAGER() : m_pApplicationInfoHash(NULL),
m_pFileWatcher(NULL),
m_hostingModel(HOSTING_UNKNOWN)
m_hostingModel(HOSTING_UNKNOWN)
{
InitializeSRWLock(&m_srwLock);
}
FILE_WATCHER *m_pFileWatcher;
APPLICATION_INFO_HASH *m_pApplicationInfoHash;
static APPLICATION_MANAGER *sm_pApplicationManager;
SRWLOCK m_srwLock;

View File

@ -1,105 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
#include "stringa.h"
#include "stringu.h"
class APP_OFFLINE_HTM
{
public:
APP_OFFLINE_HTM(LPCWSTR pszPath) : m_cRefs(1)
{
m_Path.Copy(pszPath);
}
VOID
ReferenceAppOfflineHtm() const
{
InterlockedIncrement(&m_cRefs);
}
VOID
DereferenceAppOfflineHtm() const
{
if (InterlockedDecrement(&m_cRefs) == 0)
{
delete this;
}
}
BOOL
Load(
VOID
)
{
BOOL fResult = TRUE;
LARGE_INTEGER li = { 0 };
CHAR *pszBuff = NULL;
HANDLE handle = INVALID_HANDLE_VALUE;
handle = CreateFile(m_Path.QueryStr(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (handle == INVALID_HANDLE_VALUE)
{
if (HRESULT_FROM_WIN32(GetLastError()) == ERROR_FILE_NOT_FOUND)
{
fResult = FALSE;
}
// This Load() member function is supposed be called only when the change notification event of file creation or file modification happens.
// If file is currenlty locked exclusively by other processes, we might get INVALID_HANDLE_VALUE even though the file exists. In that case, we should return TRUE here.
goto Finished;
}
if (!GetFileSizeEx(handle, &li))
{
goto Finished;
}
if (li.HighPart != 0)
{
// > 4gb file size not supported
// todo: log a warning at event log
goto Finished;
}
DWORD bytesRead = 0;
if (li.LowPart > 0)
{
pszBuff = new CHAR[li.LowPart + 1];
if (ReadFile(handle, pszBuff, li.LowPart, &bytesRead, NULL))
{
m_Contents.Copy(pszBuff, bytesRead);
}
}
Finished:
if (handle != INVALID_HANDLE_VALUE)
{
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
}
if (pszBuff != NULL)
{
delete[] pszBuff;
pszBuff = NULL;
}
return fResult;
}
mutable LONG m_cRefs;
STRA m_Contents;
STRU m_Path;
};

View File

@ -16,8 +16,9 @@
#include <Windows.h>
#include <atlbase.h>
#include <httpserv.h>
#include <ntassert.h>
#include "stringu.h"
#include "stringu.h"
#include "stringa.h"
extern PVOID g_pModuleId;
extern BOOL g_fAspnetcoreRHAssemblyLoaded;

View File

@ -3,6 +3,7 @@
#pragma once
#include <memory>
#include "applicationinfo.h"
#include "irequesthandler.h"
@ -50,7 +51,7 @@ class ASPNET_CORE_PROXY_MODULE : public CHttpModule
APPLICATION_INFO *m_pApplicationInfo;
IAPPLICATION *m_pApplication;
IREQUEST_HANDLER *m_pHandler;
std::unique_ptr<IREQUEST_HANDLER, IREQUEST_HANDLER_DELETER> m_pHandler;
};
class ASPNET_CORE_PROXY_MODULE_FACTORY : public IHttpModuleFactory

View File

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

View File

@ -12,29 +12,14 @@
#include "SRWExclusiveLock.h"
#include "GlobalVersionUtility.h"
#include "exceptions.h"
#include "HandleWrapper.h"
#include "PollingAppOfflineApplication.h"
const PCWSTR APPLICATION_INFO::s_pwzAspnetcoreInProcessRequestHandlerName = L"aspnetcorev2_inprocess.dll";
const PCWSTR APPLICATION_INFO::s_pwzAspnetcoreOutOfProcessRequestHandlerName = L"aspnetcorev2_outofprocess.dll";
APPLICATION_INFO::~APPLICATION_INFO()
{
if (m_pAppOfflineHtm != NULL)
{
m_pAppOfflineHtm->DereferenceAppOfflineHtm();
m_pAppOfflineHtm = NULL;
}
if (m_pFileWatcherEntry != NULL)
{
// Mark the entry as invalid,
// StopMonitor will close the file handle and trigger a FCN
// the entry will delete itself when processing this FCN
m_pFileWatcherEntry->MarkEntryInValid();
m_pFileWatcherEntry->StopMonitor();
m_pFileWatcherEntry->DereferenceFileWatcherEntry();
m_pFileWatcherEntry = NULL;
}
if (m_pApplication != NULL)
{
// shutdown the application
@ -55,118 +40,24 @@ APPLICATION_INFO::~APPLICATION_INFO()
HRESULT
APPLICATION_INFO::Initialize(
_In_ IHttpServer *pServer,
_In_ IHttpApplication *pApplication,
_In_ FILE_WATCHER *pFileWatcher
_In_ IHttpApplication *pApplication
)
{
HRESULT hr = S_OK;
DBG_ASSERT(pServer);
DBG_ASSERT(pApplication);
DBG_ASSERT(pFileWatcher);
// todo: make sure Initialize should be called only once
m_pServer = pServer;
FINISHED_IF_NULL_ALLOC(m_pConfiguration = new ASPNETCORE_SHIM_CONFIG());
FINISHED_IF_FAILED(m_pConfiguration->Populate(m_pServer, pApplication));
FINISHED_IF_FAILED(m_struInfoKey.Copy(pApplication->GetApplicationId()));
FINISHED_IF_NULL_ALLOC(m_pFileWatcherEntry = new FILE_WATCHER_ENTRY(pFileWatcher));
UpdateAppOfflineFileHandle();
Finished:
return hr;
}
HRESULT
APPLICATION_INFO::StartMonitoringAppOffline()
{
if (m_pFileWatcherEntry != NULL)
{
RETURN_IF_FAILED(m_pFileWatcherEntry->Create(m_pConfiguration->QueryApplicationPhysicalPath()->QueryStr(), L"app_offline.htm", this, NULL));
}
return S_OK;
}
//
// Called by the file watcher when the app_offline.htm's file status has been changed.
// If it finds it, we will call recycle on the application.
//
VOID
APPLICATION_INFO::UpdateAppOfflineFileHandle()
{
STRU strFilePath;
UTILITY::ConvertPathToFullPath(L".\\app_offline.htm",
m_pConfiguration->QueryApplicationPhysicalPath()->QueryStr(),
&strFilePath);
APP_OFFLINE_HTM *pOldAppOfflineHtm = NULL;
APP_OFFLINE_HTM *pNewAppOfflineHtm = NULL;
ReferenceApplicationInfo();
if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(strFilePath.QueryStr()))
{
// Check if app offline was originally present.
// if it was, log that app_offline has been dropped.
if (m_fAppOfflineFound)
{
UTILITY::LogEvent(g_hEventLog,
EVENTLOG_INFORMATION_TYPE,
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE_REMOVED,
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE_REMOVED_MSG);
}
m_fAppOfflineFound = FALSE;
}
else
{
pNewAppOfflineHtm = new APP_OFFLINE_HTM(strFilePath.QueryStr());
if (pNewAppOfflineHtm != NULL)
{
if (pNewAppOfflineHtm->Load())
{
//
// loaded the new app_offline.htm
//
pOldAppOfflineHtm = (APP_OFFLINE_HTM *)InterlockedExchangePointer((VOID**)&m_pAppOfflineHtm, pNewAppOfflineHtm);
if (pOldAppOfflineHtm != NULL)
{
pOldAppOfflineHtm->DereferenceAppOfflineHtm();
pOldAppOfflineHtm = NULL;
}
}
else
{
// ignored the new app_offline file because the file does not exist.
pNewAppOfflineHtm->DereferenceAppOfflineHtm();
pNewAppOfflineHtm = NULL;
}
}
m_fAppOfflineFound = TRUE;
// recycle the application
if (m_pApplication != NULL)
{
STACK_STRU(strEventMsg, 256);
if (SUCCEEDED(strEventMsg.SafeSnwprintf(
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE_MSG,
m_pConfiguration->QueryApplicationPhysicalPath()->QueryStr())))
{
UTILITY::LogEvent(g_hEventLog,
EVENTLOG_INFORMATION_TYPE,
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE,
strEventMsg.QueryStr());
}
RecycleApplication();
}
}
DereferenceApplicationInfo();
}
HRESULT
APPLICATION_INFO::EnsureApplicationCreated(
@ -179,57 +70,68 @@ APPLICATION_INFO::EnsureApplicationCreated(
STRU struHostFxrDllLocation;
STACK_STRU(struFileName, 300); // >MAX_PATH
if (m_pApplication != NULL)
if (m_pApplication != nullptr && m_pApplication->QueryStatus() != RECYCLED)
{
return S_OK;
}
// one optimization for failure scenario is to reduce the lock scope
SRWExclusiveLock lock(m_srwLock);
if (m_pApplication != nullptr)
{
if (m_pApplication != NULL)
if (m_pApplication->QueryStatus() == RECYCLED)
{
// another thread created the applicaiton
FINISHED(S_OK);
LOG_INFO("Application went offline");
// Application that went offline
// are supposed to recycle themselves
m_pApplication->DereferenceApplication();
m_pApplication = nullptr;
}
else if (m_fDoneAppCreation)
else
{
// previous CreateApplication failed
FINISHED(E_APPLICATION_ACTIVATION_EXEC_FAILURE);
// another thread created the application
FINISHED(S_OK);
}
}
else if (m_fAppCreationAttempted)
{
// previous CreateApplication failed
FINISHED(E_APPLICATION_ACTIVATION_EXEC_FAILURE);
}
auto& httpApplication = *pHttpContext->GetApplication();
if (PollingAppOfflineApplication::ShouldBeStarted(httpApplication))
{
LOG_INFO("Detected app_ofline file, creating polling application");
m_pApplication = new PollingAppOfflineApplication(httpApplication);
}
else
{
// Move the request handler check inside of the lock
// such that only one request finds and loads it.
// FindRequestHandlerAssembly obtains a global lock, but after releasing the lock,
// there is a period where we could call
m_fAppCreationAttempted = TRUE;
FINISHED_IF_FAILED(FindRequestHandlerAssembly(struExeLocation));
if (m_pfnAspNetCoreCreateApplication == NULL)
{
FINISHED(HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION));
}
//
// in case of app offline, we don't want to create a new application now
//
if (!m_fAppOfflineFound)
{
// Move the request handler check inside of the lock
// such that only one request finds and loads it.
// FindRequestHandlerAssembly obtains a global lock, but after releasing the lock,
// there is a period where we could call
std::array<APPLICATION_PARAMETER, 1> parameters {
{"InProcessExeLocation", struExeLocation.QueryStr()}
};
LOG_INFO("Creating handler application");
FINISHED_IF_FAILED(m_pfnAspNetCoreCreateApplication(
m_pServer,
pHttpContext->GetApplication(),
parameters.data(),
static_cast<DWORD>(parameters.size()),
&pApplication));
m_fDoneAppCreation = TRUE;
FINISHED_IF_FAILED(FindRequestHandlerAssembly(struExeLocation));
if (m_pfnAspNetCoreCreateApplication == NULL)
{
FINISHED(HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION));
}
std::array<APPLICATION_PARAMETER, 1> parameters {
{"InProcessExeLocation", struExeLocation.QueryStr()}
};
FINISHED_IF_FAILED(m_pfnAspNetCoreCreateApplication(
m_pServer,
pHttpContext->GetApplication(),
parameters.data(),
static_cast<DWORD>(parameters.size()),
&pApplication));
m_pApplication = pApplication;
}
m_pApplication = pApplication;
}
Finished:
@ -426,7 +328,7 @@ APPLICATION_INFO::FindNativeAssemblyFromHostfxr(
DWORD dwBufferSize = 1024 * 10;
DWORD dwRequiredBufferSize = 0;
DBG_ASSERT(struFileName != NULL);
DBG_ASSERT(struFilename != NULL);
FINISHED_LAST_ERROR_IF_NULL(hmHostFxrDll = LoadLibraryW(hostfxrOptions->GetHostFxrLocation()));

View File

@ -57,7 +57,7 @@ APPLICATION_MANAGER::GetOrCreateApplicationInfo(
{
pApplicationInfo = new APPLICATION_INFO();
FINISHED_IF_FAILED(pApplicationInfo->Initialize(pServer, pHttpContext->GetApplication(), m_pFileWatcher));
FINISHED_IF_FAILED(pApplicationInfo->Initialize(pServer, pHttpContext->GetApplication()));
SRWExclusiveLock lock(m_srwLock);
@ -102,10 +102,7 @@ APPLICATION_MANAGER::GetOrCreateApplicationInfo(
FINISHED_IF_FAILED(m_pApplicationInfoHash->InsertRecord(pApplicationInfo));
*ppApplicationInfo = pApplicationInfo;
pApplicationInfo->StartMonitoringAppOffline();
pApplicationInfo = NULL;
}
@ -323,12 +320,6 @@ APPLICATION_MANAGER::ShutDown()
g_fInShutdown = TRUE;
if (m_pApplicationInfoHash != NULL)
{
if (m_pFileWatcher != NULL)
{
delete m_pFileWatcher;
m_pFileWatcher = NULL;
}
DBG_ASSERT(m_pApplicationInfoHash);
// During shutdown we lock until we delete the application

View File

@ -11,7 +11,7 @@
#include "resources.h"
#include "exceptions.h"
DECLARE_DEBUG_PRINT_OBJECT("aspnetcore.dll");
DECLARE_DEBUG_PRINT_OBJECT("aspnetcorev2.dll");
HTTP_MODULE_ID g_pModuleId = NULL;
IHttpServer * g_pHttpServer = NULL;
@ -120,7 +120,7 @@ HRESULT
// check whether the feature is disabled due to security reason
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\IIS Extensions\\IIS AspNetCore Module\\Parameters",
L"SOFTWARE\\Microsoft\\IIS Extensions\\IIS AspNetCore Module V2\\Parameters",
0,
KEY_READ,
&hKey) == NO_ERROR)

View File

@ -48,7 +48,7 @@ Return value:
}
ASPNET_CORE_PROXY_MODULE::ASPNET_CORE_PROXY_MODULE(
) : m_pApplicationInfo(NULL), m_pHandler(NULL)
) : m_pApplicationInfo(nullptr), m_pApplication(nullptr), m_pHandler(nullptr)
{
}
@ -59,12 +59,6 @@ ASPNET_CORE_PROXY_MODULE::~ASPNET_CORE_PROXY_MODULE()
m_pApplicationInfo->DereferenceApplicationInfo();
m_pApplicationInfo = NULL;
}
if (m_pHandler != NULL)
{
m_pHandler->DereferenceRequestHandler();
m_pHandler = NULL;
}
}
__override
@ -77,93 +71,48 @@ ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler(
HRESULT hr = S_OK;
APPLICATION_MANAGER *pApplicationManager = NULL;
REQUEST_NOTIFICATION_STATUS retVal = RQ_NOTIFICATION_CONTINUE;
IAPPLICATION* pApplication = NULL;
STRU struExeLocation;
try
{
if (g_fInShutdown)
{
hr = HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS);
goto Finished;
FINISHED(HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS));
}
pApplicationManager = APPLICATION_MANAGER::GetInstance();
hr = pApplicationManager->GetOrCreateApplicationInfo(
FINISHED_IF_FAILED(pApplicationManager->GetOrCreateApplicationInfo(
g_pHttpServer,
pHttpContext,
&m_pApplicationInfo);
if (FAILED(hr))
{
goto Finished;
}
&m_pApplicationInfo));
if (!m_pApplicationInfo->IsValid())
{
// Application cannot be started due to wrong hosting mode
// the error should already been logged to window event log for the first request
hr = E_APPLICATION_ACTIVATION_EXEC_FAILURE;
goto Finished;
}
// app_offline check to avoid loading aspnetcorerh.dll unnecessarily
if (m_pApplicationInfo->AppOfflineFound())
{
// servicing app_offline
HTTP_DATA_CHUNK DataChunk;
IHttpResponse *pResponse = NULL;
APP_OFFLINE_HTM *pAppOfflineHtm = NULL;
pResponse = pHttpContext->GetResponse();
pAppOfflineHtm = m_pApplicationInfo->QueryAppOfflineHtm();
DBG_ASSERT(pAppOfflineHtm);
DBG_ASSERT(pResponse);
// Ignore failure hresults as nothing we can do
// Set fTrySkipCustomErrors to true as we want client see the offline content
pResponse->SetStatus(503, "Service Unavailable", 0, hr, NULL, TRUE);
pResponse->SetHeader("Content-Type",
"text/html",
(USHORT)strlen("text/html"),
FALSE
);
DataChunk.DataChunkType = HttpDataChunkFromMemory;
DataChunk.FromMemory.pBuffer = (PVOID)pAppOfflineHtm->m_Contents.QueryStr();
DataChunk.FromMemory.BufferLength = pAppOfflineHtm->m_Contents.QueryCB();
pResponse->WriteEntityChunkByReference(&DataChunk);
retVal = RQ_NOTIFICATION_FINISH_REQUEST;
goto Finished;
FINISHED(E_APPLICATION_ACTIVATION_EXEC_FAILURE);
}
// make sure assmebly is loaded and application is created
hr = m_pApplicationInfo->EnsureApplicationCreated(pHttpContext);
if (FAILED(hr))
{
goto Finished;
}
FINISHED_IF_FAILED(m_pApplicationInfo->EnsureApplicationCreated(pHttpContext));
m_pApplicationInfo->ExtractApplication(&pApplication);
auto pApplication = m_pApplicationInfo->ExtractApplication();
// make sure application is in running state
// cannot recreate the application as we cannot reload clr for inprocess
if (pApplication != NULL &&
pApplication->QueryStatus() != APPLICATION_STATUS::RUNNING &&
DBG_ASSERT(pHttpContext);
// We allow OFFLINE application to serve pages
if (pApplication->QueryStatus() != APPLICATION_STATUS::RUNNING &&
pApplication->QueryStatus() != APPLICATION_STATUS::STARTING)
{
hr = HRESULT_FROM_WIN32(ERROR_SERVER_DISABLED);
goto Finished;
}
// Create RequestHandler and process the request
hr = pApplication->CreateHandler(pHttpContext,
&m_pHandler);
if (FAILED(hr))
{
goto Finished;
}
IREQUEST_HANDLER* pHandler;
// Create RequestHandler and process the request
FINISHED_IF_FAILED(pApplication->CreateHandler(pHttpContext, &pHandler));
m_pHandler.reset(pHandler);
retVal = m_pHandler->OnExecuteRequestHandler();
}
@ -186,10 +135,6 @@ Finished:
}
}
if (pApplication != NULL)
{
pApplication->DereferenceApplication();
}
return retVal;
}

View File

@ -205,6 +205,7 @@
<ClInclude Include="debugutil.h" />
<ClInclude Include="disconnectcontext.h" />
<ClInclude Include="irequesthandler.h" />
<ClInclude Include="PollingAppOfflineApplication.h" />
<ClInclude Include="requesthandler.h" />
<ClInclude Include="resources.h" />
<ClInclude Include="SRWExclusiveLock.h" />
@ -221,6 +222,7 @@
<ClCompile Include="HandleWrapper.cpp" />
<ClCompile Include="hostfxr_utility.cpp" />
<ClCompile Include="hostfxroptions.cpp" />
<ClCompile Include="PollingAppOfflineApplication.cpp" />
<ClCompile Include="SRWExclusiveLock.cpp" />
<ClCompile Include="SRWSharedLock.cpp" />
<ClCompile Include="stdafx.cpp">

View File

@ -0,0 +1,133 @@
// 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.
#include "PollingAppOfflineApplication.h"
#include <experimental/filesystem>
#include "SRWExclusiveLock.h"
#include "HandleWrapper.h"
HRESULT PollingAppOfflineApplication::CreateHandler(IHttpContext* pHttpContext, IREQUEST_HANDLER** pRequestHandler)
{
try
{
*pRequestHandler = new PollingAppOfflineHandler(pHttpContext, m_strAppOfflineContent);
}
CATCH_RETURN();
return S_OK;
}
APPLICATION_STATUS PollingAppOfflineApplication::QueryStatus()
{
if (AppOfflineExists())
{
return APPLICATION_STATUS::RUNNING;
}
return APPLICATION_STATUS::RECYCLED;
}
bool
PollingAppOfflineApplication::AppOfflineExists()
{
const auto ulCurrentTime = GetTickCount64();
//
// we only care about app offline presented. If not, it means the application has started
// and is monitoring the app offline file
// we cache the file exist check result for 1 second
//
if (ulCurrentTime - m_ulLastCheckTime > c_appOfflineRefreshIntervalMS)
{
SRWExclusiveLock lock(m_statusLock);
if (ulCurrentTime - m_ulLastCheckTime > c_appOfflineRefreshIntervalMS)
{
m_fAppOfflineFound = is_regular_file(m_appOfflineLocation);
if(m_fAppOfflineFound)
{
LOG_IF_FAILED(LoadAppOfflineContent());
}
m_ulLastCheckTime = ulCurrentTime;
}
}
return m_fAppOfflineFound;
}
HRESULT PollingAppOfflineApplication::LoadAppOfflineContent()
{
LARGE_INTEGER li = {};
HandleWrapper<InvalidHandleTraits> handle = CreateFile(m_appOfflineLocation.c_str(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr);
RETURN_LAST_ERROR_IF(handle == INVALID_HANDLE_VALUE);
RETURN_LAST_ERROR_IF (!GetFileSizeEx(handle, &li));
if (li.HighPart != 0)
{
// > 4gb file size not supported
// todo: log a warning at event log
return TRUE;
}
if (li.LowPart > 0)
{
DWORD bytesRead = 0;
std::string pszBuff(li.LowPart + 1, '\0');
RETURN_LAST_ERROR_IF(!ReadFile(handle, pszBuff.data(), li.LowPart, &bytesRead, NULL));
pszBuff.resize(bytesRead);
m_strAppOfflineContent = pszBuff;
}
return S_OK;
}
bool PollingAppOfflineApplication::ShouldBeStarted(IHttpApplication& pApplication)
{
return is_regular_file(GetAppOfflineLocation(pApplication));
}
std::experimental::filesystem::path PollingAppOfflineApplication::GetAppOfflineLocation(IHttpApplication& pApplication)
{
return std::experimental::filesystem::path(pApplication.GetApplicationPhysicalPath()) / "app_offline.htm";
}
void PollingAppOfflineApplication::ShutDown()
{
}
void PollingAppOfflineApplication::Recycle()
{
}
REQUEST_NOTIFICATION_STATUS PollingAppOfflineHandler::OnExecuteRequestHandler()
{
HTTP_DATA_CHUNK DataChunk;
IHttpResponse* pResponse = m_pContext->GetResponse();
DBG_ASSERT(pResponse);
// Ignore failure hresults as nothing we can do
// Set fTrySkipCustomErrors to true as we want client see the offline content
pResponse->SetStatus(503, "Service Unavailable", 0, S_OK, nullptr, TRUE);
pResponse->SetHeader("Content-Type",
"text/html",
static_cast<USHORT>(strlen("text/html")),
FALSE
);
DataChunk.DataChunkType = HttpDataChunkFromMemory;
DataChunk.FromMemory.pBuffer = m_strAppOfflineContent.data();
DataChunk.FromMemory.BufferLength = static_cast<ULONG>(m_strAppOfflineContent.size());
pResponse->WriteEntityChunkByReference(&DataChunk);
return REQUEST_NOTIFICATION_STATUS::RQ_NOTIFICATION_FINISH_REQUEST;
}

View File

@ -0,0 +1,57 @@
// 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.
#pragma once
#include <experimental/filesystem>
#include "application.h"
#include "requesthandler.h"
class PollingAppOfflineApplication: public APPLICATION
{
public:
PollingAppOfflineApplication(IHttpApplication& pApplication)
:
m_ulLastCheckTime(0),
m_appOfflineLocation(GetAppOfflineLocation(pApplication)),
m_fAppOfflineFound(false)
{
InitializeSRWLock(&m_statusLock);
}
HRESULT CreateHandler(IHttpContext* pHttpContext, IREQUEST_HANDLER** pRequestHandler) override;
APPLICATION_STATUS QueryStatus() override;
bool AppOfflineExists();
HRESULT LoadAppOfflineContent();
static bool ShouldBeStarted(IHttpApplication& pApplication);
void ShutDown() override;
void Recycle() override;
private:
static const int c_appOfflineRefreshIntervalMS = 200;
static std::experimental::filesystem::path GetAppOfflineLocation(IHttpApplication& pApplication);
std::string m_strAppOfflineContent;
ULONGLONG m_ulLastCheckTime;
std::experimental::filesystem::path m_appOfflineLocation;
bool m_fAppOfflineFound;
SRWLOCK m_statusLock {};
};
class PollingAppOfflineHandler: public REQUEST_HANDLER
{
public:
PollingAppOfflineHandler(IHttpContext* pContext, const std::string appOfflineContent)
: m_pContext(pContext),
m_strAppOfflineContent(appOfflineContent)
{
}
REQUEST_NOTIFICATION_STATUS OnExecuteRequestHandler() override;
private:
IHttpContext* m_pContext;
std::string m_strAppOfflineContent;
};

View File

@ -4,12 +4,18 @@
#pragma once
#include "iapplication.h"
#include "exceptions.h"
#include "utility.h"
#include "ntassert.h"
class APPLICATION : public IAPPLICATION
{
public:
// Non-copyable
APPLICATION(const APPLICATION&) = delete;
const APPLICATION& operator=(const APPLICATION&) = delete;
APPLICATION_STATUS
QueryStatus() override
@ -33,8 +39,7 @@ public:
{
DBG_ASSERT(m_cRefs != 0);
LONG cRefs = 0;
if ((cRefs = InterlockedDecrement(&m_cRefs)) == 0)
if (InterlockedDecrement(&m_cRefs) == 0)
{
delete this;
}
@ -42,7 +47,7 @@ public:
protected:
volatile APPLICATION_STATUS m_status = APPLICATION_STATUS::UNKNOWN;
private:
mutable LONG m_cRefs;
};

View File

@ -171,7 +171,7 @@ Language=English
.
Messageid=1024
SymbolicName=ASPNETCORE_EVENT_RECYCLE_APPOFFLINE_REMOVED
SymbolicName=ASPNETCORE_EVENT_MONITOR_APPOFFLINE_ERROR
Language=English
%1
.

View File

@ -23,7 +23,7 @@ DebugInitialize()
HKEY hKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\IIS Extensions\\IIS AspNetCore Module\\Parameters",
L"SOFTWARE\\Microsoft\\IIS Extensions\\IIS AspNetCore Module V2\\Parameters",
0,
KEY_READ,
&hKey) == NO_ERROR)

View File

@ -225,8 +225,8 @@ HOSTFXR_UTILITY::ParseHostfxrArguments(
{
UNREFERENCED_PARAMETER(hEventLog); // TODO use event log to set errors.
DBG_ASSERT(dwArgCount != NULL);
DBG_ASSERT(pwzArgv != NULL);
DBG_ASSERT(pdwArgCount != NULL);
DBG_ASSERT(pbstrArgv != NULL);
DBG_ASSERT(pwzExePath != NULL);
HRESULT hr = S_OK;
@ -324,7 +324,6 @@ Finished:
if (pwzArgs != NULL)
{
LocalFree(pwzArgs);
DBG_ASSERT(pwzArgs == NULL);
}
return hr;
}

View File

@ -3,6 +3,7 @@
#pragma once
#include <memory>
#include "irequesthandler.h"
enum APPLICATION_STATUS
@ -11,6 +12,7 @@ enum APPLICATION_STATUS
STARTING,
RUNNING,
SHUTDOWN,
RECYCLED,
FAIL
};
@ -53,3 +55,18 @@ public:
_In_ IHttpContext *pHttpContext,
_Out_ IREQUEST_HANDLER **pRequestHandler) = 0;
};
struct IAPPLICATION_DELETER
{
void operator ()(IAPPLICATION* application) const
{
application->DereferenceApplication();
}
};
template< class APPLICATION >
std::unique_ptr<APPLICATION, IAPPLICATION_DELETER> ReferenceApplication(APPLICATION* application)
{
application->ReferenceApplication();
return std::unique_ptr<APPLICATION, IAPPLICATION_DELETER>(application);
};

View File

@ -4,6 +4,7 @@
#pragma once
#include <httpserv.h>
#include <memory>
//
// Pure abstract class
@ -46,3 +47,12 @@ public:
VOID
) = 0;
};
struct IREQUEST_HANDLER_DELETER
{
void operator ()(IREQUEST_HANDLER* application) const
{
application->DereferenceRequestHandler();
}
};

View File

@ -24,13 +24,26 @@ public:
{
DBG_ASSERT(m_cRefs != 0);
LONG cRefs = 0;
if ((cRefs = InterlockedDecrement(&m_cRefs)) == 0)
if (InterlockedDecrement(&m_cRefs) == 0)
{
delete this;
}
}
REQUEST_NOTIFICATION_STATUS OnAsyncCompletion(DWORD cbCompletion, HRESULT hrCompletionStatus) override
{
UNREFERENCED_PARAMETER(cbCompletion);
UNREFERENCED_PARAMETER(hrCompletionStatus);
// We shouldn't get here in default implementation
DBG_ASSERT(FALSE);
return RQ_NOTIFICATION_FINISH_REQUEST;
}
VOID TerminateRequest(bool fClientInitiated) override
{
UNREFERENCED_PARAMETER(fClientInitiated);
}
private:
mutable LONG m_cRefs = 1;
};

View File

@ -33,7 +33,7 @@
#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_RECYCLE_APPOFFLINE_REMOVED_MSG L"App_offline.htm has been removed from the application. Application will be recycled."
#define ASPNETCORE_EVENT_MONITOR_APPOFFLINE_ERROR_MSG L"Monitoring app_offline.htm failed for application '%s', ErrorCode '0x%x'. "
#define ASPNETCORE_EVENT_RECYCLE_CONFIGURATION_MSG L"Application '%s' was recycled due to configuration change"
#define ASPNETCORE_EVENT_RECYCLE_FAILURE_CONFIGURATION_MSG L"Failed to recycle application due to a configuration change at '%s'. Recycling worker process."
#define ASPNETCORE_EVENT_MODULE_DISABLED_MSG L"AspNetCore Module is disabled"

View File

@ -11,6 +11,10 @@
#undef ASSERT
#endif
#ifdef DEBUG
#define DBG 1
#endif
#if defined( DBG ) && DBG
#define SX_ASSERT( _x ) ( (VOID)( ( ( _x ) ) ? TRUE : ( __annotation( L"Debug", L"AssertFail", L#_x ), DbgRaiseAssertionFailure(), FALSE ) ) )
#define SX_ASSERTMSG( _m, _x ) ( (VOID)( ( ( _x ) ) ? TRUE : ( __annotation( L"Debug", L"AssertFail", L##_m ), DbgRaiseAssertionFailure(), FALSE ) ) )

View File

@ -7,11 +7,12 @@
hostfxr_main_fn InProcessApplicationBase::s_fMainCallback = NULL;
InProcessApplicationBase::InProcessApplicationBase(
IHttpServer *pHttpServer)
:
m_srwLock(),
m_fRecycleCalled(FALSE),
m_pHttpServer(pHttpServer)
IHttpServer& pHttpServer,
IHttpApplication& pHttpApplication)
: AppOfflineTrackingApplication(pHttpApplication),
m_fRecycleCalled(FALSE),
m_srwLock(),
m_pHttpServer(pHttpServer)
{
InitializeSRWLock(&m_srwLock);
}
@ -39,23 +40,19 @@ InProcessApplicationBase::Recycle(
m_fRecycleCalled = true;
}
if (!m_pHttpServer->IsCommandLineLaunch())
if (!m_pHttpServer.IsCommandLineLaunch())
{
// IIS scenario.
// We don't actually handle any shutdown logic here.
// Instead, we notify IIS that the process needs to be recycled, which will call
// ApplicationManager->Shutdown(). This will call shutdown on the application.
m_pHttpServer->RecycleProcess(L"AspNetCore InProcess Recycle Process on Demand");
m_pHttpServer.RecycleProcess(L"AspNetCore InProcess Recycle Process on Demand");
}
else
{
// IISExpress scenario
// Try to graceful shutdown the managed application
// and call exit to terminate current process
ShutDown();
// If we set a static callback, we don't want to kill the current process as
// that will kill the test process and means we are running in hostable webcore mode.
if (m_pHttpServer->IsCommandLineLaunch()
if (m_pHttpServer.IsCommandLineLaunch()
&& s_fMainCallback == NULL)
{
exit(0);

View File

@ -3,17 +3,18 @@
#pragma once
#include "precomp.hxx"
#include "application.h"
#include "requesthandler_config.h"
#include "AppOfflineTrackingApplication.h"
typedef INT(*hostfxr_main_fn) (CONST DWORD argc, CONST PCWSTR argv[]); // TODO these may need to be BSTRs
class InProcessApplicationBase : public APPLICATION
class InProcessApplicationBase : public AppOfflineTrackingApplication
{
public:
InProcessApplicationBase(IHttpServer* pHttpServer);
InProcessApplicationBase(
IHttpServer& pHttpServer,
IHttpApplication& pHttpApplication);
~InProcessApplicationBase() = default;
@ -22,7 +23,7 @@ public:
protected:
BOOL m_fRecycleCalled;
SRWLOCK m_srwLock;
IHttpServer* const m_pHttpServer;
IHttpServer& m_pHttpServer;
// Allows to override call to hostfxr_main with custome callback
// used in testing
static hostfxr_main_fn s_fMainCallback;

View File

@ -83,7 +83,7 @@
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;REQUESTHANDLER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -104,17 +104,20 @@
<CompileAs>CompileAsCpp</CompileAs>
<IntrinsicFunctions>true</IntrinsicFunctions>
<LanguageStandard>stdcpp17</LanguageStandard>
<ForcedIncludeFiles>precomp.hxx</ForcedIncludeFiles>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;advapi32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ahadmin.lib;rpcrt4.lib;winhttp.lib;pdh.lib;ws2_32.lib;wbemuuid.lib;iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>Source.def</ModuleDefinitionFile>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;REQUESTHANDLER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -135,18 +138,21 @@
<CompileAs>CompileAsCpp</CompileAs>
<IntrinsicFunctions>true</IntrinsicFunctions>
<LanguageStandard>stdcpp17</LanguageStandard>
<ForcedIncludeFiles>precomp.hxx</ForcedIncludeFiles>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;advapi32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ahadmin.lib;rpcrt4.lib;winhttp.lib;pdh.lib;ws2_32.lib;wbemuuid.lib;iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>Source.def</ModuleDefinitionFile>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeader>Use</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
@ -166,6 +172,8 @@
<CompileAs>CompileAsCpp</CompileAs>
<IntrinsicFunctions>true</IntrinsicFunctions>
<LanguageStandard>stdcpp17</LanguageStandard>
<ForcedIncludeFiles>precomp.hxx</ForcedIncludeFiles>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -174,12 +182,13 @@
<OptimizeReferences>true</OptimizeReferences>
<ModuleDefinitionFile>Source.def</ModuleDefinitionFile>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;ahadmin.lib;winhttp.lib;odbc32.lib;ws2_32.lib;odbccp32.lib;wbemuuid.lib;iphlpapi.lib;pdh.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeader>Use</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
@ -199,6 +208,8 @@
<CompileAs>CompileAsCpp</CompileAs>
<IntrinsicFunctions>true</IntrinsicFunctions>
<LanguageStandard>stdcpp17</LanguageStandard>
<ForcedIncludeFiles>precomp.hxx</ForcedIncludeFiles>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -207,6 +218,7 @@
<OptimizeReferences>true</OptimizeReferences>
<ModuleDefinitionFile>Source.def</ModuleDefinitionFile>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;ahadmin.lib;rpcrt4.lib;winhttp.lib;pdh.lib;ws2_32.lib;wbemuuid.lib;iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
@ -224,6 +236,12 @@
<ClCompile Include="InProcessApplicationBase.cpp" />
<ClCompile Include="inprocesshandler.cpp" />
<ClCompile Include="managedexports.cxx" />
<ClCompile Include="precomp.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="StartupExceptionApplication.cpp" />
<ClCompile Include="StartupExceptionHandler.cpp" />
</ItemGroup>

View File

@ -5,11 +5,10 @@
VOID StartupExceptionApplication::ShutDown()
{
exit(0);
}
HRESULT StartupExceptionApplication::CreateHandler(IHttpContext *pContext, IREQUEST_HANDLER ** pRequestHandler)
HRESULT StartupExceptionApplication::CreateHandler(IHttpContext *pHttpContext, IREQUEST_HANDLER ** pRequestHandler)
{
*pRequestHandler = new StartupExceptionHandler(pContext, m_disableLogs, this);
*pRequestHandler = new StartupExceptionHandler(pHttpContext, m_disableLogs, this);
return S_OK;
}

View File

@ -3,68 +3,58 @@
#pragma once
#include "precomp.hxx"
#include "InProcessApplicationBase.h"
#include "StartupExceptionHandler.h"
#include "SRWExclusiveLock.h"
class StartupExceptionApplication : public InProcessApplicationBase
{
public:
StartupExceptionApplication(IHttpServer* pServer, BOOL disableLogs)
StartupExceptionApplication(
IHttpServer& pServer,
IHttpApplication& pApplication,
BOOL disableLogs)
: m_disableLogs(disableLogs),
m_pHttpServer(pServer),
InProcessApplicationBase(pServer)
InProcessApplicationBase(pServer, pApplication)
{
InitializeSRWLock(&m_srwLock);
m_status = APPLICATION_STATUS::RUNNING;
html500Page = std::string("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"> \
<html xmlns=\"http://www.w3.org/1999/xhtml\"> \
<head> \
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" /> \
<title> IIS 500.30 Error </title><style type=\"text/css\"></style></head> \
<body> <div id = \"content\"> \
<div class = \"content-container\"><h3> HTTP Error 500.30 - ANCM In-Process Start Failure </h3></div> \
<div class = \"content-container\"> \
<fieldset> <h4> Common causes of this issue: </h4> \
<ul><li> The application failed to start </li> \
<li> The application started but then stopped </li> \
<li> The application started but threw an exception during startup </li></ul></fieldset> \
</div> \
<div class = \"content-container\"> \
<fieldset><h4> Troubleshooting steps: </h4> \
<ul><li> Check the system event log for error messages </li> \
<li> Enable logging the application process' stdout messages </li> \
<li> Attach a debugger to the application process and inspect </li></ul></fieldset> \
<fieldset><h4> For more information visit: \
<a href=\"https://go.microsoft.com/fwlink/?LinkID=808681\"> <cite> https://go.microsoft.com/fwlink/?LinkID=808681 </cite></a></h4> \
</fieldset> \
</div> \
</div></body></html>");
}
~StartupExceptionApplication() = default;
virtual VOID ShutDown() override;
virtual HRESULT CreateHandler(IHttpContext * pHttpContext, IREQUEST_HANDLER ** pRequestHandler) override;
VOID ShutDown() override;
HRESULT CreateHandler(IHttpContext * pHttpContext, IREQUEST_HANDLER ** pRequestHandler) override;
std::string&
GetStaticHtml500Content()
{
{
SRWExclusiveLock lock(m_srwLock);
if (html500Page.empty())
{
html500Page = std::string("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"> \
<html xmlns=\"http://www.w3.org/1999/xhtml\"> \
<head> \
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" /> \
<title> IIS 500.30 Error </title><style type=\"text/css\"></style></head> \
<body> <div id = \"content\"> \
<div class = \"content-container\"><h3> HTTP Error 500.30 - ANCM In-Process Start Failure </h3></div> \
<div class = \"content-container\"> \
<fieldset> <h4> Common causes of this issue: </h4> \
<ul><li> The application failed to start </li> \
<li> The application started but then stopped </li> \
<li> The application started but threw an exception during startup </li></ul></fieldset> \
</div> \
<div class = \"content-container\"> \
<fieldset><h4> Troubleshooting steps: </h4> \
<ul><li> Check the system event log for error messages </li> \
<li> Enable logging the application process' stdout messages </li> \
<li> Attach a debugger to the application process and inspect </li></ul></fieldset> \
<fieldset><h4> For more information visit: \
<a href=\"https://go.microsoft.com/fwlink/?LinkID=808681\"> <cite> https://go.microsoft.com/fwlink/?LinkID=808681 </cite></a></h4> \
</fieldset> \
</div> \
</div></body></html>");
}
}
return html500Page;
}
private:
std::string html500Page;
SRWLOCK m_srwLock;
BOOL m_disableLogs;
IHttpServer* m_pHttpServer;
};

View File

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "StartupExceptionApplication.h"
#include "StartupExceptionHandler.h"
REQUEST_NOTIFICATION_STATUS StartupExceptionHandler::OnExecuteRequestHandler()
@ -28,15 +29,5 @@ REQUEST_NOTIFICATION_STATUS StartupExceptionHandler::OnExecuteRequestHandler()
}
return REQUEST_NOTIFICATION_STATUS::RQ_NOTIFICATION_FINISH_REQUEST;
}
REQUEST_NOTIFICATION_STATUS StartupExceptionHandler::OnAsyncCompletion(DWORD , HRESULT )
{
return REQUEST_NOTIFICATION_STATUS::RQ_NOTIFICATION_FINISH_REQUEST;
}
VOID StartupExceptionHandler::TerminateRequest(bool )
{
return VOID();
}

View File

@ -3,21 +3,13 @@
#pragma once
#include "precomp.hxx"
#include "requesthandler.h"
#include "StartupExceptionApplication.h"
class StartupExceptionApplication;
class StartupExceptionHandler : public REQUEST_HANDLER
{
public:
virtual REQUEST_NOTIFICATION_STATUS OnExecuteRequestHandler() override;
virtual REQUEST_NOTIFICATION_STATUS OnAsyncCompletion(DWORD cbCompletion, HRESULT hrCompletionStatus) override;
virtual VOID TerminateRequest(bool fClientInitiated) override;
StartupExceptionHandler(IHttpContext* pContext, BOOL disableLogs, StartupExceptionApplication* pApplication)
:
m_pContext(pContext),
@ -26,9 +18,7 @@ public:
{
}
~StartupExceptionHandler()
{
}
REQUEST_NOTIFICATION_STATUS OnExecuteRequestHandler() override;
private:
IHttpContext * m_pContext;

View File

@ -3,8 +3,6 @@
// dllmain.cpp : Defines the entry point for the DLL application.
#include "precomp.hxx"
#include <IPHlpApi.h>
#include <VersionHelpers.h>
#include "inprocessapplication.h"
@ -39,7 +37,7 @@ InitializeGlobalConfiguration(
g_pHttpServer = pServer;
RETURN_IF_FAILED(ALLOC_CACHE_HANDLER::StaticInitialize());
RETURN_IF_FAILED(IN_PROCESS_HANDLER::StaticInitialize());
if (pServer->IsCommandLineLaunch())
{
g_hEventLog = RegisterEventSource(NULL, ASPNETCORE_IISEXPRESS_EVENT_PROVIDER);
@ -89,27 +87,30 @@ CreateApplication(
_Out_ IAPPLICATION **ppApplication
)
{
REQUESTHANDLER_CONFIG *pConfig = NULL;
try
{
// Initialze some global variables here
RETURN_IF_FAILED(InitializeGlobalConfiguration(pServer));
REQUESTHANDLER_CONFIG *pConfig = nullptr;
RETURN_IF_FAILED(REQUESTHANDLER_CONFIG::CreateRequestHandlerConfig(pServer, pHttpApplication, &pConfig));
std::unique_ptr<REQUESTHANDLER_CONFIG> pRequestHandlerConfig(pConfig);
BOOL disableStartupPage = pConfig->QueryDisableStartUpErrorPage();
auto config = std::unique_ptr<REQUESTHANDLER_CONFIG>(pConfig);
const bool disableStartupPage = pConfig->QueryDisableStartUpErrorPage();
auto pApplication = std::make_unique<IN_PROCESS_APPLICATION>(pServer, std::move(config), pParameters, nParameters);
auto pApplication = std::make_unique<IN_PROCESS_APPLICATION>(*pServer, *pHttpApplication, std::move(pRequestHandlerConfig), pParameters, nParameters);
if (FAILED_LOG(pApplication->LoadManagedApplication()))
{
// Set the currently running application to a fake application that returns startup exceptions.
*ppApplication = new StartupExceptionApplication(pServer, disableStartupPage);
auto pErrorApplication = std::make_unique<StartupExceptionApplication>(*pServer, *pHttpApplication, disableStartupPage);
RETURN_IF_FAILED(pErrorApplication->StartMonitoringAppOffline());
*ppApplication = pErrorApplication.release();
}
else
{
RETURN_IF_FAILED(pApplication->StartMonitoringAppOffline());
*ppApplication = pApplication.release();
}
}

View File

@ -6,7 +6,6 @@
#include "hostfxroptions.h"
#include "requesthandler_config.h"
#include "environmentvariablehelpers.h"
#include "aspnetcore_event.h"
#include "utility.h"
#include "EventLog.h"
#include "SRWExclusiveLock.h"
@ -18,23 +17,20 @@ const LPCSTR IN_PROCESS_APPLICATION::s_exeLocationParameterName = "InProcessExeL
IN_PROCESS_APPLICATION* IN_PROCESS_APPLICATION::s_Application = NULL;
IN_PROCESS_APPLICATION::IN_PROCESS_APPLICATION(
IHttpServer *pHttpServer,
IHttpServer& pHttpServer,
IHttpApplication& pApplication,
std::unique_ptr<REQUESTHANDLER_CONFIG> pConfig,
APPLICATION_PARAMETER *pParameters,
DWORD nParameters) :
InProcessApplicationBase(pHttpServer),
InProcessApplicationBase(pHttpServer, pApplication),
m_pHttpServer(pHttpServer),
m_ProcessExitCode(0),
m_fBlockCallbacksIntoManaged(FALSE),
m_fShutdownCalledFromNative(FALSE),
m_fShutdownCalledFromManaged(FALSE),
m_fInitialized(FALSE),
m_pConfig(std::move(pConfig))
{
// is it guaranteed that we have already checked app offline at this point?
// If so, I don't think there is much to do here.
DBG_ASSERT(pHttpServer != NULL);
DBG_ASSERT(pConfig != NULL);
DBG_ASSERT(m_pConfig);
for (DWORD i = 0; i < nParameters; i++)
{
@ -207,98 +203,11 @@ IN_PROCESS_APPLICATION::ShutDownInternal()
s_Application = NULL;
}
REQUEST_NOTIFICATION_STATUS
IN_PROCESS_APPLICATION::OnAsyncCompletion(
DWORD cbCompletion,
HRESULT hrCompletionStatus,
IN_PROCESS_HANDLER* pInProcessHandler
)
{
REQUEST_NOTIFICATION_STATUS dwRequestNotificationStatus = RQ_NOTIFICATION_CONTINUE;
ReferenceApplication();
if (pInProcessHandler->QueryIsManagedRequestComplete())
{
// means PostCompletion has been called and this is the associated callback.
dwRequestNotificationStatus = pInProcessHandler->QueryAsyncCompletionStatus();
}
else if (m_fBlockCallbacksIntoManaged)
{
// this can potentially happen in ungraceful shutdown.
// Or something really wrong happening with async completions
// At this point, managed is in a shutting down state and we cannot send a request to it.
pInProcessHandler->QueryHttpContext()->GetResponse()->SetStatus(503,
"Server has been shutdown",
0,
(ULONG)HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS));
dwRequestNotificationStatus = RQ_NOTIFICATION_FINISH_REQUEST;
}
else
{
// Call the managed handler for async completion.
dwRequestNotificationStatus = m_AsyncCompletionHandler(pInProcessHandler->QueryManagedHttpContext(), hrCompletionStatus, cbCompletion);
}
DereferenceApplication();
return dwRequestNotificationStatus;
}
REQUEST_NOTIFICATION_STATUS
IN_PROCESS_APPLICATION::OnExecuteRequest(
_In_ IHttpContext* pHttpContext,
_In_ IN_PROCESS_HANDLER* pInProcessHandler
)
{
REQUEST_NOTIFICATION_STATUS dwRequestNotificationStatus = RQ_NOTIFICATION_CONTINUE;
PFN_REQUEST_HANDLER pRequestHandler = NULL;
ReferenceApplication();
pRequestHandler = m_RequestHandler;
if (pRequestHandler == NULL)
{
//
// return error as the application did not register callback
//
if (ANCMEvents::ANCM_EXECUTE_REQUEST_FAIL::IsEnabled(pHttpContext->GetTraceContext()))
{
ANCMEvents::ANCM_EXECUTE_REQUEST_FAIL::RaiseEvent(pHttpContext->GetTraceContext(),
NULL,
(ULONG)E_APPLICATION_ACTIVATION_EXEC_FAILURE);
}
pHttpContext->GetResponse()->SetStatus(500,
"Internal Server Error",
0,
(ULONG)E_APPLICATION_ACTIVATION_EXEC_FAILURE);
dwRequestNotificationStatus = RQ_NOTIFICATION_FINISH_REQUEST;
}
else if (m_status != APPLICATION_STATUS::RUNNING || m_fBlockCallbacksIntoManaged)
{
pHttpContext->GetResponse()->SetStatus(503,
"Server is currently shutting down.",
0,
(ULONG)HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS));
dwRequestNotificationStatus = RQ_NOTIFICATION_FINISH_REQUEST;
}
else
{
dwRequestNotificationStatus = pRequestHandler(pInProcessHandler, m_RequestHandlerContext);
}
DereferenceApplication();
return dwRequestNotificationStatus;
}
VOID
IN_PROCESS_APPLICATION::SetCallbackHandles(
_In_ PFN_REQUEST_HANDLER request_handler,
_In_ PFN_SHUTDOWN_HANDLER shutdown_handler,
_In_ PFN_MANAGED_CONTEXT_HANDLER async_completion_handler,
_In_ PFN_ASYNC_COMPLETION_HANDLER async_completion_handler,
_In_ VOID* pvRequstHandlerContext,
_In_ VOID* pvShutdownHandlerContext
)
@ -664,14 +573,6 @@ IN_PROCESS_APPLICATION::RunDotnetApplication(DWORD argc, CONST PCWSTR* argv, hos
return hr;
}
// static
REQUESTHANDLER_CONFIG*
IN_PROCESS_APPLICATION::QueryConfig() const
{
return m_pConfig.get();
}
HRESULT
IN_PROCESS_APPLICATION::CreateHandler(
_In_ IHttpContext *pHttpContext,
@ -680,7 +581,7 @@ IN_PROCESS_APPLICATION::CreateHandler(
HRESULT hr = S_OK;
IREQUEST_HANDLER* pHandler = NULL;
pHandler = new IN_PROCESS_HANDLER(pHttpContext, this);
pHandler = new IN_PROCESS_HANDLER(::ReferenceApplication(this), pHttpContext, m_RequestHandler, m_RequestHandlerContext, m_AsyncCompletionHandler);
if (pHandler == NULL)
{

View File

@ -3,21 +3,21 @@
#pragma once
#include "precomp.hxx"
#include "InProcessApplicationBase.h"
#include "inprocesshandler.h"
#include "requesthandler_config.h"
#include "IOutputManager.h"
class IN_PROCESS_HANDLER;
typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_REQUEST_HANDLER) (IN_PROCESS_HANDLER* pInProcessHandler, void* pvRequestHandlerContext);
typedef BOOL(WINAPI * PFN_SHUTDOWN_HANDLER) (void* pvShutdownHandlerContext);
typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_MANAGED_CONTEXT_HANDLER)(void *pvManagedHttpContext, HRESULT hrCompletionStatus, DWORD cbCompletion);
typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_ASYNC_COMPLETION_HANDLER)(void *pvManagedHttpContext, HRESULT hrCompletionStatus, DWORD cbCompletion);
class IN_PROCESS_APPLICATION : public InProcessApplicationBase
{
public:
IN_PROCESS_APPLICATION(
IHttpServer* pHttpServer,
IHttpServer& pHttpServer,
IHttpApplication& pApplication,
std::unique_ptr<REQUESTHANDLER_CONFIG> pConfig,
APPLICATION_PARAMETER *pParameters,
DWORD nParameters);
@ -32,7 +32,7 @@ public:
SetCallbackHandles(
_In_ PFN_REQUEST_HANDLER request_callback,
_In_ PFN_SHUTDOWN_HANDLER shutdown_callback,
_In_ PFN_MANAGED_CONTEXT_HANDLER managed_context_callback,
_In_ PFN_ASYNC_COMPLETION_HANDLER managed_context_callback,
_In_ VOID* pvRequstHandlerContext,
_In_ VOID* pvShutdownHandlerContext
);
@ -60,20 +60,6 @@ public:
HRESULT hr
);
REQUEST_NOTIFICATION_STATUS
OnAsyncCompletion(
DWORD cbCompletion,
HRESULT hrCompletionStatus,
IN_PROCESS_HANDLER* pInProcessHandler
);
REQUEST_NOTIFICATION_STATUS
OnExecuteRequest
(
IHttpContext* pHttpContext,
IN_PROCESS_HANDLER* pInProcessHandler
);
VOID
StopCallsIntoManaged(
VOID
@ -105,17 +91,27 @@ public:
return s_Application;
}
REQUESTHANDLER_CONFIG*
QueryConfig() const;
PCWSTR
QueryExeLocation()
{
return m_struExeLocation.QueryStr();
}
REQUESTHANDLER_CONFIG*
QueryConfig()
{
return m_pConfig.get();
}
bool
QueryBlockCallbacksIntoManaged() const
{
return m_fBlockCallbacksIntoManaged;
}
private:
IHttpServer* const m_pHttpServer;
IHttpServer & m_pHttpServer;
// Thread executing the .NET Core process
HANDLE m_hThread;
@ -128,7 +124,7 @@ private:
PFN_SHUTDOWN_HANDLER m_ShutdownHandler;
VOID* m_ShutdownHandlerContext;
PFN_MANAGED_CONTEXT_HANDLER m_AsyncCompletionHandler;
PFN_ASYNC_COMPLETION_HANDLER m_AsyncCompletionHandler;
// The event that gets triggered when managed initialization is complete
HANDLE m_pInitalizeEvent;
@ -144,12 +140,11 @@ private:
volatile BOOL m_fShutdownCalledFromManaged;
BOOL m_fRecycleCalled;
BOOL m_fInitialized;
std::unique_ptr<REQUESTHANDLER_CONFIG> m_pConfig;
static IN_PROCESS_APPLICATION* s_Application;
IOutputManager* m_pLoggerProvider;
std::unique_ptr<REQUESTHANDLER_CONFIG> m_pConfig;
static const LPCSTR s_exeLocationParameterName;

View File

@ -9,17 +9,20 @@
ALLOC_CACHE_HANDLER * IN_PROCESS_HANDLER::sm_pAlloc = NULL;
IN_PROCESS_HANDLER::IN_PROCESS_HANDLER(
_In_ std::unique_ptr<IN_PROCESS_APPLICATION, IAPPLICATION_DELETER> pApplication,
_In_ IHttpContext *pW3Context,
_In_ IN_PROCESS_APPLICATION *pApplication
): m_pW3Context(pW3Context),
m_pApplication(pApplication)
_In_ PFN_REQUEST_HANDLER pRequestHandler,
_In_ void * pRequestHandlerContext,
_In_ PFN_ASYNC_COMPLETION_HANDLER pAsyncCompletion
): m_pManagedHttpContext(nullptr),
m_requestNotificationStatus(RQ_NOTIFICATION_PENDING),
m_fManagedRequestComplete(FALSE),
m_pW3Context(pW3Context),
m_pApplication(std::move(pApplication)),
m_pRequestHandler(pRequestHandler),
m_pRequestHandlerContext(pRequestHandlerContext),
m_pAsyncCompletionHandler(pAsyncCompletion)
{
m_fManagedRequestComplete = FALSE;
}
IN_PROCESS_HANDLER::~IN_PROCESS_HANDLER()
{
//todo
}
__override
@ -36,8 +39,32 @@ IN_PROCESS_HANDLER::OnExecuteRequestHandler()
L"InProcess Application");
}
//SetHttpSysDisconnectCallback();
return m_pApplication->OnExecuteRequest(m_pW3Context, this);
if (m_pRequestHandler == NULL)
{
//
// return error as the application did not register callback
//
if (ANCMEvents::ANCM_EXECUTE_REQUEST_FAIL::IsEnabled(m_pW3Context->GetTraceContext()))
{
ANCMEvents::ANCM_EXECUTE_REQUEST_FAIL::RaiseEvent(m_pW3Context->GetTraceContext(),
NULL,
(ULONG)E_APPLICATION_ACTIVATION_EXEC_FAILURE);
}
m_pW3Context->GetResponse()->SetStatus(500,
"Internal Server Error",
0,
(ULONG)E_APPLICATION_ACTIVATION_EXEC_FAILURE);
return RQ_NOTIFICATION_FINISH_REQUEST;
}
else if (m_pApplication->QueryStatus() != APPLICATION_STATUS::RUNNING || m_pApplication->
QueryBlockCallbacksIntoManaged())
{
return ServerShutdownMessage();
}
return m_pRequestHandler(this, m_pRequestHandlerContext);
}
__override
@ -47,9 +74,28 @@ IN_PROCESS_HANDLER::OnAsyncCompletion(
HRESULT hrCompletionStatus
)
{
// OnAsyncCompletion must call into the application if there was a error. We will redo calls
// to Read/Write if we called cancelIo on the IHttpContext.
return m_pApplication->OnAsyncCompletion(cbCompletion, hrCompletionStatus, this);
if (m_fManagedRequestComplete)
{
// means PostCompletion has been called and this is the associated callback.
return m_requestNotificationStatus;
}
if (m_pApplication->QueryBlockCallbacksIntoManaged())
{
// this can potentially happen in ungraceful shutdown.
// Or something really wrong happening with async completions
// At this point, managed is in a shutting down state and we cannot send a request to it.
return ServerShutdownMessage();
}
// Call the managed handler for async completion.
return m_pAsyncCompletionHandler(m_pManagedHttpContext, hrCompletionStatus, cbCompletion);
}
REQUEST_NOTIFICATION_STATUS IN_PROCESS_HANDLER::ServerShutdownMessage() const
{
m_pW3Context->GetResponse()->SetStatus(503, "Server has been shutdown", 0,
(ULONG)HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS));
return RQ_NOTIFICATION_FINISH_REQUEST;
}
VOID
@ -58,31 +104,6 @@ IN_PROCESS_HANDLER::TerminateRequest(
)
{
UNREFERENCED_PARAMETER(fClientInitiated);
//todo
}
PVOID
IN_PROCESS_HANDLER::QueryManagedHttpContext(
VOID
)
{
return m_pManagedHttpContext;
}
BOOL
IN_PROCESS_HANDLER::QueryIsManagedRequestComplete(
VOID
)
{
return m_fManagedRequestComplete;
}
IHttpContext*
IN_PROCESS_HANDLER::QueryHttpContext(
VOID
)
{
return m_pW3Context;
}
VOID
@ -93,14 +114,6 @@ IN_PROCESS_HANDLER::IndicateManagedRequestComplete(
m_fManagedRequestComplete = TRUE;
}
REQUEST_NOTIFICATION_STATUS
IN_PROCESS_HANDLER::QueryAsyncCompletionStatus(
VOID
)
{
return m_requestNotificationStatus;
}
VOID
IN_PROCESS_HANDLER::SetAsyncCompletionStatus(
REQUEST_NOTIFICATION_STATUS requestNotificationStatus
@ -163,7 +176,7 @@ HRESULT
}
hr = sm_pAlloc->Initialize(sizeof(IN_PROCESS_HANDLER),
64); // nThreshold
64); // nThreshold
Finished:
if (FAILED(hr))

View File

@ -3,8 +3,10 @@
#pragma once
#include "precomp.hxx"
#include "requesthandler.h"
#include <memory>
#include "iapplication.h"
#include "inprocessapplication.h"
class IN_PROCESS_APPLICATION;
@ -12,10 +14,13 @@ class IN_PROCESS_HANDLER : public REQUEST_HANDLER
{
public:
IN_PROCESS_HANDLER(
_In_ std::unique_ptr<IN_PROCESS_APPLICATION, IAPPLICATION_DELETER> pApplication,
_In_ IHttpContext *pW3Context,
_In_ IN_PROCESS_APPLICATION *pApplication);
_In_ PFN_REQUEST_HANDLER pRequestHandler,
_In_ void * pRequestHandlerContext,
_In_ PFN_ASYNC_COMPLETION_HANDLER pAsyncCompletion);
~IN_PROCESS_HANDLER() override;
~IN_PROCESS_HANDLER() override = default;
__override
REQUEST_NOTIFICATION_STATUS
@ -33,37 +38,25 @@ public:
TerminateRequest(
bool fClientInitiated
) override;
PVOID
QueryManagedHttpContext(
IHttpContext*
QueryHttpContext(
VOID
);
) const
{
return m_pW3Context;
}
VOID
SetManagedHttpContext(
PVOID pManagedHttpContext
);
IHttpContext*
QueryHttpContext(
VOID
);
BOOL
QueryIsManagedRequestComplete(
VOID
);
VOID
IndicateManagedRequestComplete(
VOID
);
REQUEST_NOTIFICATION_STATUS
QueryAsyncCompletionStatus(
VOID
);
VOID
SetAsyncCompletionStatus(
REQUEST_NOTIFICATION_STATUS requestNotificationStatus
@ -77,12 +70,18 @@ public:
HRESULT
StaticInitialize(VOID);
private:
REQUEST_NOTIFICATION_STATUS
ServerShutdownMessage() const;
PVOID m_pManagedHttpContext;
BOOL m_fManagedRequestComplete;
REQUEST_NOTIFICATION_STATUS m_requestNotificationStatus;
IHttpContext* m_pW3Context;
IN_PROCESS_APPLICATION* m_pApplication;
std::unique_ptr<IN_PROCESS_APPLICATION, IAPPLICATION_DELETER> m_pApplication;
PFN_REQUEST_HANDLER m_pRequestHandler;
void* m_pRequestHandlerContext;
PFN_ASYNC_COMPLETION_HANDLER m_pAsyncCompletionHandler;
static ALLOC_CACHE_HANDLER * sm_pAlloc;
};

View File

@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include "precomp.hxx"
#include "inprocessapplication.h"
#include "inprocesshandler.h"
#include "requesthandler_config.h"
@ -14,7 +13,7 @@ register_callbacks(
_In_ IN_PROCESS_APPLICATION* pInProcessApplication,
_In_ PFN_REQUEST_HANDLER request_handler,
_In_ PFN_SHUTDOWN_HANDLER shutdown_handler,
_In_ PFN_MANAGED_CONTEXT_HANDLER async_completion_handler,
_In_ PFN_ASYNC_COMPLETION_HANDLER async_completion_handler,
_In_ VOID* pvRequstHandlerContext,
_In_ VOID* pvShutdownHandlerContext
)
@ -64,8 +63,6 @@ http_get_server_variable(
PCWSTR pszVariableValue;
DWORD cbLength;
DBG_ASSERT(pszVariableValue != NULL);
HRESULT hr = pInProcessHandler
->QueryHttpContext()
->GetServerVariable(pszVariableName, &pszVariableValue, &cbLength);

View File

@ -0,0 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// Do not remove this file. It is used for precompiled header generation

View File

@ -111,6 +111,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;nothrownew.obj;advapi32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ahadmin.lib;rpcrt4.lib;winhttp.lib;pdh.lib;ws2_32.lib;wbemuuid.lib;iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>Source.def</ModuleDefinitionFile>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -143,6 +144,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;user32.lib;nothrownew.obj;advapi32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ahadmin.lib;rpcrt4.lib;winhttp.lib;pdh.lib;ws2_32.lib;wbemuuid.lib;iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>Source.def</ModuleDefinitionFile>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@ -177,6 +179,7 @@
<OptimizeReferences>true</OptimizeReferences>
<ModuleDefinitionFile>Source.def</ModuleDefinitionFile>
<AdditionalDependencies>kernel32.lib;user32.lib;nothrownew.obj;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;ahadmin.lib;winhttp.lib;odbc32.lib;ws2_32.lib;odbccp32.lib;wbemuuid.lib;iphlpapi.lib;pdh.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -211,6 +214,7 @@
<OptimizeReferences>true</OptimizeReferences>
<ModuleDefinitionFile>Source.def</ModuleDefinitionFile>
<AdditionalDependencies>kernel32.lib;user32.lib;nothrownew.obj;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;ahadmin.lib;rpcrt4.lib;winhttp.lib;pdh.lib;ws2_32.lib;wbemuuid.lib;iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemGroup>

View File

@ -54,7 +54,7 @@ InitializeGlobalConfiguration(
}
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\IIS Extensions\\IIS AspNetCore Module\\Parameters",
L"SOFTWARE\\Microsoft\\IIS Extensions\\IIS AspNetCore Module V2\\Parameters",
0,
KEY_READ,
&hKey) == NO_ERROR)
@ -119,7 +119,7 @@ HRESULT
EnsureOutOfProcessInitializtion()
{
DBG_ASSERT(pServer);
DBG_ASSERT(g_pHttpServer);
HRESULT hr = S_OK;
BOOL fLocked = FALSE;
@ -148,7 +148,7 @@ EnsureOutOfProcessInitializtion()
g_hWinHttpModule = GetModuleHandle(TEXT("winhttp.dll"));
g_hAspNetCoreModule = GetModuleHandle(TEXT("aspnetcore.dll"));
g_hAspNetCoreModule = GetModuleHandle(TEXT("aspnetcorev2.dll"));
hr = WINHTTP_HELPER::StaticInitialize();
if (FAILED(hr))
@ -279,42 +279,20 @@ CreateApplication(
{
UNREFERENCED_PARAMETER(pParameters);
UNREFERENCED_PARAMETER(nParameters);
HRESULT hr = S_OK;
IAPPLICATION *pApplication = NULL;
REQUESTHANDLER_CONFIG *pConfig = NULL;
// Initialze some global variables here
InitializeGlobalConfiguration(pServer);
hr = REQUESTHANDLER_CONFIG::CreateRequestHandlerConfig(pServer, pHttpApplication, &pConfig);
if (FAILED(hr))
{
return hr;
}
REQUESTHANDLER_CONFIG *pConfig = nullptr;
RETURN_IF_FAILED(REQUESTHANDLER_CONFIG::CreateRequestHandlerConfig(pServer, pHttpApplication, &pConfig));
std::unique_ptr<REQUESTHANDLER_CONFIG> pRequestHandlerConfig(pConfig);
hr = EnsureOutOfProcessInitializtion();
if (FAILED(hr))
{
goto Finished;
}
RETURN_IF_FAILED(EnsureOutOfProcessInitializtion());
pApplication = new OUT_OF_PROCESS_APPLICATION(pConfig);
if (pApplication == NULL)
{
hr = HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
goto Finished;
}
std::unique_ptr<OUT_OF_PROCESS_APPLICATION> pApplication = std::make_unique<OUT_OF_PROCESS_APPLICATION>(*pHttpApplication, std::move(pRequestHandlerConfig));
hr = ((OUT_OF_PROCESS_APPLICATION*)pApplication)->Initialize();
if (FAILED(hr))
{
delete pApplication;
pApplication = NULL;
goto Finished;
}
RETURN_IF_FAILED(pApplication->Initialize());
RETURN_IF_FAILED(pApplication->StartMonitoringAppOffline());
*ppApplication = pApplication;
Finished:
return hr;
*ppApplication = pApplication.release();
return S_OK;
}

View File

@ -1,9 +1,12 @@
#include "..\precomp.hxx"
#include "SRWExclusiveLock.h"
OUT_OF_PROCESS_APPLICATION::OUT_OF_PROCESS_APPLICATION(
REQUESTHANDLER_CONFIG *pConfig) :
IHttpApplication& pApplication,
std::unique_ptr<REQUESTHANDLER_CONFIG> pConfig) :
AppOfflineTrackingApplication(pApplication),
m_fWebSocketSupported(WEBSOCKET_STATUS::WEBSOCKET_UNKNOWN),
m_pConfig(pConfig)
m_pConfig(std::move(pConfig))
{
m_status = APPLICATION_STATUS::RUNNING;
m_pProcessManager = NULL;
@ -12,18 +15,13 @@ OUT_OF_PROCESS_APPLICATION::OUT_OF_PROCESS_APPLICATION(
OUT_OF_PROCESS_APPLICATION::~OUT_OF_PROCESS_APPLICATION()
{
SRWExclusiveLock lock(m_srwLock);
if (m_pProcessManager != NULL)
{
m_pProcessManager->ShutdownAllProcesses();
m_pProcessManager->Shutdown();
m_pProcessManager->DereferenceProcessManager();
m_pProcessManager = NULL;
}
if (m_pConfig != NULL)
{
delete m_pConfig;
m_pConfig = NULL;
}
}
HRESULT
@ -56,29 +54,18 @@ OUT_OF_PROCESS_APPLICATION::GetProcess(
_Out_ SERVER_PROCESS **ppServerProcess
)
{
return m_pProcessManager->GetProcess(m_pConfig, QueryWebsocketStatus(), ppServerProcess);
}
REQUESTHANDLER_CONFIG*
OUT_OF_PROCESS_APPLICATION::QueryConfig() const
{
return m_pConfig;
return m_pProcessManager->GetProcess(m_pConfig.get(), QueryWebsocketStatus(), ppServerProcess);
}
__override
VOID
OUT_OF_PROCESS_APPLICATION::ShutDown()
{
AcquireSRWLockExclusive(&m_srwLock);
{
SRWExclusiveLock lock(m_srwLock);
if (m_pProcessManager != NULL)
{
if (m_pProcessManager != NULL)
{
m_pProcessManager->ShutdownAllProcesses();
m_pProcessManager->DereferenceProcessManager();
m_pProcessManager = NULL;
}
m_pProcessManager->Shutdown();
}
ReleaseSRWLockExclusive(&m_srwLock);
}
__override

View File

@ -3,7 +3,9 @@
#pragma once
class OUT_OF_PROCESS_APPLICATION : public APPLICATION
#include "AppOfflineTrackingApplication.h"
class OUT_OF_PROCESS_APPLICATION : public AppOfflineTrackingApplication
{
enum WEBSOCKET_STATUS
{
@ -14,7 +16,8 @@ class OUT_OF_PROCESS_APPLICATION : public APPLICATION
public:
OUT_OF_PROCESS_APPLICATION(
REQUESTHANDLER_CONFIG *pConfig);
IHttpApplication& pApplication,
std::unique_ptr<REQUESTHANDLER_CONFIG> pConfig);
__override
~OUT_OF_PROCESS_APPLICATION() override;
@ -44,14 +47,15 @@ public:
_Out_ IREQUEST_HANDLER **pRequestHandler)
override;
REQUESTHANDLER_CONFIG*
QueryConfig()
const;
BOOL
QueryWebsocketStatus()
const;
REQUESTHANDLER_CONFIG* QueryConfig()
{
return m_pConfig.get();
}
private:
VOID SetWebsocketStatus(IHttpContext *pHttpContext);
@ -60,6 +64,6 @@ private:
SRWLOCK m_srwLock;
IHttpServer *m_pHttpServer;
REQUESTHANDLER_CONFIG* m_pConfig;
WEBSOCKET_STATUS m_fWebSocketSupported;
std::unique_ptr<REQUESTHANDLER_CONFIG> m_pConfig;
};

View File

@ -114,6 +114,11 @@ PROCESS_MANAGER::GetProcess(
DWORD dwProcessIndex = 0;
SERVER_PROCESS *pSelectedServerProcess = NULL;
if (InterlockedCompareExchange(&m_lStopping, 1L, 1L) == 1L)
{
return hr = E_APPLICATION_EXITING;
}
if (!m_fServerProcessListReady)
{
AcquireSRWLockExclusive(&m_srwLock);

View File

@ -88,6 +88,16 @@ public:
ReleaseSRWLockExclusive( &m_srwLock );
}
VOID
Shutdown(
)
{
if (InterlockedCompareExchange(&m_lStopping, 1L, 0L) == 0L)
{
ShutdownAllProcesses();
}
}
VOID
IncrementRapidFailCount(
VOID
@ -103,6 +113,7 @@ public:
m_dwProcessesPerApplication( 1 ),
m_dwRouteToProcessIndex( 0 ),
m_fServerProcessListReady(FALSE),
m_lStopping(0),
m_cRefs( 1 )
{
m_ppServerProcessList = NULL;
@ -193,4 +204,5 @@ private:
volatile static BOOL sm_fWSAStartupDone;
volatile BOOL m_fServerProcessListReady;
volatile LONG m_lStopping;
};

View File

@ -0,0 +1,62 @@
// 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.
#include "stdafx.h"
#include "AppOfflineTrackingApplication.h"
#include "EventLog.h"
#include "exceptions.h"
extern HANDLE g_hEventLog;
HRESULT AppOfflineTrackingApplication::StartMonitoringAppOffline()
{
LOG_INFOF("Starting app_offline monitoring in application %S", m_applicationPath.c_str());
HRESULT hr = StartMonitoringAppOflineImpl();
if (FAILED_LOG(hr))
{
UTILITY::LogEventF(g_hEventLog,
EVENTLOG_WARNING_TYPE,
ASPNETCORE_EVENT_MONITOR_APPOFFLINE_ERROR,
ASPNETCORE_EVENT_MONITOR_APPOFFLINE_ERROR_MSG,
m_applicationPath.c_str(),
hr);
}
return hr;
}
HRESULT AppOfflineTrackingApplication::StartMonitoringAppOflineImpl()
{
if (m_fileWatcher)
{
RETURN_IF_FAILED(E_UNEXPECTED);
}
m_fileWatcher = std::make_unique<FILE_WATCHER>();
RETURN_IF_FAILED(m_fileWatcher->Create());
m_fileWatcherEntry.reset(new FILE_WATCHER_ENTRY(m_fileWatcher.get()));
// FileWatcherCompletionRoutine calls dereference so we need to get ourselves a reference count
m_fileWatcherEntry->ReferenceFileWatcherEntry();
RETURN_IF_FAILED(m_fileWatcherEntry->Create(
m_applicationPath.c_str(),
L"app_offline.htm",
std::bind(&AppOfflineTrackingApplication::OnAppOffline, this),
NULL));
return S_OK;
}
void AppOfflineTrackingApplication::OnAppOffline()
{
LOG_INFOF("Received app_offline notification in application %S", m_applicationPath.c_str());
m_fileWatcherEntry->StopMonitor();
m_status = APPLICATION_STATUS::RECYCLED;
UTILITY::LogEventF(g_hEventLog,
EVENTLOG_INFORMATION_TYPE,
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE,
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE_MSG,
m_applicationPath.c_str());
Recycle();
}

View File

@ -0,0 +1,36 @@
// 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.
#pragma once
#include <Windows.h>
#include "application.h"
#include "filewatcher.h"
class AppOfflineTrackingApplication: public APPLICATION
{
public:
AppOfflineTrackingApplication(const IHttpApplication& application)
: m_applicationPath(application.GetApplicationPhysicalPath()),
m_fileWatcher(nullptr),
m_fileWatcherEntry(nullptr)
{
}
~AppOfflineTrackingApplication() override = default;
HRESULT
StartMonitoringAppOffline();
virtual
VOID
OnAppOffline();
private:
HRESULT
StartMonitoringAppOflineImpl();
std::wstring m_applicationPath;
std::unique_ptr<FILE_WATCHER> m_fileWatcher;
std::unique_ptr<FILE_WATCHER_ENTRY, FILE_WATCHER_ENTRY_DELETER> m_fileWatcherEntry;
};

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
@ -188,7 +188,9 @@
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="AppOfflineTrackingApplication.h" />
<ClInclude Include="environmentvariablehelpers.h" />
<ClInclude Include="filewatcher.h" />
<ClInclude Include="NullOutputManager.h" />
<ClInclude Include="FileOutputManager.h" />
<ClInclude Include="environmentvariablehash.h" />
@ -199,7 +201,9 @@
<ClInclude Include="stdafx.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="AppOfflineTrackingApplication.cpp" />
<ClCompile Include="FileOutputManager.cpp" />
<ClCompile Include="filewatcher.cpp" />
<ClCompile Include="requesthandler_config.cpp" />
<ClCompile Include="LoggingHelpers.cpp" />
<ClCompile Include="PipeOutputManager.cpp" />

View File

@ -1,11 +1,9 @@
// 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 "filewatcher.h"
#include "ntassert.h"
#include "applicationinfo.h"
FILE_WATCHER::FILE_WATCHER() :
m_hCompletionPort(NULL),
m_hChangeNotificationThread(NULL),
@ -214,7 +212,7 @@ FILE_WATCHER_ENTRY::FILE_WATCHER_ENTRY(FILE_WATCHER * pFileMonitor) :
_pFileMonitor(pFileMonitor),
_hDirectory(INVALID_HANDLE_VALUE),
_hImpersonationToken(NULL),
_pApplicationInfo(NULL),
_pCallback(),
_lStopMonitorCalled(0),
_cRefs(1),
_fIsValid(TRUE)
@ -225,6 +223,8 @@ FILE_WATCHER_ENTRY::FILE_WATCHER_ENTRY(FILE_WATCHER * pFileMonitor) :
FILE_WATCHER_ENTRY::~FILE_WATCHER_ENTRY()
{
StopMonitor();
_dwSignature = FILE_WATCHER_ENTRY_SIGNATURE_FREE;
if (_hDirectory != INVALID_HANDLE_VALUE)
@ -330,16 +330,16 @@ HRESULT
}
}
Finished:
ReleaseSRWLockExclusive(&_srwLock);
if (fFileChanged)
{
//
// so far we only monitoring app_offline
//
_pApplicationInfo->UpdateAppOfflineFileHandle();
_pCallback();
}
Finished:
ReleaseSRWLockExclusive(&_srwLock);
return hr;
}
@ -399,7 +399,7 @@ HRESULT
FILE_WATCHER_ENTRY::Create(
_In_ PCWSTR pszDirectoryToMonitor,
_In_ PCWSTR pszFileNameToMonitor,
_In_ APPLICATION_INFO* pApplicationInfo,
_In_ std::function<void()> pCallback,
_In_ HANDLE hImpersonationToken
)
{
@ -408,17 +408,14 @@ FILE_WATCHER_ENTRY::Create(
if (pszDirectoryToMonitor == NULL ||
pszFileNameToMonitor == NULL ||
pApplicationInfo == NULL)
pCallback == NULL)
{
DBG_ASSERT(FALSE);
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
goto Finished;
}
//
//remember the application
//
_pApplicationInfo = pApplicationInfo;
_pCallback = pCallback;
if (FAILED(hr = _strFileName.Copy(pszFileNameToMonitor)))
{

View File

@ -1,20 +1,18 @@
// 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 <windows.h>
#include "stringu.h"
#include <Windows.h>
#include <functional>
#define FILE_WATCHER_SHUTDOWN_KEY (ULONG_PTR)(-1)
#define FILE_WATCHER_ENTRY_BUFFER_SIZE 4096
#define FILE_NOTIFY_VALID_MASK 0x00000fff
#define FILE_NOTIFY_VALID_MASK 0x00000fff
#define FILE_WATCHER_ENTRY_SIGNATURE ((DWORD) 'FWES')
#define FILE_WATCHER_ENTRY_SIGNATURE_FREE ((DWORD) 'sewf')
class APPLICATION_INFO;
class FILE_WATCHER{
public:
@ -62,7 +60,7 @@ public:
Create(
_In_ PCWSTR pszDirectoryToMonitor,
_In_ PCWSTR pszFileNameToMonitor,
_In_ APPLICATION_INFO* pApplicationInfo,
_In_ std::function<void()> pCallback,
_In_ HANDLE hImpersonationToken
);
@ -111,11 +109,20 @@ private:
HANDLE _hImpersonationToken;
HANDLE _hDirectory;
FILE_WATCHER* _pFileMonitor;
APPLICATION_INFO* _pApplicationInfo;
STRU _strFileName;
STRU _strDirectoryName;
LONG _lStopMonitorCalled;
mutable LONG _cRefs;
BOOL _fIsValid;
SRWLOCK _srwLock;
std::function<void()> _pCallback;
};
struct FILE_WATCHER_ENTRY_DELETER
{
void operator()(FILE_WATCHER_ENTRY* entry) const
{
entry->DereferenceFileWatcherEntry();
}
};

View File

@ -54,7 +54,8 @@ enum APP_HOSTING_MODEL
class REQUESTHANDLER_CONFIG
{
public:
virtual
~REQUESTHANDLER_CONFIG();
static
@ -217,7 +218,6 @@ protected:
REQUESTHANDLER_CONFIG() :
m_fStdoutLogEnabled(FALSE),
m_pEnvironmentVariables(NULL),
m_cRefs(1),
m_hostingModel(HOSTING_UNKNOWN),
m_ppStrArguments(NULL)
{
@ -229,8 +229,6 @@ protected:
IHttpApplication *pHttpApplication
);
mutable LONG m_cRefs;
DWORD m_dwRequestTimeoutInMS;
DWORD m_dwStartupTimeLimitInMS;
DWORD m_dwShutdownTimeLimitInMS;
@ -254,4 +252,5 @@ protected:
STRU m_struHostFxrLocation;
PWSTR* m_ppStrArguments;
DWORD m_dwArgc;
};

View File

@ -24,5 +24,3 @@
#include "dbgutil.h"
#include "ahutil.h"
#include "hashfn.h"

View File

@ -0,0 +1,31 @@
// 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.Reflection;
using Microsoft.Extensions.Logging.Testing;
using Xunit.Abstractions;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
public class FixtureLoggedTest: LoggedTest
{
private readonly IISTestSiteFixture _fixture;
public FixtureLoggedTest(IISTestSiteFixture fixture)
{
_fixture = fixture;
}
public override void Initialize(MethodInfo methodInfo, object[] testMethodArguments, ITestOutputHelper testOutputHelper)
{
base.Initialize(methodInfo, testMethodArguments, testOutputHelper);
_fixture.Attach(this);
}
public override void Dispose()
{
_fixture.Detach(this);
base.Dispose();
}
}
}

View File

@ -6,23 +6,22 @@ using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Testing.xunit;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
[Collection(IISTestSiteCollection.Name)]
public class SynchronousReadAndWriteTests
public class SynchronousReadAndWriteTests: FixtureLoggedTest
{
private readonly IISTestSiteFixture _fixture;
public SynchronousReadAndWriteTests(IISTestSiteFixture fixture)
public SynchronousReadAndWriteTests(IISTestSiteFixture fixture): base(fixture)
{
_fixture = fixture;
}
[ConditionalFact(Skip = "See: https://github.com/aspnet/IISIntegration/issues/766")]
[ConditionalFact]
public async Task ReadAndWriteSynchronously()
{
for (int i = 0; i < 100; i++)

View File

@ -53,15 +53,20 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
}
// Defaults to inprocess specific deployment parameters
public static DeploymentParameters GetBaseDeploymentParameters(string site = "InProcessWebSite")
public static DeploymentParameters GetBaseDeploymentParameters(string site = null, HostingModel hostingModel = HostingModel.InProcess, bool publish = false)
{
if (site == null)
{
site = hostingModel == HostingModel.InProcess ? "InProcessWebSite" : "OutOfProcessWebSite";
}
return new DeploymentParameters(Helpers.GetTestWebSitePath(site), DeployerSelector.ServerType, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64)
{
TargetFramework = Tfm.NetCoreApp22,
ApplicationType = ApplicationType.Portable,
AncmVersion = AncmVersion.AspNetCoreModuleV2,
HostingModel = HostingModel.InProcess,
PublishApplicationBeforeDeployment = site == "InProcessWebSite",
HostingModel = hostingModel,
PublishApplicationBeforeDeployment = publish,
};
}

View File

@ -2,10 +2,12 @@
// 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.Net.Http;
using System.Threading;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Testing;
@ -14,6 +16,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
public class IISTestSiteFixture : IDisposable
{
private readonly ApplicationDeployer _deployer;
private readonly ForwardingProvider _forwardingProvider;
public IISTestSiteFixture()
{
@ -30,14 +33,17 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
PublishApplicationBeforeDeployment = true,
};
_forwardingProvider = new ForwardingProvider();
var loggerFactory = logging.CreateLoggerFactory(null, nameof(IISTestSiteFixture));
loggerFactory.AddProvider(_forwardingProvider);
if (deploymentParameters.ServerType == ServerType.IIS)
{
// Currently hosting throws if the Servertype = IIS.
_deployer = new IISDeployer(deploymentParameters, logging.CreateLoggerFactory(null, nameof(IISTestSiteFixture)));
_deployer = new IISDeployer(deploymentParameters, loggerFactory);
}
else if (deploymentParameters.ServerType == ServerType.IISExpress)
{
_deployer = new IISExpressDeployer(deploymentParameters, logging.CreateLoggerFactory(null, nameof(IISTestSiteFixture)));
_deployer = new IISExpressDeployer(deploymentParameters, loggerFactory);
}
DeploymentResult = _deployer.DeployAsync().Result;
@ -60,5 +66,88 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{
_deployer.Dispose();
}
public void Attach(LoggedTest test)
{
if (_forwardingProvider.LoggerFactory != null)
{
throw new InvalidOperationException("Test instance is already attached to this fixture");
}
_forwardingProvider.LoggerFactory = test.LoggerFactory;
}
public void Detach(LoggedTest test)
{
if (_forwardingProvider.LoggerFactory != test.LoggerFactory)
{
throw new InvalidOperationException("Different test is attached to this fixture");
}
_forwardingProvider.LoggerFactory = null;
}
private class ForwardingProvider : ILoggerProvider
{
private readonly List<ForwardingLogger> _loggers = new List<ForwardingLogger>();
private ILoggerFactory _loggerFactory;
public ILoggerFactory LoggerFactory
{
get => _loggerFactory;
set
{
lock (_loggers)
{
_loggerFactory = value;
foreach (var logger in _loggers)
{
logger.Logger = _loggerFactory?.CreateLogger("FIXTURE:" + logger.Name);
}
}
}
}
public void Dispose()
{
lock (_loggers)
{
_loggers.Clear();
}
}
public ILogger CreateLogger(string categoryName)
{
lock (_loggers)
{
var logger = new ForwardingLogger(categoryName);
_loggers.Add(logger);
return logger;
}
}
}
internal class ForwardingLogger : ILogger
{
public ForwardingLogger(string name)
{
Name = name;
}
public ILogger Logger { get; set; }
public string Name { get; set; }
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
Logger?.Log(logLevel, eventId, state, exception, formatter);
}
public bool IsEnabled(LogLevel logLevel) => Logger?.IsEnabled(logLevel) == true;
public IDisposable BeginScope<TState>(TState state) => Logger?.BeginScope(state);
}
}
}

View File

@ -101,7 +101,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<AdditionalLibraryDirectories>..\..\src\AspNetCoreModuleV2\InProcessRequestHandler\$(Configuration)\;</AdditionalLibraryDirectories>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;inprocessapplication.obj;inprocesshandler.obj;ahadmin.lib;Rpcrt4.lib;inprocessapplicationbase.obj;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;inprocessapplication.obj;inprocesshandler.obj;ahadmin.lib;Rpcrt4.lib;inprocessapplicationbase.obj;precomp.obj;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Lib>
<AdditionalDependencies>
@ -127,7 +127,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<AdditionalLibraryDirectories>..\..\src\AspNetCoreModuleV2\InProcessRequestHandler\x64\$(Configuration)\;</AdditionalLibraryDirectories>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;inprocessapplication.obj;inprocesshandler.obj;ahadmin.lib;Rpcrt4.lib;inprocessapplicationbase.obj;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;inprocessapplication.obj;inprocesshandler.obj;ahadmin.lib;Rpcrt4.lib;inprocessapplicationbase.obj;precomp.obj;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Lib>
<AdditionalDependencies>
@ -153,7 +153,7 @@
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<AdditionalLibraryDirectories>..\..\src\AspNetCoreModuleV2\InProcessRequestHandler\$(Configuration)\;</AdditionalLibraryDirectories>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;inprocessapplication.obj;inprocesshandler.obj;ahadmin.lib;Rpcrt4.lib;inprocessapplicationbase.obj;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;inprocessapplication.obj;inprocesshandler.obj;ahadmin.lib;Rpcrt4.lib;inprocessapplicationbase.obj;precomp.obj;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Lib>
<AdditionalDependencies>
@ -179,7 +179,7 @@
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<AdditionalLibraryDirectories>..\..\src\AspNetCoreModuleV2\InProcessRequestHandler\x64\$(Configuration)\;</AdditionalLibraryDirectories>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;inprocessapplication.obj;inprocesshandler.obj;ahadmin.lib;Rpcrt4.lib;inprocessapplicationbase.obj;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;inprocessapplication.obj;inprocesshandler.obj;ahadmin.lib;Rpcrt4.lib;inprocessapplicationbase.obj;precomp.obj;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Lib>
<AdditionalDependencies>

View File

@ -41,7 +41,6 @@ public:
class MockElement : public IAppHostElement
{
public:
MOCK_METHOD2_WITH_CALLTYPE(__stdcall, QueryInterface, HRESULT(REFIID riid, void ** ppvObject));
MOCK_METHOD0_WITH_CALLTYPE(__stdcall, AddRef, ULONG());
MOCK_METHOD0_WITH_CALLTYPE(__stdcall, Release, ULONG());
@ -167,6 +166,16 @@ class MockHttpServer : public IHttpServer
}
};
class MockHttpApplication: public IHttpApplication
{
public:
MOCK_CONST_METHOD0(GetApplicationPhysicalPath, PCWSTR ());
MOCK_CONST_METHOD0(GetApplicationId, PCWSTR ());
MOCK_CONST_METHOD0(GetAppConfigPath, PCWSTR ());
MOCK_METHOD0(GetModuleContextContainer, IHttpModuleContextContainer* ());
};
class MockRequestHandlerConfig : public REQUESTHANDLER_CONFIG
{
public:

View File

@ -7,6 +7,9 @@
#include "inprocessapplication.h"
#include "fakeclasses.h"
using ::testing::_;
using ::testing::NiceMock;
// Externals defined in inprocess
BOOL g_fProcessDetach;
HANDLE g_hEventLog;
@ -15,16 +18,22 @@ namespace InprocessTests
{
TEST(InProcessTest, NoNullRefForExePath)
{
auto server = new MockHttpServer();
auto requestHandlerConfig = MockRequestHandlerConfig::CreateConfig();
auto config = std::unique_ptr<REQUESTHANDLER_CONFIG>(requestHandlerConfig);
MockHttpServer server;
NiceMock<MockHttpApplication> application;
ON_CALL(application, GetApplicationPhysicalPath())
.WillByDefault(testing::Return(L"Some path"));
auto requestHandlerConfig = std::unique_ptr<REQUESTHANDLER_CONFIG>(MockRequestHandlerConfig::CreateConfig());
std::wstring exePath(L"hello");
std::array<APPLICATION_PARAMETER, 1> parameters {
std::array<APPLICATION_PARAMETER, 1> parameters{
{"InProcessExeLocation", exePath.data()}
};
IN_PROCESS_APPLICATION *app = new IN_PROCESS_APPLICATION(server, std::move(config), parameters.data(), 1);
IN_PROCESS_APPLICATION *app = new IN_PROCESS_APPLICATION(server, application, std::move(requestHandlerConfig), parameters.data(), 1);
ASSERT_STREQ(app->QueryExeLocation(), L"hello");
}
}

View File

@ -19,21 +19,25 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
public class AppOfflineTests : IISFunctionalTestBase
{
// TODO these will differ between IIS and IISExpress
[ConditionalFact]
public async Task AppOfflineDroppedWhileSiteIsDown_SiteReturns503()
[ConditionalTheory]
[InlineData(HostingModel.InProcess)]
[InlineData(HostingModel.OutOfProcess)]
public async Task AppOfflineDroppedWhileSiteIsDown_SiteReturns503(HostingModel hostingModel)
{
var deploymentResult = await DeployApp();
var deploymentResult = await DeployApp(hostingModel);
AddAppOffline(deploymentResult.DeploymentResult.ContentRoot);
await AssertAppOffline(deploymentResult);
}
[ConditionalFact]
public async Task AppOfflineDroppedWhileSiteIsDown_CustomResponse()
[ConditionalTheory]
[InlineData(HostingModel.InProcess)]
[InlineData(HostingModel.OutOfProcess)]
public async Task AppOfflineDroppedWhileSiteIsDown_CustomResponse(HostingModel hostingModel)
{
var expectedResponse = "The app is offline.";
var deploymentResult = await DeployApp();
var deploymentResult = await DeployApp(hostingModel);
AddAppOffline(deploymentResult.DeploymentResult.ContentRoot, expectedResponse);
@ -41,9 +45,9 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
}
[ConditionalFact]
public async Task AppOfflineDroppedWhileSiteRunning_SiteShutsDown()
public async Task AppOfflineDroppedWhileSiteRunning_SiteShutsDown_InProcess()
{
var deploymentResult = await AssertStarts();
var deploymentResult = await AssertStarts(HostingModel.InProcess);
AddAppOffline(deploymentResult.DeploymentResult.ContentRoot);
@ -51,9 +55,26 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
}
[ConditionalFact]
public async Task AppOfflineDropped_CanRemoveAppOfflineAfterAddingAndSiteWorks()
public async Task AppOfflineDroppedWhileSiteRunning_SiteShutsDown_OutOfProcess()
{
var deploymentResult = await DeployApp();
var deploymentResult = await AssertStarts(HostingModel.OutOfProcess);
// Repeat dropping file and restarting multiple times
for (int i = 0; i < 5; i++)
{
AddAppOffline(deploymentResult.DeploymentResult.ContentRoot);
await AssertAppOffline(deploymentResult);
RemoveAppOffline(deploymentResult.DeploymentResult.ContentRoot);
await AssertRunning(deploymentResult);
}
}
[ConditionalTheory]
[InlineData(HostingModel.InProcess)]
[InlineData(HostingModel.OutOfProcess)]
public async Task AppOfflineDropped_CanRemoveAppOfflineAfterAddingAndSiteWorks(HostingModel hostingModel)
{
var deploymentResult = await DeployApp(hostingModel);
AddAppOffline(deploymentResult.DeploymentResult.ContentRoot);
@ -61,13 +82,12 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
RemoveAppOffline(deploymentResult.DeploymentResult.ContentRoot);
var response = await deploymentResult.HttpClient.GetAsync("HelloWorld");
await AssertRunning(deploymentResult);
}
private async Task<IISDeploymentResult> DeployApp()
private async Task<IISDeploymentResult> DeployApp(HostingModel hostingModel = HostingModel.InProcess)
{
var deploymentParameters = Helpers.GetBaseDeploymentParameters();
var deploymentParameters = Helpers.GetBaseDeploymentParameters(hostingModel: hostingModel, publish: true);
return await DeployAsync(deploymentParameters);
}
@ -118,18 +138,21 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
Assert.True(hostShutdownToken.IsCancellationRequested);
}
private async Task<IISDeploymentResult> AssertStarts()
private async Task<IISDeploymentResult> AssertStarts(HostingModel hostingModel)
{
var deploymentParameters = Helpers.GetBaseDeploymentParameters();
var deploymentResult = await DeployApp(hostingModel);
var deploymentResult = await DeployAsync(deploymentParameters);
await AssertRunning(deploymentResult);
return deploymentResult;
}
private static async Task AssertRunning(IISDeploymentResult deploymentResult)
{
var response = await deploymentResult.RetryingHttpClient.GetAsync("HelloWorld");
var responseText = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello World", responseText);
return deploymentResult;
}
}
}

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
[ConditionalFact]
public async Task Authentication_InProcess()
{
var deploymentParameters = Helpers.GetBaseDeploymentParameters();
var deploymentParameters = Helpers.GetBaseDeploymentParameters(publish: true);
deploymentParameters.ServerConfigTemplateContent = GetWindowsAuthConfig();
var deploymentResult = await DeployAsync(deploymentParameters);

View File

@ -73,7 +73,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
var tempFile = Path.GetTempFileName();
try
{
var deploymentParameters = Helpers.GetBaseDeploymentParameters();
var deploymentParameters = Helpers.GetBaseDeploymentParameters(publish: true);
deploymentParameters.EnvironmentVariables["ASPNETCORE_MODULE_DEBUG_FILE"] = tempFile;
var deploymentResult = await DeployAsync(deploymentParameters);
@ -82,7 +82,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
StopServer();
var logContents = File.ReadAllText(tempFile);
Assert.Contains("[aspnetcore.dll]", logContents);
Assert.Contains("[aspnetcorev2.dll]", logContents);
Assert.Contains("[aspnetcorev2_inprocess.dll]", logContents);
}
finally

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
[ConditionalFact]
public async Task ServerShutsDownWhenMainExits()
{
var parameters = Helpers.GetBaseDeploymentParameters();
var parameters = Helpers.GetBaseDeploymentParameters(publish: true);
var result = await DeployAsync(parameters);
var response = await result.RetryingHttpClient.GetAsync("/Shutdown");