485 lines
13 KiB
C++
485 lines
13 KiB
C++
// 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"
|
|
|
|
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 = NULL;
|
|
}
|
|
|
|
if (m_pApplication != NULL)
|
|
{
|
|
// shutdown the application
|
|
m_pApplication->ShutDown();
|
|
m_pApplication->DereferenceApplication();
|
|
m_pApplication = NULL;
|
|
}
|
|
|
|
// configuration should be dereferenced after application shutdown
|
|
// since the former will use it during shutdown
|
|
if (m_pConfiguration != NULL)
|
|
{
|
|
// Need to dereference the configuration instance
|
|
m_pConfiguration->DereferenceConfiguration();
|
|
m_pConfiguration = NULL;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
APPLICATION_INFO::Initialize(
|
|
_In_ ASPNETCORE_CONFIG *pConfiguration,
|
|
_In_ FILE_WATCHER *pFileWatcher
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
DBG_ASSERT(pConfiguration);
|
|
DBG_ASSERT(pFileWatcher);
|
|
|
|
m_pConfiguration = pConfiguration;
|
|
|
|
// reference the configuration instance to prevent it will be not release
|
|
// earlier in case of configuration change and shutdown
|
|
m_pConfiguration->ReferenceConfiguration();
|
|
|
|
hr = m_applicationInfoKey.Initialize(pConfiguration->QueryConfigPath()->QueryStr());
|
|
if (FAILED(hr))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
if (m_pFileWatcherEntry == NULL)
|
|
{
|
|
m_pFileWatcherEntry = new FILE_WATCHER_ENTRY(pFileWatcher);
|
|
if (m_pFileWatcherEntry == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Finished;
|
|
}
|
|
}
|
|
|
|
UpdateAppOfflineFileHandle();
|
|
|
|
Finished:
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
APPLICATION_INFO::StartMonitoringAppOffline()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
if (m_pFileWatcherEntry != NULL)
|
|
{
|
|
hr = m_pFileWatcherEntry->Create(m_pConfiguration->QueryApplicationPhysicalPath()->QueryStr(), L"app_offline.htm", this, NULL);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
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;
|
|
|
|
if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(strFilePath.QueryStr()) &&
|
|
GetLastError() == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
m_fAppOfflineFound = FALSE;
|
|
}
|
|
else
|
|
{
|
|
m_fAppOfflineFound = TRUE;
|
|
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;
|
|
}
|
|
}
|
|
|
|
// recycle the application
|
|
if (m_pApplication != NULL)
|
|
{
|
|
STACK_STRU(strEventMsg, 256);
|
|
if (SUCCEEDED(strEventMsg.SafeSnwprintf(
|
|
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE_MSG,
|
|
m_pApplication->QueryConfig()->QueryApplicationPath()->QueryStr())))
|
|
{
|
|
UTILITY::LogEvent(g_hEventLog,
|
|
EVENTLOG_INFORMATION_TYPE,
|
|
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE,
|
|
strEventMsg.QueryStr());
|
|
}
|
|
|
|
m_pApplication->ShutDown();
|
|
m_pApplication->DereferenceApplication();
|
|
m_pApplication = NULL;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
APPLICATION_INFO::EnsureApplicationCreated()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fLocked = FALSE;
|
|
APPLICATION* pApplication = NULL;
|
|
STACK_STRU(struFileName, 300); // >MAX_PATH
|
|
STRU hostFxrDllLocation;
|
|
|
|
if (m_pApplication != NULL)
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
if ( m_pConfiguration->QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS )
|
|
{
|
|
if ( FAILED( hr = HOSTFXR_UTILITY::GetHostFxrParameters( m_pConfiguration ) ) )
|
|
{
|
|
goto Finished;
|
|
}
|
|
}
|
|
|
|
hr = FindRequestHandlerAssembly();
|
|
if (FAILED(hr))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
if (m_pApplication == NULL)
|
|
{
|
|
AcquireSRWLockExclusive(&m_srwLock);
|
|
fLocked = TRUE;
|
|
if (m_pApplication != NULL)
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
if (m_pfnAspNetCoreCreateApplication == NULL)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
|
|
goto Finished;
|
|
}
|
|
|
|
hr = m_pfnAspNetCoreCreateApplication(m_pServer, m_pConfiguration, &pApplication);
|
|
if (FAILED(hr))
|
|
{
|
|
goto Finished;
|
|
}
|
|
m_pApplication = pApplication;
|
|
}
|
|
Finished:
|
|
if (fLocked)
|
|
{
|
|
ReleaseSRWLockExclusive(&m_srwLock);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
APPLICATION_INFO::FindRequestHandlerAssembly()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fLocked = FALSE;
|
|
STACK_STRU(struFileName, 256);
|
|
|
|
if (g_fAspnetcoreRHLoadedError)
|
|
{
|
|
hr = E_APPLICATION_ACTIVATION_EXEC_FAILURE;
|
|
goto Finished;
|
|
}
|
|
else if (!g_fAspnetcoreRHAssemblyLoaded)
|
|
{
|
|
AcquireSRWLockExclusive(&g_srwLock);
|
|
fLocked = TRUE;
|
|
if (g_fAspnetcoreRHLoadedError)
|
|
{
|
|
hr = E_APPLICATION_ACTIVATION_EXEC_FAILURE;
|
|
goto Finished;
|
|
}
|
|
if (g_fAspnetcoreRHAssemblyLoaded)
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
if (FAILED(hr = FindNativeAssemblyFromHostfxr(&struFileName)))
|
|
{
|
|
// TODO eventually make this fail for in process loading.
|
|
hr = FindNativeAssemblyFromGlobalLocation(&struFileName);
|
|
if (FAILED(hr))
|
|
{
|
|
goto Finished;
|
|
}
|
|
}
|
|
|
|
g_hAspnetCoreRH = LoadLibraryW(struFileName.QueryStr());
|
|
if (g_hAspnetCoreRH == NULL)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Finished;
|
|
}
|
|
|
|
g_pfnAspNetCoreCreateApplication = (PFN_ASPNETCORE_CREATE_APPLICATION)
|
|
GetProcAddress(g_hAspnetCoreRH, "CreateApplication");
|
|
if (g_pfnAspNetCoreCreateApplication == NULL)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Finished;
|
|
}
|
|
|
|
g_pfnAspNetCoreCreateRequestHandler = (PFN_ASPNETCORE_CREATE_REQUEST_HANDLER)
|
|
GetProcAddress(g_hAspnetCoreRH, "CreateRequestHandler");
|
|
if (g_pfnAspNetCoreCreateRequestHandler == NULL)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Finished;
|
|
}
|
|
g_fAspnetcoreRHAssemblyLoaded = TRUE;
|
|
}
|
|
|
|
Finished:
|
|
//
|
|
// Question: we remember the load failure so that we will not try again.
|
|
// User needs to check whether the fuction pointer is NULL
|
|
//
|
|
m_pfnAspNetCoreCreateApplication = g_pfnAspNetCoreCreateApplication;
|
|
m_pfnAspNetCoreCreateRequestHandler = g_pfnAspNetCoreCreateRequestHandler;
|
|
if (!g_fAspnetcoreRHLoadedError && FAILED(hr))
|
|
{
|
|
g_fAspnetcoreRHLoadedError = TRUE;
|
|
}
|
|
|
|
if (fLocked)
|
|
{
|
|
ReleaseSRWLockExclusive(&g_srwLock);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
APPLICATION_INFO::FindNativeAssemblyFromGlobalLocation(STRU* struFilename)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwSize = MAX_PATH;
|
|
BOOL fDone = FALSE;
|
|
DWORD dwPosition = 0;
|
|
|
|
// Though we could call LoadLibrary(L"aspnetcorerh.dll") relying the OS to solve
|
|
// the path (the targeted dll is the same folder of w3wp.exe/iisexpress)
|
|
// let's still load with full path to avoid security issue
|
|
if (FAILED(hr = struFilename->Resize(dwSize + 20)))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
while (!fDone)
|
|
{
|
|
DWORD dwReturnedSize = GetModuleFileNameW(g_hModule, struFilename->QueryStr(), dwSize);
|
|
if (dwReturnedSize == 0)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
fDone = TRUE;
|
|
goto Finished;
|
|
}
|
|
else if ((dwReturnedSize == dwSize) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
|
|
{
|
|
dwSize *= 2; // smaller buffer. increase the buffer and retry
|
|
if (FAILED(hr = struFilename->Resize(dwSize + 20))) // + 20 for aspnetcorerh.dll
|
|
{
|
|
goto Finished;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fDone = TRUE;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr = struFilename->SyncWithBuffer()))
|
|
{
|
|
goto Finished;
|
|
}
|
|
dwPosition = struFilename->LastIndexOf(L'\\', 0);
|
|
struFilename->QueryStr()[dwPosition] = L'\0';
|
|
|
|
if (FAILED(hr = struFilename->SyncWithBuffer()) ||
|
|
FAILED(hr = struFilename->Append(L"\\")) ||
|
|
FAILED(hr = struFilename->Append(g_pwzAspnetcoreRequestHandlerName)))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
Finished:
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Tries to find aspnetcorerh.dll from the application
|
|
// Calls into hostfxr.dll to find it.
|
|
// Will leave hostfxr.dll loaded as it will be used again to call hostfxr_main.
|
|
//
|
|
|
|
HRESULT
|
|
APPLICATION_INFO::FindNativeAssemblyFromHostfxr(
|
|
STRU* struFilename
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
STRU struApplicationFullPath;
|
|
STRU struNativeSearchPaths;
|
|
STRU struNativeDllLocation;
|
|
HMODULE hmHostFxrDll = NULL;
|
|
INT intHostFxrExitCode = 0;
|
|
INT intIndex = -1;
|
|
INT intPrevIndex = 0;
|
|
BOOL fFound = FALSE;
|
|
DWORD dwBufferSize = 1024 * 10;
|
|
|
|
DBG_ASSERT(struFileName != NULL);
|
|
|
|
hmHostFxrDll = LoadLibraryW(m_pConfiguration->QueryHostFxrFullPath());
|
|
|
|
if (hmHostFxrDll == NULL)
|
|
{
|
|
// Could not load hostfxr
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Finished;
|
|
}
|
|
|
|
hostfxr_get_native_search_directories_fn pFnHostFxrSearchDirectories = (hostfxr_get_native_search_directories_fn)
|
|
GetProcAddress(hmHostFxrDll, "hostfxr_get_native_search_directories");
|
|
|
|
if (pFnHostFxrSearchDirectories == NULL)
|
|
{
|
|
// Host fxr version is incorrect (need a higher version).
|
|
// TODO log error
|
|
hr = E_FAIL;
|
|
goto Finished;
|
|
}
|
|
|
|
if (FAILED(hr = struNativeSearchPaths.Resize(dwBufferSize)))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
while (TRUE)
|
|
{
|
|
intHostFxrExitCode = pFnHostFxrSearchDirectories(
|
|
m_pConfiguration->QueryHostFxrArgCount(),
|
|
m_pConfiguration->QueryHostFxrArguments(),
|
|
struNativeSearchPaths.QueryStr(),
|
|
dwBufferSize
|
|
);
|
|
|
|
if (intHostFxrExitCode == 0)
|
|
{
|
|
break;
|
|
}
|
|
else if (intHostFxrExitCode == API_BUFFER_TOO_SMALL)
|
|
{
|
|
dwBufferSize *= 2; // smaller buffer. increase the buffer and retry
|
|
if (FAILED(hr = struNativeSearchPaths.Resize(dwBufferSize)))
|
|
{
|
|
goto Finished;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
// Log "Error finding native search directories from aspnetcore application.
|
|
goto Finished;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr = struNativeSearchPaths.SyncWithBuffer()))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
fFound = FALSE;
|
|
|
|
// The native search directories are semicolon delimited.
|
|
// Split on semicolons, append aspnetcorerh.dll, and check if the file exists.
|
|
while ((intIndex = struNativeSearchPaths.IndexOf(L";", intPrevIndex)) != -1)
|
|
{
|
|
if (FAILED(hr = struNativeDllLocation.Copy(struNativeSearchPaths.QueryStr(), intIndex - intPrevIndex)))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
if (!struNativeDllLocation.EndsWith(L"\\"))
|
|
{
|
|
if (FAILED(hr = struNativeDllLocation.Append(L"\\")))
|
|
{
|
|
goto Finished;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr = struNativeDllLocation.Append(g_pwzAspnetcoreRequestHandlerName)))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
if (UTILITY::CheckIfFileExists(struNativeDllLocation.QueryStr()))
|
|
{
|
|
if (FAILED(hr = struFilename->Copy(struNativeDllLocation)))
|
|
{
|
|
goto Finished;
|
|
}
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
intPrevIndex = intIndex + 1;
|
|
}
|
|
|
|
if (!fFound)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Finished;
|
|
}
|
|
|
|
Finished:
|
|
if (FAILED(hr) && hmHostFxrDll != NULL)
|
|
{
|
|
FreeLibrary(hmHostFxrDll);
|
|
}
|
|
return hr;
|
|
}
|