Finish up appoffline refactoring (#1018)

This commit is contained in:
Pavel Krymets 2018-07-09 16:17:33 -07:00 committed by GitHub
parent cbfd791d7b
commit f7936ac062
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 634 additions and 622 deletions

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,19 +82,12 @@ 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.
@ -152,11 +124,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

@ -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:

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

@ -107,37 +107,6 @@ ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler(
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;
}
// make sure assmebly is loaded and application is created
hr = m_pApplicationInfo->EnsureApplicationCreated(pHttpContext);
if (FAILED(hr))
@ -147,18 +116,18 @@ ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler(
m_pApplicationInfo->ExtractApplication(&pApplication);
// 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);
hr = pApplication->CreateHandler(pHttpContext, &m_pHandler);
if (FAILED(hr))
{

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

@ -11,6 +11,7 @@ enum APPLICATION_STATUS
STARTING,
RUNNING,
SHUTDOWN,
RECYCLED,
FAIL
};

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

@ -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

@ -5,15 +5,17 @@
#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 +24,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

@ -1,15 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "precomp.hxx"
#include "StartupExceptionApplication.h"
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,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "precomp.hxx"
#include "StartupExceptionApplication.h"
#include "StartupExceptionHandler.h"
REQUEST_NOTIFICATION_STATUS StartupExceptionHandler::OnExecuteRequestHandler()
@ -28,15 +31,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

@ -4,7 +4,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 +38,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 +88,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

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "precomp.hxx"
#include "inprocessapplication.h"
#include "inprocesshandler.h"
#include "hostfxroptions.h"
@ -18,17 +19,17 @@ 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?
@ -664,14 +665,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,

View File

@ -3,7 +3,6 @@
#pragma once
#include "precomp.hxx"
#include "InProcessApplicationBase.h"
#include "inprocesshandler.h"
#include "requesthandler_config.h"
@ -17,7 +16,8 @@ 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);
@ -105,17 +105,21 @@ public:
return s_Application;
}
REQUESTHANDLER_CONFIG*
QueryConfig() const;
PCWSTR
QueryExeLocation()
{
return m_struExeLocation.QueryStr();
}
REQUESTHANDLER_CONFIG*
QueryConfig()
{
return m_pConfig.get();
}
private:
IHttpServer* const m_pHttpServer;
IHttpServer & m_pHttpServer;
// Thread executing the .NET Core process
HANDLE m_hThread;
@ -144,12 +148,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

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "precomp.hxx"
#include "inprocesshandler.h"
#include "inprocessapplication.h"
#include "aspnetcore_event.h"
@ -12,9 +13,10 @@ IN_PROCESS_HANDLER::IN_PROCESS_HANDLER(
_In_ IHttpContext *pW3Context,
_In_ IN_PROCESS_APPLICATION *pApplication
): m_pW3Context(pW3Context),
m_pApplication(pApplication)
m_requestNotificationStatus(RQ_NOTIFICATION_PENDING),
m_pApplication(pApplication),
m_fManagedRequestComplete(FALSE)
{
m_fManagedRequestComplete = FALSE;
}
IN_PROCESS_HANDLER::~IN_PROCESS_HANDLER()

View File

@ -3,7 +3,6 @@
#pragma once
#include "precomp.hxx"
#include "requesthandler.h"
class IN_PROCESS_APPLICATION;

View File

@ -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

@ -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

@ -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);

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");