Panwang/inproc (#174)
refactoring the code to support inprocess (step 1) with app_offline and Graceful shutdown support
This commit is contained in:
parent
ea7bc30dd3
commit
746f578c3c
|
|
@ -154,7 +154,6 @@
|
||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="Inc\aspnetcoreapplication.h" />
|
|
||||||
<ClInclude Include="Inc\resource.h" />
|
<ClInclude Include="Inc\resource.h" />
|
||||||
<ClInclude Include="Inc\application.h" />
|
<ClInclude Include="Inc\application.h" />
|
||||||
<ClInclude Include="Inc\applicationmanager.h" />
|
<ClInclude Include="Inc\applicationmanager.h" />
|
||||||
|
|
@ -174,10 +173,13 @@
|
||||||
<ClInclude Include="Inc\websockethandler.h" />
|
<ClInclude Include="Inc\websockethandler.h" />
|
||||||
<ClInclude Include="Inc\winhttphelper.h" />
|
<ClInclude Include="Inc\winhttphelper.h" />
|
||||||
<ClInclude Include="Inc\fx_ver.h" />
|
<ClInclude Include="Inc\fx_ver.h" />
|
||||||
|
<ClInclude Include="Inc\inprocessapplication.h" />
|
||||||
|
<ClInclude Include="Inc\outprocessapplication.h" />
|
||||||
<ClInclude Include="Src\precomp.hxx" />
|
<ClInclude Include="Src\precomp.hxx" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="Src\aspnetcoreapplication.cxx" />
|
<ClCompile Include="Src\inprocessapplication.cxx" />
|
||||||
|
<ClCompile Include="Src\outprocessapplication.cxx" />
|
||||||
<ClCompile Include="Src\application.cxx" />
|
<ClCompile Include="Src\application.cxx" />
|
||||||
<ClCompile Include="Src\applicationmanager.cxx" />
|
<ClCompile Include="Src\applicationmanager.cxx" />
|
||||||
<ClCompile Include="Src\aspnetcoreconfig.cxx" />
|
<ClCompile Include="Src\aspnetcoreconfig.cxx" />
|
||||||
|
|
|
||||||
|
|
@ -146,9 +146,9 @@ class APPLICATION
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
APPLICATION() : m_pProcessManager(NULL), m_pApplicationManager(NULL), m_cRefs(1),
|
APPLICATION() : m_pApplicationManager(NULL), m_cRefs(1),
|
||||||
m_fAppOfflineFound(FALSE), m_pAppOfflineHtm(NULL), m_pFileWatcherEntry(NULL),
|
m_fAppOfflineFound(FALSE), m_pAppOfflineHtm(NULL),
|
||||||
m_pAspNetCoreApplication(NULL)
|
m_pFileWatcherEntry(NULL), m_pConfiguration(NULL)
|
||||||
{
|
{
|
||||||
InitializeSRWLock(&m_srwLock);
|
InitializeSRWLock(&m_srwLock);
|
||||||
}
|
}
|
||||||
|
|
@ -159,92 +159,14 @@ public:
|
||||||
return &m_applicationKey;
|
return &m_applicationKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID
|
virtual
|
||||||
SetAppOfflineFound(
|
~APPLICATION();
|
||||||
BOOL found
|
|
||||||
)
|
|
||||||
{
|
|
||||||
m_fAppOfflineFound = found;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL
|
|
||||||
AppOfflineFound()
|
|
||||||
{
|
|
||||||
return m_fAppOfflineFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
virtual
|
||||||
HRESULT
|
HRESULT
|
||||||
GetProcess(
|
Initialize(
|
||||||
_In_ IHttpContext *context,
|
_In_ APPLICATION_MANAGER *pApplicationManager,
|
||||||
_In_ ASPNETCORE_CONFIG *pConfig,
|
_In_ ASPNETCORE_CONFIG *pConfiguration) = 0;
|
||||||
_Out_ SERVER_PROCESS **ppServerProcess
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return m_pProcessManager->GetProcess( context, pConfig, ppServerProcess );
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT
|
|
||||||
GetAspNetCoreApplication(
|
|
||||||
_In_ ASPNETCORE_CONFIG *pConfig,
|
|
||||||
_In_ IHttpContext *context,
|
|
||||||
_Out_ ASPNETCORE_APPLICATION **ppAspNetCoreApplication
|
|
||||||
)
|
|
||||||
{
|
|
||||||
HRESULT hr = S_OK;
|
|
||||||
BOOL fLockTaken = FALSE;
|
|
||||||
ASPNETCORE_APPLICATION *application;
|
|
||||||
IHttpApplication *pHttpApplication = context->GetApplication();
|
|
||||||
|
|
||||||
if (m_pAspNetCoreApplication == NULL)
|
|
||||||
{
|
|
||||||
AcquireSRWLockExclusive(&m_srwLock);
|
|
||||||
fLockTaken = TRUE;
|
|
||||||
|
|
||||||
if (m_pAspNetCoreApplication == NULL)
|
|
||||||
{
|
|
||||||
application = new ASPNETCORE_APPLICATION();
|
|
||||||
if (application == NULL) {
|
|
||||||
hr = E_OUTOFMEMORY;
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = application->Initialize(pConfig);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assign after initialization
|
|
||||||
m_pAspNetCoreApplication = application;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (pHttpApplication->GetModuleContextContainer()->GetModuleContext(g_pModuleId) == NULL)
|
|
||||||
{
|
|
||||||
// This means that we are trying to load a second application
|
|
||||||
// TODO set a flag saying that the whole app pool is invalid '
|
|
||||||
// (including the running application) and return 500 every request.
|
|
||||||
hr = E_FAIL;
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
*ppAspNetCoreApplication = m_pAspNetCoreApplication;
|
|
||||||
|
|
||||||
Finished:
|
|
||||||
if (fLockTaken)
|
|
||||||
{
|
|
||||||
ReleaseSRWLockExclusive(&m_srwLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT
|
|
||||||
Recycle()
|
|
||||||
{
|
|
||||||
HRESULT hr = S_OK;
|
|
||||||
m_pProcessManager->ShutdownAllProcesses();
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
ReferenceApplication() const
|
ReferenceApplication() const
|
||||||
|
|
@ -266,14 +188,15 @@ public:
|
||||||
return m_pAppOfflineHtm;
|
return m_pAppOfflineHtm;
|
||||||
}
|
}
|
||||||
|
|
||||||
~APPLICATION();
|
BOOL
|
||||||
|
AppOfflineFound()
|
||||||
|
{
|
||||||
|
return m_fAppOfflineFound;
|
||||||
|
}
|
||||||
|
|
||||||
HRESULT
|
virtual
|
||||||
Initialize(
|
VOID
|
||||||
_In_ APPLICATION_MANAGER *pApplicationManager,
|
OnAppOfflineHandleChange() = 0;
|
||||||
_In_ LPCWSTR pszApplication,
|
|
||||||
_In_ LPCWSTR pszPhysicalPath
|
|
||||||
);
|
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
UpdateAppOfflineFileHandle();
|
UpdateAppOfflineFileHandle();
|
||||||
|
|
@ -281,18 +204,29 @@ public:
|
||||||
HRESULT
|
HRESULT
|
||||||
StartMonitoringAppOffline();
|
StartMonitoringAppOffline();
|
||||||
|
|
||||||
private:
|
ASPNETCORE_CONFIG*
|
||||||
|
QueryConfig()
|
||||||
|
{
|
||||||
|
return m_pConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual
|
||||||
|
REQUEST_NOTIFICATION_STATUS
|
||||||
|
ExecuteRequest(
|
||||||
|
_In_ IHttpContext* pHttpContext
|
||||||
|
) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
STRU m_strAppPhysicalPath;
|
|
||||||
mutable LONG m_cRefs;
|
mutable LONG m_cRefs;
|
||||||
APPLICATION_KEY m_applicationKey;
|
APPLICATION_KEY m_applicationKey;
|
||||||
PROCESS_MANAGER* m_pProcessManager;
|
|
||||||
APPLICATION_MANAGER *m_pApplicationManager;
|
APPLICATION_MANAGER *m_pApplicationManager;
|
||||||
BOOL m_fAppOfflineFound;
|
BOOL m_fAppOfflineFound;
|
||||||
APP_OFFLINE_HTM *m_pAppOfflineHtm;
|
APP_OFFLINE_HTM *m_pAppOfflineHtm;
|
||||||
FILE_WATCHER_ENTRY *m_pFileWatcherEntry;
|
FILE_WATCHER_ENTRY *m_pFileWatcherEntry;
|
||||||
ASPNETCORE_APPLICATION *m_pAspNetCoreApplication;
|
ASPNETCORE_CONFIG *m_pConfiguration;
|
||||||
SRWLOCK m_srwLock;
|
SRWLOCK m_srwLock;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class APPLICATION_HASH :
|
class APPLICATION_HASH :
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ public:
|
||||||
HRESULT
|
HRESULT
|
||||||
GetApplication(
|
GetApplication(
|
||||||
_In_ IHttpContext* pContext,
|
_In_ IHttpContext* pContext,
|
||||||
|
_In_ ASPNETCORE_CONFIG* pConfig,
|
||||||
_Out_ APPLICATION ** ppApplication
|
_Out_ APPLICATION ** ppApplication
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -121,7 +122,9 @@ private:
|
||||||
//
|
//
|
||||||
// we currently limit the size of m_pstrErrorInfo to 5000, be careful if you want to change its payload
|
// we currently limit the size of m_pstrErrorInfo to 5000, be careful if you want to change its payload
|
||||||
//
|
//
|
||||||
APPLICATION_MANAGER() : m_pApplicationHash(NULL), m_pFileWatcher(NULL), m_pHttp502ErrorPage(NULL), m_pstrErrorInfo(
|
APPLICATION_MANAGER() : m_pApplicationHash(NULL), m_pFileWatcher(NULL),
|
||||||
|
m_pHttp502ErrorPage(NULL), m_hostingModel(HOSTING_UNKNOWN),
|
||||||
|
m_pstrErrorInfo(
|
||||||
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"> \
|
"<!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\"> \
|
<html xmlns=\"http://www.w3.org/1999/xhtml\"> \
|
||||||
<head> \
|
<head> \
|
||||||
|
|
@ -155,4 +158,5 @@ private:
|
||||||
SRWLOCK m_srwLock;
|
SRWLOCK m_srwLock;
|
||||||
HTTP_DATA_CHUNK *m_pHttp502ErrorPage;
|
HTTP_DATA_CHUNK *m_pHttp502ErrorPage;
|
||||||
LPSTR m_pstrErrorInfo;
|
LPSTR m_pstrErrorInfo;
|
||||||
|
APP_HOSTING_MODEL m_hostingModel;
|
||||||
};
|
};
|
||||||
|
|
@ -38,6 +38,14 @@
|
||||||
|
|
||||||
extern HTTP_MODULE_ID g_pModuleId;
|
extern HTTP_MODULE_ID g_pModuleId;
|
||||||
extern IHttpServer * g_pHttpServer;
|
extern IHttpServer * g_pHttpServer;
|
||||||
|
extern BOOL g_fRecycleProcessCalled;
|
||||||
|
|
||||||
|
enum APP_HOSTING_MODEL
|
||||||
|
{
|
||||||
|
HOSTING_UNKNOWN = 0,
|
||||||
|
HOSTING_IN_PROCESS,
|
||||||
|
HOSTING_OUT_PROCESS
|
||||||
|
};
|
||||||
|
|
||||||
class ASPNETCORE_CONFIG : IHttpStoredContext
|
class ASPNETCORE_CONFIG : IHttpStoredContext
|
||||||
{
|
{
|
||||||
|
|
@ -123,13 +131,13 @@ public:
|
||||||
return &m_struApplication;
|
return &m_struApplication;
|
||||||
}
|
}
|
||||||
|
|
||||||
STRU*
|
STRU*
|
||||||
QueryApplicationFullPath(
|
QueryApplicationFullPath(
|
||||||
VOID
|
VOID
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return &m_struApplicationFullPath;
|
return &m_struApplicationFullPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
STRU*
|
STRU*
|
||||||
QueryProcessPath(
|
QueryProcessPath(
|
||||||
|
|
@ -139,6 +147,22 @@ public:
|
||||||
return &m_struProcessPath;
|
return &m_struProcessPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
APP_HOSTING_MODEL
|
||||||
|
QueryHostingModel(
|
||||||
|
VOID
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return m_hostingModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
STRU*
|
||||||
|
QueryHostingModelStr(
|
||||||
|
VOID
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return &m_strHostingModel;
|
||||||
|
}
|
||||||
|
|
||||||
BOOL
|
BOOL
|
||||||
QueryStdoutLogEnabled()
|
QueryStdoutLogEnabled()
|
||||||
{
|
{
|
||||||
|
|
@ -175,19 +199,6 @@ public:
|
||||||
return m_fDisableStartUpErrorPage;
|
return m_fDisableStartUpErrorPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL
|
|
||||||
QueryIsInProcess()
|
|
||||||
{
|
|
||||||
return m_fIsInProcess;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL
|
|
||||||
QueryIsOutOfProcess()
|
|
||||||
{
|
|
||||||
return m_fIsOutOfProcess;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
STRU*
|
STRU*
|
||||||
QueryStdoutLogFile()
|
QueryStdoutLogFile()
|
||||||
{
|
{
|
||||||
|
|
@ -201,7 +212,8 @@ private:
|
||||||
//
|
//
|
||||||
ASPNETCORE_CONFIG():
|
ASPNETCORE_CONFIG():
|
||||||
m_fStdoutLogEnabled( FALSE ),
|
m_fStdoutLogEnabled( FALSE ),
|
||||||
m_pEnvironmentVariables( NULL )
|
m_pEnvironmentVariables( NULL ),
|
||||||
|
m_hostingModel( HOSTING_UNKNOWN )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -220,13 +232,13 @@ private:
|
||||||
STRU m_struProcessPath;
|
STRU m_struProcessPath;
|
||||||
STRU m_struStdoutLogFile;
|
STRU m_struStdoutLogFile;
|
||||||
STRU m_struApplicationFullPath;
|
STRU m_struApplicationFullPath;
|
||||||
|
STRU m_strHostingModel;
|
||||||
BOOL m_fStdoutLogEnabled;
|
BOOL m_fStdoutLogEnabled;
|
||||||
BOOL m_fForwardWindowsAuthToken;
|
BOOL m_fForwardWindowsAuthToken;
|
||||||
BOOL m_fDisableStartUpErrorPage;
|
BOOL m_fDisableStartUpErrorPage;
|
||||||
BOOL m_fWindowsAuthEnabled;
|
BOOL m_fWindowsAuthEnabled;
|
||||||
BOOL m_fBasicAuthEnabled;
|
BOOL m_fBasicAuthEnabled;
|
||||||
BOOL m_fAnonymousAuthEnabled;
|
BOOL m_fAnonymousAuthEnabled;
|
||||||
BOOL m_fIsInProcess;
|
APP_HOSTING_MODEL m_hostingModel;
|
||||||
BOOL m_fIsOutOfProcess;
|
|
||||||
ENVIRONMENT_VAR_HASH* m_pEnvironmentVariables;
|
ENVIRONMENT_VAR_HASH* m_pEnvironmentVariables;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,8 @@ class FORWARDING_HANDLER
|
||||||
public:
|
public:
|
||||||
|
|
||||||
FORWARDING_HANDLER(
|
FORWARDING_HANDLER(
|
||||||
__in IHttpContext * pW3Context
|
__in IHttpContext * pW3Context,
|
||||||
|
__in APPLICATION * pApplication
|
||||||
);
|
);
|
||||||
|
|
||||||
static void * operator new(size_t size);
|
static void * operator new(size_t size);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
@ -7,46 +7,34 @@ typedef void(*request_handler_cb) (int error, IHttpContext* pHttpContext, void*
|
||||||
typedef REQUEST_NOTIFICATION_STATUS(*PFN_REQUEST_HANDLER) (IHttpContext* pHttpContext, void* pvRequstHandlerContext);
|
typedef REQUEST_NOTIFICATION_STATUS(*PFN_REQUEST_HANDLER) (IHttpContext* pHttpContext, void* pvRequstHandlerContext);
|
||||||
typedef BOOL(*PFN_SHUTDOWN_HANDLER) (void* pvShutdownHandlerContext);
|
typedef BOOL(*PFN_SHUTDOWN_HANDLER) (void* pvShutdownHandlerContext);
|
||||||
|
|
||||||
class ASPNETCORE_APPLICATION
|
#include "application.h"
|
||||||
|
|
||||||
|
class IN_PROCESS_APPLICATION : public APPLICATION
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
IN_PROCESS_APPLICATION();
|
||||||
|
|
||||||
ASPNETCORE_APPLICATION():
|
~IN_PROCESS_APPLICATION();
|
||||||
m_pConfiguration(NULL),
|
|
||||||
m_RequestHandler(NULL)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~ASPNETCORE_APPLICATION()
|
|
||||||
{
|
|
||||||
if (m_hThread != NULL)
|
|
||||||
{
|
|
||||||
CloseHandle(m_hThread);
|
|
||||||
m_hThread = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_pInitalizeEvent != NULL)
|
|
||||||
{
|
|
||||||
CloseHandle(m_pInitalizeEvent);
|
|
||||||
m_pInitalizeEvent = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
__override
|
||||||
HRESULT
|
HRESULT
|
||||||
Initialize(
|
Initialize(_In_ APPLICATION_MANAGER* pApplicationManager,
|
||||||
_In_ ASPNETCORE_CONFIG* pConfig
|
_In_ ASPNETCORE_CONFIG* pConfiguration);
|
||||||
|
|
||||||
|
VOID
|
||||||
|
Recycle(
|
||||||
|
VOID
|
||||||
);
|
);
|
||||||
|
|
||||||
|
__override
|
||||||
|
VOID OnAppOfflineHandleChange();
|
||||||
|
|
||||||
|
__override
|
||||||
REQUEST_NOTIFICATION_STATUS
|
REQUEST_NOTIFICATION_STATUS
|
||||||
ExecuteRequest(
|
ExecuteRequest(
|
||||||
_In_ IHttpContext* pHttpContext
|
_In_ IHttpContext* pHttpContext
|
||||||
);
|
);
|
||||||
|
|
||||||
VOID
|
|
||||||
Shutdown(
|
|
||||||
VOID
|
|
||||||
);
|
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
SetCallbackHandles(
|
SetCallbackHandles(
|
||||||
_In_ PFN_REQUEST_HANDLER request_callback,
|
_In_ PFN_REQUEST_HANDLER request_callback,
|
||||||
|
|
@ -61,16 +49,13 @@ public:
|
||||||
VOID
|
VOID
|
||||||
);
|
);
|
||||||
|
|
||||||
ASPNETCORE_CONFIG*
|
HRESULT
|
||||||
GetConfig(
|
LoadManagedApplication(
|
||||||
VOID
|
VOID
|
||||||
)
|
);
|
||||||
{
|
|
||||||
return m_pConfiguration;
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
static
|
||||||
ASPNETCORE_APPLICATION*
|
IN_PROCESS_APPLICATION*
|
||||||
GetInstance(
|
GetInstance(
|
||||||
VOID
|
VOID
|
||||||
)
|
)
|
||||||
|
|
@ -78,13 +63,11 @@ public:
|
||||||
return s_Application;
|
return s_Application;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Thread executing the .NET Core process
|
// Thread executing the .NET Core process
|
||||||
HANDLE m_hThread;
|
HANDLE m_hThread;
|
||||||
|
|
||||||
// Configuration for this application
|
|
||||||
ASPNETCORE_CONFIG* m_pConfiguration;
|
|
||||||
|
|
||||||
// The request handler callback from managed code
|
// The request handler callback from managed code
|
||||||
PFN_REQUEST_HANDLER m_RequestHandler;
|
PFN_REQUEST_HANDLER m_RequestHandler;
|
||||||
VOID* m_RequstHandlerContext;
|
VOID* m_RequstHandlerContext;
|
||||||
|
|
@ -99,29 +82,41 @@ private:
|
||||||
// The exit code of the .NET Core process
|
// The exit code of the .NET Core process
|
||||||
INT m_ProcessExitCode;
|
INT m_ProcessExitCode;
|
||||||
|
|
||||||
static ASPNETCORE_APPLICATION* s_Application;
|
BOOL m_fManagedAppLoaded;
|
||||||
|
BOOL m_fLoadManagedAppError;
|
||||||
|
|
||||||
static VOID
|
static IN_PROCESS_APPLICATION* s_Application;
|
||||||
FindDotNetFolders(
|
|
||||||
_In_ STRU *pstrPath,
|
|
||||||
_Out_ std::vector<std::wstring> *pvFolders
|
|
||||||
);
|
|
||||||
|
|
||||||
static HRESULT
|
static
|
||||||
FindHighestDotNetVersion(
|
VOID
|
||||||
_In_ std::vector<std::wstring> vFolders,
|
FindDotNetFolders(
|
||||||
_Out_ STRU *pstrResult
|
_In_ PCWSTR pszPath,
|
||||||
);
|
_Out_ std::vector<std::wstring> *pvFolders
|
||||||
|
);
|
||||||
|
|
||||||
|
static
|
||||||
|
HRESULT
|
||||||
|
FindHighestDotNetVersion(
|
||||||
|
_In_ std::vector<std::wstring> vFolders,
|
||||||
|
_Out_ STRU *pstrResult
|
||||||
|
);
|
||||||
|
|
||||||
|
static
|
||||||
|
BOOL
|
||||||
|
DirectoryExists(
|
||||||
|
_In_ STRU *pstrPath //todo: this does not need to be stru, can be PCWSTR
|
||||||
|
);
|
||||||
|
|
||||||
static BOOL
|
static BOOL
|
||||||
DirectoryExists(
|
GetEnv(
|
||||||
_In_ STRU *pstrPath
|
_In_ PCWSTR pszEnvironmentVariable,
|
||||||
);
|
_Out_ STRU *pstrResult
|
||||||
|
);
|
||||||
|
|
||||||
static BOOL
|
static
|
||||||
GetEnv(
|
VOID
|
||||||
_In_ PCWSTR pszEnvironmentVariable,
|
ExecuteAspNetCoreProcess(
|
||||||
_Out_ STRU *pstrResult
|
_In_ LPVOID pContext
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
// 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 "application.h"
|
||||||
|
|
||||||
|
class OUT_OF_PROCESS_APPLICATION : public APPLICATION
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OUT_OF_PROCESS_APPLICATION();
|
||||||
|
|
||||||
|
~OUT_OF_PROCESS_APPLICATION();
|
||||||
|
|
||||||
|
__override
|
||||||
|
HRESULT Initialize(_In_ APPLICATION_MANAGER* pApplicationManager,
|
||||||
|
_In_ ASPNETCORE_CONFIG* pConfiguration);
|
||||||
|
|
||||||
|
__override
|
||||||
|
VOID OnAppOfflineHandleChange();
|
||||||
|
|
||||||
|
__override
|
||||||
|
REQUEST_NOTIFICATION_STATUS
|
||||||
|
ExecuteRequest(
|
||||||
|
_In_ IHttpContext* pHttpContext
|
||||||
|
);
|
||||||
|
|
||||||
|
HRESULT
|
||||||
|
GetProcess(
|
||||||
|
_In_ IHttpContext *context,
|
||||||
|
_Out_ SERVER_PROCESS **ppServerProcess
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return m_pProcessManager->GetProcess(context, m_pConfiguration, ppServerProcess);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
PROCESS_MANAGER* m_pProcessManager;
|
||||||
|
};
|
||||||
|
|
@ -17,4 +17,7 @@
|
||||||
#define ASPNETCORE_EVENT_INVALID_STDOUT_LOG_FILE_MSG L"Warning: Could not create stdoutLogFile %s, ErrorCode = %d."
|
#define ASPNETCORE_EVENT_INVALID_STDOUT_LOG_FILE_MSG L"Warning: Could not create stdoutLogFile %s, ErrorCode = %d."
|
||||||
#define ASPNETCORE_EVENT_GRACEFUL_SHUTDOWN_FAILURE_MSG L"Failed to gracefully shutdown process '%d'."
|
#define ASPNETCORE_EVENT_GRACEFUL_SHUTDOWN_FAILURE_MSG L"Failed to gracefully shutdown process '%d'."
|
||||||
#define ASPNETCORE_EVENT_SENT_SHUTDOWN_HTTP_REQUEST_MSG L"Sent shutdown HTTP message to process '%d' and received http status '%d'."
|
#define ASPNETCORE_EVENT_SENT_SHUTDOWN_HTTP_REQUEST_MSG L"Sent shutdown HTTP message to process '%d' and received http status '%d'."
|
||||||
|
#define ASPNETCORE_EVENT_LOAD_CLR_FALIURE_MSG L"Application '%s' with physical root '%s' failed to load clr and managed application, ErrorCode = '0x%x."
|
||||||
|
#define ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP_MSG L"Only one inprocess application is allowed per IIS application pool. Please assign the application '%s' to a different IIS application pool."
|
||||||
|
#define ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG L"Mixed hosting model is not supported. Application '%s' configured with different hostingModel value '%s' other than the one of running application(s)."
|
||||||
|
#define ASPNETCORE_EVENT_ADD_APPLICATION_ERROR_MSG L"Failed to start application '%s', ErrorCode '0x%x'."
|
||||||
|
|
@ -20,92 +20,16 @@ APPLICATION::~APPLICATION()
|
||||||
m_pFileWatcherEntry->StopMonitor();
|
m_pFileWatcherEntry->StopMonitor();
|
||||||
m_pFileWatcherEntry = NULL;
|
m_pFileWatcherEntry = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_pProcessManager != NULL)
|
|
||||||
{
|
|
||||||
m_pProcessManager->ShutdownAllProcesses();
|
|
||||||
m_pProcessManager->DereferenceProcessManager();
|
|
||||||
m_pProcessManager = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT
|
|
||||||
APPLICATION::Initialize(
|
|
||||||
_In_ APPLICATION_MANAGER* pApplicationManager,
|
|
||||||
_In_ LPCWSTR pszApplication,
|
|
||||||
_In_ LPCWSTR pszPhysicalPath
|
|
||||||
)
|
|
||||||
{
|
|
||||||
HRESULT hr = S_OK;
|
|
||||||
|
|
||||||
DBG_ASSERT(pszPhysicalPath != NULL);
|
|
||||||
DBG_ASSERT(pApplicationManager != NULL);
|
|
||||||
DBG_ASSERT(pszPhysicalPath != NULL);
|
|
||||||
m_strAppPhysicalPath.Copy(pszPhysicalPath);
|
|
||||||
|
|
||||||
m_pApplicationManager = pApplicationManager;
|
|
||||||
|
|
||||||
hr = m_applicationKey.Initialize(pszApplication);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_pProcessManager == NULL)
|
|
||||||
{
|
|
||||||
m_pProcessManager = new PROCESS_MANAGER;
|
|
||||||
if (m_pProcessManager == NULL)
|
|
||||||
{
|
|
||||||
hr = E_OUTOFMEMORY;
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = m_pProcessManager->Initialize();
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_pFileWatcherEntry == NULL)
|
|
||||||
{
|
|
||||||
m_pFileWatcherEntry = new FILE_WATCHER_ENTRY(pApplicationManager->GetFileWatcher());
|
|
||||||
if (m_pFileWatcherEntry == NULL)
|
|
||||||
{
|
|
||||||
hr = E_OUTOFMEMORY;
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateAppOfflineFileHandle();
|
|
||||||
|
|
||||||
Finished:
|
|
||||||
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
if (m_pFileWatcherEntry != NULL)
|
|
||||||
{
|
|
||||||
m_pFileWatcherEntry->DereferenceFileWatcherEntry();
|
|
||||||
m_pFileWatcherEntry = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_pProcessManager != NULL)
|
|
||||||
{
|
|
||||||
m_pProcessManager->DereferenceProcessManager();
|
|
||||||
m_pProcessManager = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
APPLICATION::StartMonitoringAppOffline()
|
APPLICATION::StartMonitoringAppOffline()
|
||||||
{
|
{
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
|
if (m_pFileWatcherEntry != NULL)
|
||||||
hr = m_pFileWatcherEntry->Create(m_strAppPhysicalPath.QueryStr(), L"app_offline.htm", this, NULL);
|
{
|
||||||
|
hr = m_pFileWatcherEntry->Create(m_pConfiguration->QueryApplicationFullPath()->QueryStr(), L"app_offline.htm", this, NULL);
|
||||||
|
}
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,7 +37,7 @@ VOID
|
||||||
APPLICATION::UpdateAppOfflineFileHandle()
|
APPLICATION::UpdateAppOfflineFileHandle()
|
||||||
{
|
{
|
||||||
STRU strFilePath;
|
STRU strFilePath;
|
||||||
PATH::ConvertPathToFullPath(L".\\app_offline.htm", m_strAppPhysicalPath.QueryStr(), &strFilePath);
|
PATH::ConvertPathToFullPath(L".\\app_offline.htm", m_pConfiguration->QueryApplicationFullPath()->QueryStr(), &strFilePath);
|
||||||
APP_OFFLINE_HTM *pOldAppOfflineHtm = NULL;
|
APP_OFFLINE_HTM *pOldAppOfflineHtm = NULL;
|
||||||
APP_OFFLINE_HTM *pNewAppOfflineHtm = NULL;
|
APP_OFFLINE_HTM *pNewAppOfflineHtm = NULL;
|
||||||
|
|
||||||
|
|
@ -124,18 +48,6 @@ APPLICATION::UpdateAppOfflineFileHandle()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_fAppOfflineFound = TRUE;
|
m_fAppOfflineFound = TRUE;
|
||||||
|
|
||||||
//
|
|
||||||
// send shutdown signal
|
|
||||||
//
|
|
||||||
|
|
||||||
// The reason why we send the shutdown signal before loading the new app_offline file is because we want to make some delay
|
|
||||||
// before reading the appoffline.htm so that the file change can be done on time.
|
|
||||||
if (m_pProcessManager != NULL)
|
|
||||||
{
|
|
||||||
m_pProcessManager->SendShutdownSignal();
|
|
||||||
}
|
|
||||||
|
|
||||||
pNewAppOfflineHtm = new APP_OFFLINE_HTM(strFilePath.QueryStr());
|
pNewAppOfflineHtm = new APP_OFFLINE_HTM(strFilePath.QueryStr());
|
||||||
|
|
||||||
if ( pNewAppOfflineHtm != NULL )
|
if ( pNewAppOfflineHtm != NULL )
|
||||||
|
|
@ -160,5 +72,7 @@ APPLICATION::UpdateAppOfflineFileHandle()
|
||||||
pNewAppOfflineHtm = NULL;
|
pNewAppOfflineHtm = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OnAppOfflineHandleChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ APPLICATION_MANAGER* APPLICATION_MANAGER::sm_pApplicationManager = NULL;
|
||||||
HRESULT
|
HRESULT
|
||||||
APPLICATION_MANAGER::GetApplication(
|
APPLICATION_MANAGER::GetApplication(
|
||||||
_In_ IHttpContext* pContext,
|
_In_ IHttpContext* pContext,
|
||||||
|
_In_ ASPNETCORE_CONFIG* pConfig,
|
||||||
_Out_ APPLICATION ** ppApplication
|
_Out_ APPLICATION ** ppApplication
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
@ -15,12 +16,17 @@ APPLICATION_MANAGER::GetApplication(
|
||||||
APPLICATION *pApplication = NULL;
|
APPLICATION *pApplication = NULL;
|
||||||
APPLICATION_KEY key;
|
APPLICATION_KEY key;
|
||||||
BOOL fExclusiveLock = FALSE;
|
BOOL fExclusiveLock = FALSE;
|
||||||
|
BOOL fMixedHostingModelError = FALSE;
|
||||||
|
BOOL fDuplicatedInProcessApp = FALSE;
|
||||||
PCWSTR pszApplicationId = NULL;
|
PCWSTR pszApplicationId = NULL;
|
||||||
|
LPCWSTR apsz[1];
|
||||||
|
STACK_STRU ( strEventMsg, 256 );
|
||||||
|
|
||||||
*ppApplication = NULL;
|
*ppApplication = NULL;
|
||||||
|
|
||||||
DBG_ASSERT(pContext != NULL);
|
DBG_ASSERT(pContext != NULL);
|
||||||
DBG_ASSERT(pContext->GetApplication() != NULL);
|
DBG_ASSERT(pContext->GetApplication() != NULL);
|
||||||
|
|
||||||
pszApplicationId = pContext->GetApplication()->GetApplicationId();
|
pszApplicationId = pContext->GetApplication()->GetApplicationId();
|
||||||
|
|
||||||
hr = key.Initialize(pszApplicationId);
|
hr = key.Initialize(pszApplicationId);
|
||||||
|
|
@ -33,8 +39,28 @@ APPLICATION_MANAGER::GetApplication(
|
||||||
|
|
||||||
if (*ppApplication == NULL)
|
if (*ppApplication == NULL)
|
||||||
{
|
{
|
||||||
|
switch (pConfig->QueryHostingModel())
|
||||||
|
{
|
||||||
|
case HOSTING_IN_PROCESS:
|
||||||
|
if (m_pApplicationHash->Count() > 0)
|
||||||
|
{
|
||||||
|
// Only one inprocess app is allowed per IIS worker process
|
||||||
|
fDuplicatedInProcessApp = TRUE;
|
||||||
|
hr = HRESULT_FROM_WIN32(ERROR_APP_INIT_FAILURE);
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
pApplication = new IN_PROCESS_APPLICATION();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HOSTING_OUT_PROCESS:
|
||||||
|
pApplication = new OUT_OF_PROCESS_APPLICATION();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
hr = E_UNEXPECTED;
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
pApplication = new APPLICATION();
|
|
||||||
if (pApplication == NULL)
|
if (pApplication == NULL)
|
||||||
{
|
{
|
||||||
hr = E_OUTOFMEMORY;
|
hr = E_OUTOFMEMORY;
|
||||||
|
|
@ -53,18 +79,39 @@ APPLICATION_MANAGER::GetApplication(
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = pApplication->Initialize(this, pszApplicationId, pContext->GetApplication()->GetApplicationPhysicalPath());
|
// hosting model check. We do not allow mixed scenario for now
|
||||||
|
// could be changed in the future
|
||||||
|
if (m_hostingModel != HOSTING_UNKNOWN)
|
||||||
|
{
|
||||||
|
if (m_hostingModel != pConfig->QueryHostingModel())
|
||||||
|
{
|
||||||
|
// hosting model does not match, error out
|
||||||
|
fMixedHostingModelError = TRUE;
|
||||||
|
hr = HRESULT_FROM_WIN32(ERROR_APP_INIT_FAILURE);
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = pApplication->Initialize(this, pConfig);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = m_pApplicationHash->InsertRecord( pApplication );
|
hr = m_pApplicationHash->InsertRecord( pApplication );
|
||||||
|
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// first application will decide which hosting model allowed by this process
|
||||||
|
//
|
||||||
|
if (m_hostingModel == HOSTING_UNKNOWN)
|
||||||
|
{
|
||||||
|
m_hostingModel = pConfig->QueryHostingModel();
|
||||||
|
}
|
||||||
|
|
||||||
ReleaseSRWLockExclusive(&m_srwLock);
|
ReleaseSRWLockExclusive(&m_srwLock);
|
||||||
fExclusiveLock = FALSE;
|
fExclusiveLock = FALSE;
|
||||||
|
|
||||||
|
|
@ -76,8 +123,10 @@ APPLICATION_MANAGER::GetApplication(
|
||||||
|
|
||||||
Finished:
|
Finished:
|
||||||
|
|
||||||
if (fExclusiveLock == TRUE)
|
if (fExclusiveLock)
|
||||||
|
{
|
||||||
ReleaseSRWLockExclusive(&m_srwLock);
|
ReleaseSRWLockExclusive(&m_srwLock);
|
||||||
|
}
|
||||||
|
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
|
|
@ -86,12 +135,77 @@ Finished:
|
||||||
pApplication->DereferenceApplication();
|
pApplication->DereferenceApplication();
|
||||||
pApplication = NULL;
|
pApplication = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fDuplicatedInProcessApp)
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(strEventMsg.SafeSnwprintf(
|
||||||
|
ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP_MSG,
|
||||||
|
pszApplicationId)))
|
||||||
|
{
|
||||||
|
apsz[0] = strEventMsg.QueryStr();
|
||||||
|
if (FORWARDING_HANDLER::QueryEventLog() != NULL)
|
||||||
|
{
|
||||||
|
ReportEventW(FORWARDING_HANDLER::QueryEventLog(),
|
||||||
|
EVENTLOG_ERROR_TYPE,
|
||||||
|
0,
|
||||||
|
ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP,
|
||||||
|
NULL,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
apsz,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (fMixedHostingModelError)
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(strEventMsg.SafeSnwprintf(
|
||||||
|
ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG,
|
||||||
|
pszApplicationId,
|
||||||
|
pConfig->QueryHostingModelStr())))
|
||||||
|
{
|
||||||
|
apsz[0] = strEventMsg.QueryStr();
|
||||||
|
if (FORWARDING_HANDLER::QueryEventLog() != NULL)
|
||||||
|
{
|
||||||
|
ReportEventW(FORWARDING_HANDLER::QueryEventLog(),
|
||||||
|
EVENTLOG_ERROR_TYPE,
|
||||||
|
0,
|
||||||
|
ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR,
|
||||||
|
NULL,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
apsz,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(strEventMsg.SafeSnwprintf(
|
||||||
|
ASPNETCORE_EVENT_ADD_APPLICATION_ERROR_MSG,
|
||||||
|
pszApplicationId,
|
||||||
|
hr)))
|
||||||
|
{
|
||||||
|
apsz[0] = strEventMsg.QueryStr();
|
||||||
|
if (FORWARDING_HANDLER::QueryEventLog() != NULL)
|
||||||
|
{
|
||||||
|
ReportEventW(FORWARDING_HANDLER::QueryEventLog(),
|
||||||
|
EVENTLOG_ERROR_TYPE,
|
||||||
|
0,
|
||||||
|
ASPNETCORE_EVENT_ADD_APPLICATION_ERROR,
|
||||||
|
NULL,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
apsz,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
APPLICATION_MANAGER::RecycleApplication(
|
APPLICATION_MANAGER::RecycleApplication(
|
||||||
_In_ LPCWSTR pszApplication
|
_In_ LPCWSTR pszApplication
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
;/*++
|
;/*++
|
||||||
;
|
;
|
||||||
;Copyright (c) 2014 Microsoft Corporation
|
; Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
; Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
;
|
;
|
||||||
;Module Name:
|
;Module Name:
|
||||||
;
|
;
|
||||||
|
|
@ -67,6 +68,30 @@ Language=English
|
||||||
%1
|
%1
|
||||||
.
|
.
|
||||||
|
|
||||||
|
Messageid=1007
|
||||||
|
SymbolicName=ASPNETCORE_EVENT_LOAD_CLR_FALIURE
|
||||||
|
Language=English
|
||||||
|
%1
|
||||||
|
.
|
||||||
|
|
||||||
|
Messageid=1008
|
||||||
|
SymbolicName=ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP
|
||||||
|
Language=English
|
||||||
|
%1
|
||||||
|
.
|
||||||
|
|
||||||
|
Messageid=1009
|
||||||
|
SymbolicName=ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR
|
||||||
|
Language=English
|
||||||
|
%1
|
||||||
|
.
|
||||||
|
|
||||||
|
Messageid=1010
|
||||||
|
SymbolicName=ASPNETCORE_EVENT_ADD_APPLICATION_ERROR
|
||||||
|
Language=English
|
||||||
|
%1
|
||||||
|
.
|
||||||
|
|
||||||
;
|
;
|
||||||
;#endif // _ASPNETCORE_MODULE_MSG_H_
|
;#endif // _ASPNETCORE_MODULE_MSG_H_
|
||||||
;
|
;
|
||||||
|
|
@ -5,24 +5,37 @@
|
||||||
|
|
||||||
ASPNETCORE_CONFIG::~ASPNETCORE_CONFIG()
|
ASPNETCORE_CONFIG::~ASPNETCORE_CONFIG()
|
||||||
{
|
{
|
||||||
//
|
if (QueryHostingModel() == HOSTING_IN_PROCESS &&
|
||||||
// the destructor will be called once IIS decides to recycle the module context (i.e., application)
|
!g_fRecycleProcessCalled &&
|
||||||
//
|
!g_pHttpServer->IsCommandLineLaunch())
|
||||||
// shutting down core application first
|
|
||||||
if (ASPNETCORE_APPLICATION::GetInstance() != NULL) {
|
|
||||||
ASPNETCORE_APPLICATION::GetInstance()->Shutdown();
|
|
||||||
}
|
|
||||||
m_struApplicationFullPath.Reset();
|
|
||||||
if (!m_struApplication.IsEmpty())
|
|
||||||
{
|
{
|
||||||
APPLICATION_MANAGER::GetInstance()->RecycleApplication(m_struApplication.QueryStr());
|
// RecycleProcess can olny be called once
|
||||||
|
// In case of configuration change for in-process app
|
||||||
|
// We want notify IIS first to let new request routed to new worker process
|
||||||
|
g_fRecycleProcessCalled = TRUE;
|
||||||
|
g_pHttpServer->RecycleProcess(L"AspNetCore Recycle Process on Configuration Change");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_struApplicationFullPath.Reset();
|
||||||
if (m_pEnvironmentVariables != NULL)
|
if (m_pEnvironmentVariables != NULL)
|
||||||
{
|
{
|
||||||
m_pEnvironmentVariables->Clear();
|
m_pEnvironmentVariables->Clear();
|
||||||
delete m_pEnvironmentVariables;
|
delete m_pEnvironmentVariables;
|
||||||
m_pEnvironmentVariables = NULL;
|
m_pEnvironmentVariables = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!m_struApplication.IsEmpty())
|
||||||
|
{
|
||||||
|
APPLICATION_MANAGER::GetInstance()->RecycleApplication(m_struApplication.QueryStr());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (QueryHostingModel() == HOSTING_IN_PROCESS &&
|
||||||
|
g_pHttpServer->IsCommandLineLaunch())
|
||||||
|
{
|
||||||
|
// IISExpress scenario, only option is to call exit in case configuration change
|
||||||
|
// as CLR or application may change
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
|
|
@ -124,7 +137,6 @@ ASPNETCORE_CONFIG::Populate(
|
||||||
STRU strEnvValue;
|
STRU strEnvValue;
|
||||||
STRU strExpandedEnvValue;
|
STRU strExpandedEnvValue;
|
||||||
STRU strApplicationFullPath;
|
STRU strApplicationFullPath;
|
||||||
STRU strHostingModel;
|
|
||||||
IAppHostAdminManager *pAdminManager = NULL;
|
IAppHostAdminManager *pAdminManager = NULL;
|
||||||
IAppHostElement *pAspNetCoreElement = NULL;
|
IAppHostElement *pAspNetCoreElement = NULL;
|
||||||
IAppHostElement *pWindowsAuthenticationElement = NULL;
|
IAppHostElement *pWindowsAuthenticationElement = NULL;
|
||||||
|
|
@ -238,19 +250,27 @@ ASPNETCORE_CONFIG::Populate(
|
||||||
|
|
||||||
hr = GetElementStringProperty(pAspNetCoreElement,
|
hr = GetElementStringProperty(pAspNetCoreElement,
|
||||||
CS_ASPNETCORE_HOSTING_MODEL,
|
CS_ASPNETCORE_HOSTING_MODEL,
|
||||||
&strHostingModel);
|
&m_strHostingModel);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
|
// Swallow this error for backward compatability
|
||||||
|
// Use default behavior for empty string
|
||||||
hr = S_OK;
|
hr = S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strHostingModel.IsEmpty() || strHostingModel.Equals(L"outofprocess", TRUE))
|
if (m_strHostingModel.IsEmpty() || m_strHostingModel.Equals(L"outofprocess", TRUE))
|
||||||
{
|
{
|
||||||
m_fIsOutOfProcess = TRUE;
|
m_hostingModel = HOSTING_OUT_PROCESS;
|
||||||
}
|
}
|
||||||
else if (strHostingModel.Equals(L"inprocess", TRUE))
|
else if (m_strHostingModel.Equals(L"inprocess", TRUE))
|
||||||
{
|
{
|
||||||
m_fIsInProcess = TRUE;
|
m_hostingModel = HOSTING_IN_PROCESS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// block unknown hosting value
|
||||||
|
hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
||||||
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = GetElementStringProperty(pAspNetCoreElement,
|
hr = GetElementStringProperty(pAspNetCoreElement,
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ HTTP_MODULE_ID g_pModuleId = NULL;
|
||||||
IHttpServer * g_pHttpServer = NULL;
|
IHttpServer * g_pHttpServer = NULL;
|
||||||
BOOL g_fAsyncDisconnectAvailable = FALSE;
|
BOOL g_fAsyncDisconnectAvailable = FALSE;
|
||||||
BOOL g_fWinHttpNonBlockingCallbackAvailable = FALSE;
|
BOOL g_fWinHttpNonBlockingCallbackAvailable = FALSE;
|
||||||
|
BOOL g_fRecycleProcessCalled = FALSE;
|
||||||
PCWSTR g_pszModuleName = NULL;
|
PCWSTR g_pszModuleName = NULL;
|
||||||
HINSTANCE g_hModule;
|
HINSTANCE g_hModule;
|
||||||
HINSTANCE g_hWinHttpModule;
|
HINSTANCE g_hWinHttpModule;
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,11 +1,14 @@
|
||||||
|
// 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 "precomp.hxx"
|
||||||
#include "fx_ver.h"
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
typedef DWORD(*hostfxr_main_fn) (CONST DWORD argc, CONST WCHAR* argv[]);
|
typedef DWORD(*hostfxr_main_fn) (CONST DWORD argc, CONST WCHAR* argv[]);
|
||||||
|
|
||||||
|
//
|
||||||
// Initialization export
|
// Initialization export
|
||||||
|
//
|
||||||
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
|
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
|
||||||
VOID
|
VOID
|
||||||
register_callbacks(
|
register_callbacks(
|
||||||
|
|
@ -15,7 +18,7 @@ register_callbacks(
|
||||||
_In_ VOID* pvShutdownHandlerContext
|
_In_ VOID* pvShutdownHandlerContext
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
ASPNETCORE_APPLICATION::GetInstance()->SetCallbackHandles(
|
IN_PROCESS_APPLICATION::GetInstance()->SetCallbackHandles(
|
||||||
request_handler,
|
request_handler,
|
||||||
shutdown_handler,
|
shutdown_handler,
|
||||||
pvRequstHandlerContext,
|
pvRequstHandlerContext,
|
||||||
|
|
@ -81,11 +84,21 @@ http_get_completion_info(
|
||||||
*hr = info->GetCompletionStatus();
|
*hr = info->GetCompletionStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// todo: we should not rely on IN_PROCESS_APPLICATION::GetInstance()
|
||||||
|
// the signature should be changed. application's based address should be passed in
|
||||||
|
//
|
||||||
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
|
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
|
||||||
BSTR // TODO probably should make this a wide string
|
BSTR // TODO probably should make this a wide string
|
||||||
http_get_application_full_path()
|
http_get_application_full_path()
|
||||||
{
|
{
|
||||||
return SysAllocString(ASPNETCORE_APPLICATION::GetInstance()->GetConfig()->QueryApplicationFullPath()->QueryStr());
|
LPWSTR pwzPath = NULL;
|
||||||
|
IN_PROCESS_APPLICATION* pApplication = IN_PROCESS_APPLICATION::GetInstance();
|
||||||
|
if(pApplication != NULL)
|
||||||
|
{
|
||||||
|
pwzPath = pApplication->QueryConfig()->QueryApplicationFullPath()->QueryStr();
|
||||||
|
}
|
||||||
|
return SysAllocString(pwzPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
|
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
|
||||||
|
|
@ -176,311 +189,45 @@ http_flush_response_bytes(
|
||||||
pfCompletionExpected);
|
pfCompletionExpected);
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
// End of export
|
||||||
|
|
||||||
// Thread execution callback
|
|
||||||
static
|
IN_PROCESS_APPLICATION* IN_PROCESS_APPLICATION::s_Application = NULL;
|
||||||
VOID
|
|
||||||
ExecuteAspNetCoreProcess(
|
IN_PROCESS_APPLICATION::IN_PROCESS_APPLICATION():
|
||||||
_In_ LPVOID pContext
|
m_fManagedAppLoaded ( FALSE ), m_fLoadManagedAppError ( FALSE )
|
||||||
)
|
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
|
||||||
ASPNETCORE_APPLICATION *pApplication = (ASPNETCORE_APPLICATION*)pContext;
|
|
||||||
|
|
||||||
hr = pApplication->ExecuteApplication();
|
|
||||||
if (hr != S_OK)
|
|
||||||
{
|
|
||||||
// TODO log error
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ASPNETCORE_APPLICATION*
|
|
||||||
ASPNETCORE_APPLICATION::s_Application = NULL;
|
|
||||||
|
|
||||||
VOID
|
IN_PROCESS_APPLICATION::~IN_PROCESS_APPLICATION()
|
||||||
ASPNETCORE_APPLICATION::SetCallbackHandles(
|
|
||||||
_In_ PFN_REQUEST_HANDLER request_handler,
|
|
||||||
_In_ PFN_SHUTDOWN_HANDLER shutdown_handler,
|
|
||||||
_In_ VOID* pvRequstHandlerContext,
|
|
||||||
_In_ VOID* pvShutdownHandlerContext
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
m_RequestHandler = request_handler;
|
Recycle();
|
||||||
m_RequstHandlerContext = pvRequstHandlerContext;
|
|
||||||
m_ShutdownHandler = shutdown_handler;
|
|
||||||
m_ShutdownHandlerContext = pvShutdownHandlerContext;
|
|
||||||
|
|
||||||
// Initialization complete
|
|
||||||
SetEvent(m_pInitalizeEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT
|
|
||||||
ASPNETCORE_APPLICATION::Initialize(
|
|
||||||
_In_ ASPNETCORE_CONFIG * pConfig
|
|
||||||
)
|
|
||||||
{
|
|
||||||
HRESULT hr = S_OK;
|
|
||||||
|
|
||||||
DWORD dwTimeout;
|
|
||||||
DWORD dwResult;
|
|
||||||
DBG_ASSERT(pConfig != NULL);
|
|
||||||
|
|
||||||
m_pConfiguration = pConfig;
|
|
||||||
|
|
||||||
m_pInitalizeEvent = CreateEvent(
|
|
||||||
NULL, // default security attributes
|
|
||||||
TRUE, // manual reset event
|
|
||||||
FALSE, // not set
|
|
||||||
NULL); // name
|
|
||||||
|
|
||||||
if (m_pInitalizeEvent == NULL)
|
|
||||||
{
|
|
||||||
return HRESULT_FROM_WIN32(GetLastError());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_hThread = CreateThread(
|
|
||||||
NULL, // default security attributes
|
|
||||||
0, // default stack size
|
|
||||||
(LPTHREAD_START_ROUTINE)ExecuteAspNetCoreProcess,
|
|
||||||
this, // thread function arguments
|
|
||||||
0, // default creation flags
|
|
||||||
NULL); // receive thread identifier
|
|
||||||
|
|
||||||
if (m_hThread == NULL)
|
|
||||||
{
|
|
||||||
return HRESULT_FROM_WIN32(GetLastError());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the debugger is attached, never timeout
|
|
||||||
if (IsDebuggerPresent())
|
|
||||||
{
|
|
||||||
dwTimeout = INFINITE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dwTimeout = pConfig->QueryStartupTimeLimitInMS();
|
|
||||||
}
|
|
||||||
|
|
||||||
const HANDLE pHandles[2]{ m_hThread, m_pInitalizeEvent };
|
|
||||||
|
|
||||||
// Wait on either the thread to complete or the event to be set
|
|
||||||
dwResult = WaitForMultipleObjects(2, pHandles, FALSE, dwTimeout);
|
|
||||||
|
|
||||||
// It all timed out
|
|
||||||
if (dwResult == WAIT_TIMEOUT)
|
|
||||||
{
|
|
||||||
return HRESULT_FROM_WIN32(dwResult);
|
|
||||||
}
|
|
||||||
else if (dwResult == WAIT_FAILED)
|
|
||||||
{
|
|
||||||
return HRESULT_FROM_WIN32(GetLastError());
|
|
||||||
}
|
|
||||||
|
|
||||||
dwResult = WaitForSingleObject(m_hThread, 0);
|
|
||||||
|
|
||||||
// The thread ended it means that something failed
|
|
||||||
if (dwResult == WAIT_OBJECT_0)
|
|
||||||
{
|
|
||||||
return HRESULT_FROM_WIN32(dwResult);
|
|
||||||
}
|
|
||||||
else if (dwResult == WAIT_FAILED)
|
|
||||||
{
|
|
||||||
return HRESULT_FROM_WIN32(GetLastError());
|
|
||||||
}
|
|
||||||
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT
|
|
||||||
ASPNETCORE_APPLICATION::ExecuteApplication(
|
|
||||||
VOID
|
|
||||||
)
|
|
||||||
{
|
|
||||||
HRESULT hr = S_OK;
|
|
||||||
|
|
||||||
STRU strFullPath;
|
|
||||||
STRU strDotnetExeLocation;
|
|
||||||
STRU strHostFxrSearchExpression;
|
|
||||||
STRU strDotnetFolderLocation;
|
|
||||||
STRU strHighestDotnetVersion;
|
|
||||||
STRU strApplicationFullPath;
|
|
||||||
PWSTR strDelimeterContext = NULL;
|
|
||||||
PCWSTR pszDotnetExeLocation = NULL;
|
|
||||||
PCWSTR pszDotnetExeString(L"dotnet.exe");
|
|
||||||
DWORD dwCopyLength;
|
|
||||||
HMODULE hModule;
|
|
||||||
PCWSTR argv[2];
|
|
||||||
hostfxr_main_fn pProc;
|
|
||||||
std::vector<std::wstring> vVersionFolders;
|
|
||||||
|
|
||||||
// Get the System PATH value.
|
|
||||||
if (!GetEnv(L"PATH", &strFullPath))
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split on ';', checking to see if dotnet.exe exists in any folders.
|
|
||||||
pszDotnetExeLocation = wcstok_s(strFullPath.QueryStr(), L";", &strDelimeterContext);
|
|
||||||
|
|
||||||
while (pszDotnetExeLocation != NULL)
|
|
||||||
{
|
|
||||||
dwCopyLength = wcsnlen_s(pszDotnetExeLocation, 260);
|
|
||||||
if (dwCopyLength == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We store both the exe and folder locations as we eventually need to check inside of host\\fxr
|
|
||||||
// which doesn't need the dotnet.exe portion of the string
|
|
||||||
// TODO consider reducing allocations.
|
|
||||||
strDotnetExeLocation.Reset();
|
|
||||||
strDotnetFolderLocation.Reset();
|
|
||||||
hr = strDotnetExeLocation.Copy(pszDotnetExeLocation, dwCopyLength);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = strDotnetFolderLocation.Copy(pszDotnetExeLocation, dwCopyLength);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dwCopyLength > 0 && pszDotnetExeLocation[dwCopyLength - 1] != L'\\')
|
|
||||||
{
|
|
||||||
hr = strDotnetExeLocation.Append(L"\\");
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = strDotnetExeLocation.Append(pszDotnetExeString);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PathFileExists(strDotnetExeLocation.QueryStr()))
|
|
||||||
{
|
|
||||||
// means we found the folder with a dotnet.exe inside of it.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pszDotnetExeLocation = wcstok_s(NULL, L";", &strDelimeterContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = strDotnetFolderLocation.Append(L"\\host\\fxr");
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!DirectoryExists(&strDotnetFolderLocation))
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find all folders under host\\fxr\\ for version numbers.
|
|
||||||
hr = strHostFxrSearchExpression.Copy(strDotnetFolderLocation);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = strHostFxrSearchExpression.Append(L"\\*");
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// As we use the logic from core-setup, we are opting to use std here.
|
|
||||||
// TODO remove all uses of std?
|
|
||||||
FindDotNetFolders(&strHostFxrSearchExpression, &vVersionFolders);
|
|
||||||
|
|
||||||
if (vVersionFolders.size() == 0)
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = FindHighestDotNetVersion(vVersionFolders, &strHighestDotnetVersion);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
hr = strDotnetFolderLocation.Append(L"\\");
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = strDotnetFolderLocation.Append(strHighestDotnetVersion.QueryStr());
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = strDotnetFolderLocation.Append(L"\\hostfxr.dll");
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
hModule = LoadLibraryW(strDotnetFolderLocation.QueryStr());
|
|
||||||
|
|
||||||
if (hModule == NULL)
|
|
||||||
{
|
|
||||||
// .NET Core not installed (we can log a more detailed error message here)
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the entry point for main
|
|
||||||
pProc = (hostfxr_main_fn)GetProcAddress(hModule, "hostfxr_main");
|
|
||||||
if (pProc == NULL) {
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The first argument is mostly ignored
|
|
||||||
hr = strDotnetExeLocation.Append(pszDotnetExeString);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
argv[0] = strDotnetExeLocation.QueryStr();
|
|
||||||
PATH::ConvertPathToFullPath(m_pConfiguration->QueryArguments()->QueryStr(), m_pConfiguration->QueryApplicationFullPath()->QueryStr(), &strApplicationFullPath);
|
|
||||||
argv[1] = strApplicationFullPath.QueryStr();
|
|
||||||
|
|
||||||
// There can only ever be a single instance of .NET Core
|
|
||||||
// loaded in the process but we need to get config information to boot it up in the
|
|
||||||
// first place. This is happening in an execute request handler and everyone waits
|
|
||||||
// until this initialization is done.
|
|
||||||
|
|
||||||
// We set a static so that managed code can call back into this instance and
|
|
||||||
// set the callbacks
|
|
||||||
s_Application = this;
|
|
||||||
|
|
||||||
m_ProcessExitCode = pProc(2, argv);
|
|
||||||
if (m_ProcessExitCode != 0)
|
|
||||||
{
|
|
||||||
// TODO error
|
|
||||||
}
|
|
||||||
|
|
||||||
return hr;
|
|
||||||
Failed:
|
|
||||||
// TODO log any errors
|
|
||||||
return hr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL
|
BOOL
|
||||||
ASPNETCORE_APPLICATION::GetEnv(
|
IN_PROCESS_APPLICATION::DirectoryExists(
|
||||||
|
_In_ STRU *pstrPath
|
||||||
|
)
|
||||||
|
{
|
||||||
|
WIN32_FILE_ATTRIBUTE_DATA data;
|
||||||
|
|
||||||
|
if (pstrPath->IsEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetFileAttributesExW(pstrPath->QueryStr(), GetFileExInfoStandard, &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL
|
||||||
|
IN_PROCESS_APPLICATION::GetEnv(
|
||||||
_In_ PCWSTR pszEnvironmentVariable,
|
_In_ PCWSTR pszEnvironmentVariable,
|
||||||
_Out_ STRU *pstrResult
|
_Out_ STRU *pstrResult
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
DWORD dwLength;
|
DWORD dwLength;
|
||||||
PWSTR pszBuffer= NULL;
|
PWSTR pszBuffer = NULL;
|
||||||
BOOL fSucceeded = FALSE;
|
BOOL fSucceeded = FALSE;
|
||||||
|
|
||||||
if (pszEnvironmentVariable == NULL)
|
if (pszEnvironmentVariable == NULL)
|
||||||
|
|
@ -513,15 +260,15 @@ Finished:
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
ASPNETCORE_APPLICATION::FindDotNetFolders(
|
IN_PROCESS_APPLICATION::FindDotNetFolders(
|
||||||
_In_ STRU *pstrPath,
|
_In_ PCWSTR pszPath,
|
||||||
_Out_ std::vector<std::wstring> *pvFolders
|
_Out_ std::vector<std::wstring> *pvFolders
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
HANDLE handle = NULL;
|
HANDLE handle = NULL;
|
||||||
WIN32_FIND_DATAW data = { 0 };
|
WIN32_FIND_DATAW data = { 0 };
|
||||||
|
|
||||||
handle = FindFirstFileExW(pstrPath->QueryStr(), FindExInfoStandard, &data, FindExSearchNameMatch, NULL, 0);
|
handle = FindFirstFileExW(pszPath, FindExInfoStandard, &data, FindExSearchNameMatch, NULL, 0);
|
||||||
if (handle == INVALID_HANDLE_VALUE)
|
if (handle == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
@ -536,8 +283,489 @@ ASPNETCORE_APPLICATION::FindDotNetFolders(
|
||||||
FindClose(handle);
|
FindClose(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VOID
|
||||||
|
IN_PROCESS_APPLICATION::SetCallbackHandles(
|
||||||
|
_In_ PFN_REQUEST_HANDLER request_handler,
|
||||||
|
_In_ PFN_SHUTDOWN_HANDLER shutdown_handler,
|
||||||
|
_In_ VOID* pvRequstHandlerContext,
|
||||||
|
_In_ VOID* pvShutdownHandlerContext
|
||||||
|
)
|
||||||
|
{
|
||||||
|
m_RequestHandler = request_handler;
|
||||||
|
m_RequstHandlerContext = pvRequstHandlerContext;
|
||||||
|
m_ShutdownHandler = shutdown_handler;
|
||||||
|
m_ShutdownHandlerContext = pvShutdownHandlerContext;
|
||||||
|
|
||||||
|
// Initialization complete
|
||||||
|
SetEvent(m_pInitalizeEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Initialize is guarded by a lock inside APPLICATION_MANAGER::GetApplication
|
||||||
|
// It ensures only one application will be initialized and singleton
|
||||||
|
// Error wuill happen if you call Initialized outside APPLICATION_MANAGER::GetApplication
|
||||||
|
//
|
||||||
|
__override
|
||||||
HRESULT
|
HRESULT
|
||||||
ASPNETCORE_APPLICATION::FindHighestDotNetVersion(
|
IN_PROCESS_APPLICATION::Initialize(
|
||||||
|
_In_ APPLICATION_MANAGER* pApplicationManager,
|
||||||
|
_In_ ASPNETCORE_CONFIG* pConfiguration
|
||||||
|
)
|
||||||
|
{
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
DBG_ASSERT(pApplicationManager != NULL);
|
||||||
|
DBG_ASSERT(pConfiguration != NULL);
|
||||||
|
|
||||||
|
m_pConfiguration = pConfiguration;
|
||||||
|
m_pApplicationManager = pApplicationManager;
|
||||||
|
hr = m_applicationKey.Initialize(pConfiguration->QueryApplicationPath()->QueryStr());
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check app_offline
|
||||||
|
UpdateAppOfflineFileHandle();
|
||||||
|
|
||||||
|
if (m_pFileWatcherEntry == NULL)
|
||||||
|
{
|
||||||
|
m_pFileWatcherEntry = new FILE_WATCHER_ENTRY(m_pApplicationManager->GetFileWatcher());
|
||||||
|
if (m_pFileWatcherEntry == NULL)
|
||||||
|
{
|
||||||
|
hr = E_OUTOFMEMORY;
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pInitalizeEvent = CreateEvent(
|
||||||
|
NULL, // default security attributes
|
||||||
|
TRUE, // manual reset event
|
||||||
|
FALSE, // not set
|
||||||
|
NULL); // name
|
||||||
|
if (m_pInitalizeEvent == NULL)
|
||||||
|
{
|
||||||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
Finished:
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT
|
||||||
|
IN_PROCESS_APPLICATION::LoadManagedApplication()
|
||||||
|
{
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
DWORD dwTimeout;
|
||||||
|
DWORD dwResult;
|
||||||
|
BOOL fLocked = FALSE;
|
||||||
|
PCWSTR apsz[1];
|
||||||
|
STACK_STRU(strEventMsg, 256);
|
||||||
|
|
||||||
|
if (m_fManagedAppLoaded || m_fLoadManagedAppError)
|
||||||
|
{
|
||||||
|
// Core CLR has already been loaded.
|
||||||
|
// Cannot load more than once even there was a failure
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
AcquireSRWLockExclusive(&m_srwLock);
|
||||||
|
fLocked = TRUE;
|
||||||
|
if (m_fManagedAppLoaded || m_fLoadManagedAppError)
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_hThread = CreateThread(
|
||||||
|
NULL, // default security attributes
|
||||||
|
0, // default stack size
|
||||||
|
(LPTHREAD_START_ROUTINE)ExecuteAspNetCoreProcess,
|
||||||
|
this, // thread function arguments
|
||||||
|
0, // default creation flags
|
||||||
|
NULL); // receive thread identifier
|
||||||
|
|
||||||
|
if (m_hThread == NULL)
|
||||||
|
{
|
||||||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the debugger is attached, never timeout
|
||||||
|
if (IsDebuggerPresent())
|
||||||
|
{
|
||||||
|
dwTimeout = INFINITE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dwTimeout = m_pConfiguration->QueryStartupTimeLimitInMS();
|
||||||
|
}
|
||||||
|
|
||||||
|
const HANDLE pHandles[2]{ m_hThread, m_pInitalizeEvent };
|
||||||
|
|
||||||
|
// Wait on either the thread to complete or the event to be set
|
||||||
|
dwResult = WaitForMultipleObjects(2, pHandles, FALSE, dwTimeout);
|
||||||
|
|
||||||
|
// It all timed out
|
||||||
|
if (dwResult == WAIT_TIMEOUT)
|
||||||
|
{
|
||||||
|
// do we need kill the backend thread
|
||||||
|
hr = HRESULT_FROM_WIN32(dwResult);
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
else if (dwResult == WAIT_FAILED)
|
||||||
|
{
|
||||||
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The thread ended it means that something failed
|
||||||
|
if (dwResult == WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
hr = E_APPLICATION_ACTIVATION_EXEC_FAILURE;
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_fManagedAppLoaded = TRUE;
|
||||||
|
|
||||||
|
Finished:
|
||||||
|
if (fLocked)
|
||||||
|
{
|
||||||
|
ReleaseSRWLockExclusive(&m_srwLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
// Question: in case of application loading failure, should we allow retry on
|
||||||
|
// following request or block the activation at all
|
||||||
|
m_fLoadManagedAppError = FALSE; // m_hThread != NULL ?
|
||||||
|
|
||||||
|
if (SUCCEEDED(strEventMsg.SafeSnwprintf(
|
||||||
|
ASPNETCORE_EVENT_LOAD_CLR_FALIURE_MSG,
|
||||||
|
m_pConfiguration->QueryApplicationPath()->QueryStr(),
|
||||||
|
m_pConfiguration->QueryApplicationFullPath()->QueryStr(),
|
||||||
|
hr)))
|
||||||
|
{
|
||||||
|
apsz[0] = strEventMsg.QueryStr();
|
||||||
|
|
||||||
|
//
|
||||||
|
// not checking return code because if ReportEvent
|
||||||
|
// fails, we cannot do anything.
|
||||||
|
//
|
||||||
|
if (FORWARDING_HANDLER::QueryEventLog() != NULL)
|
||||||
|
{
|
||||||
|
ReportEventW(FORWARDING_HANDLER::QueryEventLog(),
|
||||||
|
EVENTLOG_ERROR_TYPE,
|
||||||
|
0,
|
||||||
|
ASPNETCORE_EVENT_LOAD_CLR_FALIURE,
|
||||||
|
NULL,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
apsz,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID
|
||||||
|
IN_PROCESS_APPLICATION::Recycle(
|
||||||
|
VOID
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DWORD dwThreadStatus = 0;
|
||||||
|
DWORD dwTimeout = m_pConfiguration->QueryShutdownTimeLimitInMS();
|
||||||
|
|
||||||
|
AcquireSRWLockExclusive(&m_srwLock);
|
||||||
|
|
||||||
|
if (!g_pHttpServer->IsCommandLineLaunch() && !g_fRecycleProcessCalled)
|
||||||
|
{
|
||||||
|
// IIS scenario.
|
||||||
|
// notify IIS first so that new request will be routed to new worker process
|
||||||
|
g_fRecycleProcessCalled = TRUE;
|
||||||
|
g_pHttpServer->RecycleProcess(L"AspNetCore Recycle Process on Demand");
|
||||||
|
}
|
||||||
|
// First call into the managed server and shutdown
|
||||||
|
if (m_ShutdownHandler != NULL)
|
||||||
|
{
|
||||||
|
m_ShutdownHandler(m_ShutdownHandlerContext);
|
||||||
|
m_ShutdownHandler = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_hThread != NULL &&
|
||||||
|
GetExitCodeThread(m_hThread, &dwThreadStatus) != 0 &&
|
||||||
|
dwThreadStatus == STILL_ACTIVE)
|
||||||
|
{
|
||||||
|
// wait for gracefullshut down, i.e., the exit of the background thread or timeout
|
||||||
|
if (WaitForSingleObject(m_hThread, dwTimeout) != WAIT_OBJECT_0)
|
||||||
|
{
|
||||||
|
// if the thread is still running, we need kill it first before exit to avoid AV
|
||||||
|
if (GetExitCodeThread(m_hThread, &dwThreadStatus) != 0 && dwThreadStatus == STILL_ACTIVE)
|
||||||
|
{
|
||||||
|
TerminateThread(m_hThread, STATUS_CONTROL_C_EXIT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(m_hThread);
|
||||||
|
m_hThread = NULL;
|
||||||
|
s_Application = NULL;
|
||||||
|
ReleaseSRWLockExclusive(&m_srwLock);
|
||||||
|
|
||||||
|
if (g_pHttpServer->IsCommandLineLaunch())
|
||||||
|
{
|
||||||
|
// IISExpress scenario
|
||||||
|
// Can only call exit to terminate current process
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID
|
||||||
|
IN_PROCESS_APPLICATION::OnAppOfflineHandleChange()
|
||||||
|
{
|
||||||
|
// only recycle the worker process after managed app was loaded
|
||||||
|
// app_offline scenario managed application has not been loaded yet
|
||||||
|
if (m_fManagedAppLoaded || m_fLoadManagedAppError)
|
||||||
|
{
|
||||||
|
Recycle();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUEST_NOTIFICATION_STATUS
|
||||||
|
IN_PROCESS_APPLICATION::ExecuteRequest(
|
||||||
|
_In_ IHttpContext* pHttpContext
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (m_RequestHandler != NULL)
|
||||||
|
{
|
||||||
|
return m_RequestHandler(pHttpContext, m_RequstHandlerContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// return error as the application did not register callback
|
||||||
|
//
|
||||||
|
pHttpContext->GetResponse()->SetStatus(500, "Internal Server Error", 0, E_APPLICATION_ACTIVATION_EXEC_FAILURE);
|
||||||
|
return RQ_NOTIFICATION_FINISH_REQUEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT
|
||||||
|
IN_PROCESS_APPLICATION::ExecuteApplication(
|
||||||
|
VOID
|
||||||
|
)
|
||||||
|
{
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
|
STRU strFullPath;
|
||||||
|
STRU strDotnetExeLocation;
|
||||||
|
STRU strHostFxrSearchExpression;
|
||||||
|
STRU strDotnetFolderLocation;
|
||||||
|
STRU strHighestDotnetVersion;
|
||||||
|
STRU strApplicationFullPath;
|
||||||
|
PWSTR strDelimeterContext = NULL;
|
||||||
|
PCWSTR pszDotnetExeLocation = NULL;
|
||||||
|
PCWSTR pszDotnetExeString(L"dotnet.exe");
|
||||||
|
DWORD dwCopyLength;
|
||||||
|
HMODULE hModule;
|
||||||
|
PCWSTR argv[2];
|
||||||
|
hostfxr_main_fn pProc;
|
||||||
|
std::vector<std::wstring> vVersionFolders;
|
||||||
|
bool fFound = FALSE;
|
||||||
|
|
||||||
|
// Get the System PATH value.
|
||||||
|
if (!GetEnv(L"PATH", &strFullPath))
|
||||||
|
{
|
||||||
|
hr = ERROR_BAD_ENVIRONMENT;
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split on ';', checking to see if dotnet.exe exists in any folders.
|
||||||
|
pszDotnetExeLocation = wcstok_s(strFullPath.QueryStr(), L";", &strDelimeterContext);
|
||||||
|
|
||||||
|
while (pszDotnetExeLocation != NULL)
|
||||||
|
{
|
||||||
|
dwCopyLength = wcsnlen_s(pszDotnetExeLocation, 260);
|
||||||
|
if (dwCopyLength == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We store both the exe and folder locations as we eventually need to check inside of host\\fxr
|
||||||
|
// which doesn't need the dotnet.exe portion of the string
|
||||||
|
// TODO consider reducing allocations.
|
||||||
|
strDotnetExeLocation.Reset();
|
||||||
|
strDotnetFolderLocation.Reset();
|
||||||
|
hr = strDotnetExeLocation.Copy(pszDotnetExeLocation, dwCopyLength);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = strDotnetFolderLocation.Copy(pszDotnetExeLocation, dwCopyLength);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dwCopyLength > 0 && pszDotnetExeLocation[dwCopyLength - 1] != L'\\')
|
||||||
|
{
|
||||||
|
hr = strDotnetExeLocation.Append(L"\\");
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = strDotnetExeLocation.Append(pszDotnetExeString);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PathFileExists(strDotnetExeLocation.QueryStr()))
|
||||||
|
{
|
||||||
|
// means we found the folder with a dotnet.exe inside of it.
|
||||||
|
fFound = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pszDotnetExeLocation = wcstok_s(NULL, L";", &strDelimeterContext);
|
||||||
|
}
|
||||||
|
if (!fFound)
|
||||||
|
{
|
||||||
|
// could not find dotnet.exe, error out
|
||||||
|
hr = ERROR_BAD_ENVIRONMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = strDotnetFolderLocation.Append(L"\\host\\fxr");
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DirectoryExists(&strDotnetFolderLocation))
|
||||||
|
{
|
||||||
|
// error, not found the folder
|
||||||
|
hr = ERROR_BAD_ENVIRONMENT;
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all folders under host\\fxr\\ for version numbers.
|
||||||
|
hr = strHostFxrSearchExpression.Copy(strDotnetFolderLocation);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = strHostFxrSearchExpression.Append(L"\\*");
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
// As we use the logic from core-setup, we are opting to use std here.
|
||||||
|
// TODO remove all uses of std?
|
||||||
|
FindDotNetFolders(strHostFxrSearchExpression.QueryStr(), &vVersionFolders);
|
||||||
|
|
||||||
|
if (vVersionFolders.size() == 0)
|
||||||
|
{
|
||||||
|
// no core framework was found
|
||||||
|
hr = ERROR_BAD_ENVIRONMENT;
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = FindHighestDotNetVersion(vVersionFolders, &strHighestDotnetVersion);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
hr = strDotnetFolderLocation.Append(L"\\");
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = strDotnetFolderLocation.Append(strHighestDotnetVersion.QueryStr());
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = strDotnetFolderLocation.Append(L"\\hostfxr.dll");
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
hModule = LoadLibraryW(strDotnetFolderLocation.QueryStr());
|
||||||
|
|
||||||
|
if (hModule == NULL)
|
||||||
|
{
|
||||||
|
// .NET Core not installed (we can log a more detailed error message here)
|
||||||
|
hr = ERROR_BAD_ENVIRONMENT;
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the entry point for main
|
||||||
|
pProc = (hostfxr_main_fn)GetProcAddress(hModule, "hostfxr_main");
|
||||||
|
if (pProc == NULL)
|
||||||
|
{
|
||||||
|
hr = ERROR_BAD_ENVIRONMENT; // better hrresult?
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first argument is mostly ignored
|
||||||
|
hr = strDotnetExeLocation.Append(pszDotnetExeString);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
argv[0] = strDotnetExeLocation.QueryStr();
|
||||||
|
PATH::ConvertPathToFullPath(m_pConfiguration->QueryArguments()->QueryStr(),
|
||||||
|
m_pConfiguration->QueryApplicationFullPath()->QueryStr(),
|
||||||
|
&strApplicationFullPath);
|
||||||
|
argv[1] = strApplicationFullPath.QueryStr();
|
||||||
|
|
||||||
|
// There can only ever be a single instance of .NET Core
|
||||||
|
// loaded in the process but we need to get config information to boot it up in the
|
||||||
|
// first place. This is happening in an execute request handler and everyone waits
|
||||||
|
// until this initialization is done.
|
||||||
|
|
||||||
|
// We set a static so that managed code can call back into this instance and
|
||||||
|
// set the callbacks
|
||||||
|
s_Application = this;
|
||||||
|
|
||||||
|
m_ProcessExitCode = pProc(2, argv);
|
||||||
|
if (m_ProcessExitCode != 0)
|
||||||
|
{
|
||||||
|
// TODO error
|
||||||
|
}
|
||||||
|
|
||||||
|
Finished:
|
||||||
|
// TODO log any errors
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// static
|
||||||
|
VOID
|
||||||
|
IN_PROCESS_APPLICATION::ExecuteAspNetCoreProcess(
|
||||||
|
_In_ LPVOID pContext
|
||||||
|
)
|
||||||
|
{
|
||||||
|
|
||||||
|
IN_PROCESS_APPLICATION *pApplication = (IN_PROCESS_APPLICATION*)pContext;
|
||||||
|
DBG_ASSERT(pApplication != NULL);
|
||||||
|
pApplication->ExecuteApplication();
|
||||||
|
//
|
||||||
|
// no need to log the error here as if error happened, the thread will exit
|
||||||
|
// the error will ba catched by caller LoadManagedApplication which will log an error
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT
|
||||||
|
IN_PROCESS_APPLICATION::FindHighestDotNetVersion(
|
||||||
_In_ std::vector<std::wstring> vFolders,
|
_In_ std::vector<std::wstring> vFolders,
|
||||||
_Out_ STRU *pstrResult
|
_Out_ STRU *pstrResult
|
||||||
)
|
)
|
||||||
|
|
@ -558,44 +786,3 @@ ASPNETCORE_APPLICATION::FindHighestDotNetVersion(
|
||||||
// we check FAILED(hr) outside of function
|
// we check FAILED(hr) outside of function
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL
|
|
||||||
ASPNETCORE_APPLICATION::DirectoryExists(
|
|
||||||
_In_ STRU *pstrPath
|
|
||||||
)
|
|
||||||
{
|
|
||||||
WIN32_FILE_ATTRIBUTE_DATA data;
|
|
||||||
|
|
||||||
if (pstrPath->IsEmpty())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetFileAttributesExW(pstrPath->QueryStr(), GetFileExInfoStandard, &data);
|
|
||||||
}
|
|
||||||
|
|
||||||
REQUEST_NOTIFICATION_STATUS
|
|
||||||
ASPNETCORE_APPLICATION::ExecuteRequest(
|
|
||||||
_In_ IHttpContext* pHttpContext
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (m_RequestHandler != NULL)
|
|
||||||
{
|
|
||||||
return m_RequestHandler(pHttpContext, m_RequstHandlerContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
pHttpContext->GetResponse()->SetStatus(500, "Internal Server Error", 0, E_APPLICATION_ACTIVATION_EXEC_FAILURE);
|
|
||||||
return RQ_NOTIFICATION_FINISH_REQUEST;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
VOID
|
|
||||||
ASPNETCORE_APPLICATION::Shutdown(
|
|
||||||
VOID
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// First call into the managed server and shutdown
|
|
||||||
BOOL result = m_ShutdownHandler(m_ShutdownHandlerContext);
|
|
||||||
s_Application = NULL;
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
OUT_OF_PROCESS_APPLICATION::OUT_OF_PROCESS_APPLICATION()
|
||||||
|
: m_pProcessManager(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
OUT_OF_PROCESS_APPLICATION::~OUT_OF_PROCESS_APPLICATION()
|
||||||
|
{
|
||||||
|
if (m_pProcessManager != NULL)
|
||||||
|
{
|
||||||
|
m_pProcessManager->ShutdownAllProcesses();
|
||||||
|
m_pProcessManager->DereferenceProcessManager();
|
||||||
|
m_pProcessManager = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Initialize is guarded by a lock inside APPLICATION_MANAGER::GetApplication
|
||||||
|
// It ensures only one application will be initialized and singleton
|
||||||
|
// Error will happen if you call Initialized outside APPLICATION_MANAGER::GetApplication
|
||||||
|
//
|
||||||
|
__override
|
||||||
|
HRESULT
|
||||||
|
OUT_OF_PROCESS_APPLICATION::Initialize(
|
||||||
|
_In_ APPLICATION_MANAGER* pApplicationManager,
|
||||||
|
_In_ ASPNETCORE_CONFIG* pConfiguration
|
||||||
|
)
|
||||||
|
{
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
|
DBG_ASSERT(pApplicationManager != NULL);
|
||||||
|
DBG_ASSERT(pConfiguration != NULL);
|
||||||
|
|
||||||
|
m_pApplicationManager = pApplicationManager;
|
||||||
|
m_pConfiguration = pConfiguration;
|
||||||
|
|
||||||
|
hr = m_applicationKey.Initialize(pConfiguration->QueryApplicationPath()->QueryStr());
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pProcessManager == NULL)
|
||||||
|
{
|
||||||
|
m_pProcessManager = new PROCESS_MANAGER;
|
||||||
|
if (m_pProcessManager == NULL)
|
||||||
|
{
|
||||||
|
hr = E_OUTOFMEMORY;
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = m_pProcessManager->Initialize();
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pFileWatcherEntry == NULL)
|
||||||
|
{
|
||||||
|
m_pFileWatcherEntry = new FILE_WATCHER_ENTRY(pApplicationManager->GetFileWatcher());
|
||||||
|
if (m_pFileWatcherEntry == NULL)
|
||||||
|
{
|
||||||
|
hr = E_OUTOFMEMORY;
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateAppOfflineFileHandle();
|
||||||
|
|
||||||
|
Finished:
|
||||||
|
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
if (m_pFileWatcherEntry != NULL)
|
||||||
|
{
|
||||||
|
m_pFileWatcherEntry->DereferenceFileWatcherEntry();
|
||||||
|
m_pFileWatcherEntry = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_pProcessManager != NULL)
|
||||||
|
{
|
||||||
|
m_pProcessManager->DereferenceProcessManager();
|
||||||
|
m_pProcessManager = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
__override
|
||||||
|
VOID
|
||||||
|
OUT_OF_PROCESS_APPLICATION::OnAppOfflineHandleChange()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Sending signal to backend process for shutdown
|
||||||
|
//
|
||||||
|
if (m_pProcessManager != NULL)
|
||||||
|
{
|
||||||
|
m_pProcessManager->SendShutdownSignal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__override
|
||||||
|
REQUEST_NOTIFICATION_STATUS
|
||||||
|
OUT_OF_PROCESS_APPLICATION::ExecuteRequest(
|
||||||
|
_In_ IHttpContext* pHttpContext
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// TODO:
|
||||||
|
// Ideally we should wrap the fowaring logic inside FORWARDING_HANDLER inside this function
|
||||||
|
// To achieve better abstraction. It is too risky to do it now
|
||||||
|
//
|
||||||
|
return RQ_NOTIFICATION_FINISH_REQUEST;
|
||||||
|
}
|
||||||
|
|
@ -114,12 +114,13 @@ inline bool IsSpace(char ch)
|
||||||
#include "environmentvariablehash.h"
|
#include "environmentvariablehash.h"
|
||||||
#include "..\aspnetcore_msg.h"
|
#include "..\aspnetcore_msg.h"
|
||||||
#include "aspnetcoreconfig.h"
|
#include "aspnetcoreconfig.h"
|
||||||
#include "aspnetcoreapplication.h"
|
|
||||||
#include "serverprocess.h"
|
#include "serverprocess.h"
|
||||||
#include "processmanager.h"
|
#include "processmanager.h"
|
||||||
#include "filewatcher.h"
|
#include "filewatcher.h"
|
||||||
#include "application.h"
|
#include "application.h"
|
||||||
#include "applicationmanager.h"
|
#include "applicationmanager.h"
|
||||||
|
#include "inprocessapplication.h"
|
||||||
|
#include "outprocessapplication.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
#include "debugutil.h"
|
#include "debugutil.h"
|
||||||
|
|
@ -130,6 +131,7 @@ inline bool IsSpace(char ch)
|
||||||
#include "websockethandler.h"
|
#include "websockethandler.h"
|
||||||
#include "forwardinghandler.h"
|
#include "forwardinghandler.h"
|
||||||
#include "proxymodule.h"
|
#include "proxymodule.h"
|
||||||
|
#include "fx_ver.h"
|
||||||
|
|
||||||
FORCEINLINE
|
FORCEINLINE
|
||||||
DWORD
|
DWORD
|
||||||
|
|
|
||||||
|
|
@ -78,55 +78,42 @@ CProxyModule::OnExecuteRequestHandler(
|
||||||
IHttpEventProvider *
|
IHttpEventProvider *
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr = S_OK;
|
||||||
APPLICATION_MANAGER* pApplicationManager;
|
ASPNETCORE_CONFIG *pConfig = NULL;
|
||||||
APPLICATION* pApplication;
|
APPLICATION_MANAGER *pApplicationManager = NULL;
|
||||||
ASPNETCORE_CONFIG* config;
|
APPLICATION *pApplication = NULL;
|
||||||
ASPNETCORE_APPLICATION* pAspNetCoreApplication;
|
hr = ASPNETCORE_CONFIG::GetConfig(pHttpContext, &pConfig);
|
||||||
ASPNETCORE_CONFIG::GetConfig(pHttpContext, &config);
|
if (FAILED(hr))
|
||||||
|
|
||||||
if (config->QueryIsOutOfProcess())// case insensitive
|
|
||||||
{
|
{
|
||||||
m_pHandler = new FORWARDING_HANDLER(pHttpContext);
|
goto Failed;
|
||||||
if (m_pHandler == NULL)
|
|
||||||
{
|
|
||||||
hr = E_OUTOFMEMORY;
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_pHandler->OnExecuteRequestHandler();
|
|
||||||
}
|
}
|
||||||
else if (config->QueryIsInProcess())
|
|
||||||
|
pApplicationManager = APPLICATION_MANAGER::GetInstance();
|
||||||
|
if (pApplicationManager == NULL)
|
||||||
{
|
{
|
||||||
pApplicationManager = APPLICATION_MANAGER::GetInstance();
|
hr = E_OUTOFMEMORY;
|
||||||
if (pApplicationManager == NULL)
|
goto Failed;
|
||||||
{
|
|
||||||
hr = E_OUTOFMEMORY;
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = pApplicationManager->GetApplication(pHttpContext,
|
|
||||||
&pApplication);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr = pApplication->GetAspNetCoreApplication(config, pHttpContext, &pAspNetCoreApplication);
|
|
||||||
if (FAILED(hr))
|
|
||||||
{
|
|
||||||
goto Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow reading and writing to simultaneously
|
|
||||||
((IHttpContext3*)pHttpContext)->EnableFullDuplex();
|
|
||||||
|
|
||||||
// Disable response buffering by default, we'll do a write behind buffering in managed code
|
|
||||||
((IHttpResponse2*)pHttpContext->GetResponse())->DisableBuffering();
|
|
||||||
|
|
||||||
// TODO: Optimize sync completions
|
|
||||||
return pAspNetCoreApplication->ExecuteRequest(pHttpContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hr = pApplicationManager->GetApplication(
|
||||||
|
pHttpContext,
|
||||||
|
pConfig,
|
||||||
|
&pApplication);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
goto Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pHandler = new FORWARDING_HANDLER(pHttpContext, pApplication);
|
||||||
|
|
||||||
|
if (m_pHandler == NULL)
|
||||||
|
{
|
||||||
|
hr = E_OUTOFMEMORY;
|
||||||
|
goto Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_pHandler->OnExecuteRequestHandler();
|
||||||
|
|
||||||
Failed:
|
Failed:
|
||||||
pHttpContext->GetResponse()->SetStatus(500, "Internal Server Error", 0, hr);
|
pHttpContext->GetResponse()->SetStatus(500, "Internal Server Error", 0, hr);
|
||||||
return REQUEST_NOTIFICATION_STATUS::RQ_NOTIFICATION_FINISH_REQUEST;
|
return REQUEST_NOTIFICATION_STATUS::RQ_NOTIFICATION_FINISH_REQUEST;
|
||||||
|
|
@ -142,21 +129,7 @@ CProxyModule::OnAsyncCompletion(
|
||||||
IHttpCompletionInfo * pCompletionInfo
|
IHttpCompletionInfo * pCompletionInfo
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// TODO store whether we are inproc or outofproc so we don't need to check the config everytime?
|
return m_pHandler->OnAsyncCompletion(
|
||||||
ASPNETCORE_CONFIG* config;
|
pCompletionInfo->GetCompletionBytes(),
|
||||||
ASPNETCORE_CONFIG::GetConfig(pHttpContext, &config);
|
pCompletionInfo->GetCompletionStatus());
|
||||||
|
|
||||||
if (config->QueryIsOutOfProcess())
|
|
||||||
{
|
|
||||||
return m_pHandler->OnAsyncCompletion(
|
|
||||||
pCompletionInfo->GetCompletionBytes(),
|
|
||||||
pCompletionInfo->GetCompletionStatus());
|
|
||||||
}
|
|
||||||
else if (config->QueryIsInProcess())
|
|
||||||
{
|
|
||||||
return REQUEST_NOTIFICATION_STATUS::RQ_NOTIFICATION_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
pHttpContext->GetResponse()->SetStatus(500, "Internal Server Error", 0, E_APPLICATION_ACTIVATION_EXEC_FAILURE);
|
|
||||||
return REQUEST_NOTIFICATION_STATUS::RQ_NOTIFICATION_FINISH_REQUEST;
|
|
||||||
}
|
}
|
||||||
|
|
@ -1064,7 +1064,6 @@ Finished:
|
||||||
{
|
{
|
||||||
if (!fDonePrepareCommandLine)
|
if (!fDonePrepareCommandLine)
|
||||||
{
|
{
|
||||||
|
|
||||||
strEventMsg.SafeSnwprintf(
|
strEventMsg.SafeSnwprintf(
|
||||||
m_struAppFullPath.QueryStr(),
|
m_struAppFullPath.QueryStr(),
|
||||||
ASPNETCORE_EVENT_PROCESS_START_INTERNAL_ERROR_MSG,
|
ASPNETCORE_EVENT_PROCESS_START_INTERNAL_ERROR_MSG,
|
||||||
|
|
@ -1073,11 +1072,11 @@ Finished:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
strEventMsg.SafeSnwprintf(
|
strEventMsg.SafeSnwprintf(
|
||||||
ASPNETCORE_EVENT_PROCESS_START_POSTCREATE_ERROR_MSG,
|
ASPNETCORE_EVENT_PROCESS_START_POSTCREATE_ERROR_MSG,
|
||||||
m_struAppFullPath.QueryStr(),
|
m_struAppFullPath.QueryStr(),
|
||||||
m_pszRootApplicationPath.QueryStr(),
|
m_pszRootApplicationPath.QueryStr(),
|
||||||
struCommandLine.QueryStr(),
|
struCommandLine.QueryStr(),
|
||||||
hr);
|
hr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue