-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Implements shadow copying for ASP.NET Core + IIS #28357
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
45ca6a0
a6ba6cc
2143c8f
6bc26c3
6ac2bf4
88a794a
9ed16c0
90ac490
67194e3
75aa559
9c418b7
14485ea
4715c93
254ab40
dca5ec4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ | |
#include "file_utility.h" | ||
|
||
extern HINSTANCE g_hServerModule; | ||
extern BOOL g_fInAppOfflineShutdown; | ||
|
||
HRESULT | ||
APPLICATION_INFO::CreateHandler( | ||
|
@@ -49,7 +50,6 @@ APPLICATION_INFO::CreateHandler( | |
while (hr != S_OK) | ||
{ | ||
// At this point application is either null or shutdown and is returning S_FALSE | ||
|
||
if (m_pApplication != nullptr) | ||
{ | ||
LOG_INFO(L"Application went offline"); | ||
|
@@ -80,11 +80,25 @@ APPLICATION_INFO::CreateApplication(IHttpContext& pHttpContext) | |
|
||
return S_OK; | ||
} | ||
|
||
try | ||
{ | ||
const WebConfigConfigurationSource configurationSource(m_pServer.GetAdminManager(), pHttpApplication); | ||
ShimOptions options(configurationSource); | ||
|
||
if (g_fInAppOfflineShutdown) | ||
{ | ||
m_pApplication = make_application<ServerErrorApplication>( | ||
pHttpApplication, | ||
E_FAIL, | ||
options.QueryDisableStartupPage() /* disableStartupPage */, | ||
"" /* responseContent */, | ||
503i16 /* statusCode */, | ||
0i16 /* subStatusCode */, | ||
"Application Shutting Down"); | ||
return S_OK; | ||
} | ||
|
||
ErrorContext errorContext; | ||
errorContext.statusCode = 500i16; | ||
errorContext.subStatusCode = 0i16; | ||
|
@@ -130,6 +144,7 @@ APPLICATION_INFO::CreateApplication(IHttpContext& pHttpContext) | |
} | ||
catch (...) | ||
{ | ||
OBSERVE_CAUGHT_EXCEPTION(); | ||
EventLog::Error( | ||
ASPNETCORE_CONFIGURATION_LOAD_ERROR, | ||
ASPNETCORE_CONFIGURATION_LOAD_ERROR_MSG, | ||
|
@@ -175,13 +190,17 @@ APPLICATION_INFO::TryCreateApplication(IHttpContext& pHttpContext, const ShimOpt | |
} | ||
} | ||
|
||
RETURN_IF_FAILED(m_handlerResolver.GetApplicationFactory(*pHttpContext.GetApplication(), m_pApplicationFactory, options, error)); | ||
auto shadowCopyPath = HandleShadowCopy(options, pHttpContext); | ||
|
||
RETURN_IF_FAILED(m_handlerResolver.GetApplicationFactory(*pHttpContext.GetApplication(), shadowCopyPath, m_pApplicationFactory, options, error)); | ||
LOG_INFO(L"Creating handler application"); | ||
|
||
IAPPLICATION * newApplication; | ||
std::wstring shadowCopyWstring = shadowCopyPath.wstring(); | ||
RETURN_IF_FAILED(m_pApplicationFactory->Execute( | ||
&m_pServer, | ||
&pHttpContext, | ||
shadowCopyWstring, | ||
&newApplication)); | ||
|
||
m_pApplication.reset(newApplication); | ||
|
@@ -206,19 +225,91 @@ APPLICATION_INFO::TryCreateHandler( | |
return S_OK; | ||
} | ||
} | ||
|
||
return S_FALSE; | ||
} | ||
|
||
VOID | ||
APPLICATION_INFO::ShutDownApplication(const bool fServerInitiated) | ||
{ | ||
IAPPLICATION* app = nullptr; | ||
{ | ||
SRWExclusiveLock lock(m_applicationLock); | ||
if (!m_pApplication) | ||
{ | ||
return; | ||
} | ||
app = m_pApplication.get(); | ||
} | ||
|
||
LOG_INFOF(L"Stopping application '%ls'", QueryApplicationInfoKey().c_str()); | ||
app->Stop(fServerInitiated); | ||
|
||
SRWExclusiveLock lock(m_applicationLock); | ||
|
||
if (m_pApplication) | ||
m_pApplication = nullptr; | ||
m_pApplicationFactory = nullptr; | ||
} | ||
|
||
std::filesystem::path | ||
APPLICATION_INFO::HandleShadowCopy(const ShimOptions& options, IHttpContext& pHttpContext) | ||
BrennanConroy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
std::filesystem::path shadowCopyPath; | ||
|
||
// Only support shadow copying for IIS. | ||
if (options.QueryShadowCopyEnabled() && !m_pServer.IsCommandLineLaunch()) | ||
{ | ||
LOG_INFOF(L"Stopping application '%ls'", QueryApplicationInfoKey().c_str()); | ||
m_pApplication->Stop(fServerInitiated); | ||
m_pApplication = nullptr; | ||
m_pApplicationFactory = nullptr; | ||
shadowCopyPath = options.QueryShadowCopyDirectory(); | ||
std::wstring physicalPath = pHttpContext.GetApplication()->GetApplicationPhysicalPath(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: |
||
|
||
// Make shadow copy path absolute. | ||
if (!shadowCopyPath.is_absolute()) | ||
{ | ||
shadowCopyPath = std::filesystem::absolute(std::filesystem::path(physicalPath) / shadowCopyPath); | ||
BrennanConroy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// The shadow copy directory itself isn't copied to directly. | ||
// Instead subdirectories with numerically increasing names are created. | ||
// This is because on shutdown, the app itself will still have all dlls loaded, | ||
// meaning we can't copy to the same subdirectory. Therefore, on shutdown, | ||
// we create a directory that is one larger than the previous largest directory number. | ||
jkotalik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
auto directoryName = 0; | ||
std::string directoryNameStr = "0"; | ||
auto shadowCopyBaseDirectory = std::filesystem::directory_entry(shadowCopyPath); | ||
if (!shadowCopyBaseDirectory.exists()) | ||
{ | ||
CreateDirectory(shadowCopyBaseDirectory.path().wstring().c_str(), NULL); | ||
} | ||
|
||
for (auto& entry : std::filesystem::directory_iterator(shadowCopyPath)) | ||
{ | ||
if (entry.is_directory()) | ||
{ | ||
try | ||
{ | ||
auto tempDirName = entry.path().filename().string(); | ||
int intFileName = std::stoi(tempDirName); | ||
if (intFileName > directoryName) | ||
{ | ||
directoryName = intFileName; | ||
directoryNameStr = tempDirName; | ||
} | ||
} | ||
catch (...) | ||
{ | ||
OBSERVE_CAUGHT_EXCEPTION(); | ||
// Ignore any folders that can't be converted to an int. | ||
} | ||
} | ||
} | ||
|
||
shadowCopyPath = shadowCopyPath / directoryNameStr; | ||
HRESULT hr = Environment::CopyToDirectory(physicalPath, shadowCopyPath, options.QueryCleanShadowCopyDirectory(), std::filesystem::canonical(shadowCopyBaseDirectory.path())); | ||
if (hr != S_OK) | ||
{ | ||
return std::wstring(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this the behavior we want? Just fallback to non-shadow copying? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I think so, the file watcher wouldn't look for dll changes if the directory is empty. |
||
} | ||
} | ||
|
||
return shadowCopyPath; | ||
} |
Uh oh!
There was an error while loading. Please reload this page.