diff --git a/src/AspNetCore/AspNetCore.vcxproj b/src/AspNetCore/AspNetCore.vcxproj
index b642d81af6..cce29d5c78 100644
--- a/src/AspNetCore/AspNetCore.vcxproj
+++ b/src/AspNetCore/AspNetCore.vcxproj
@@ -154,7 +154,6 @@
-
@@ -174,10 +173,13 @@
+
+
-
+
+
diff --git a/src/AspNetCore/Inc/application.h b/src/AspNetCore/Inc/application.h
index cecd121b80..3bed6712b8 100644
--- a/src/AspNetCore/Inc/application.h
+++ b/src/AspNetCore/Inc/application.h
@@ -146,9 +146,9 @@ class APPLICATION
{
public:
- APPLICATION() : m_pProcessManager(NULL), m_pApplicationManager(NULL), m_cRefs(1),
- m_fAppOfflineFound(FALSE), m_pAppOfflineHtm(NULL), m_pFileWatcherEntry(NULL),
- m_pAspNetCoreApplication(NULL)
+ APPLICATION() : m_pApplicationManager(NULL), m_cRefs(1),
+ m_fAppOfflineFound(FALSE), m_pAppOfflineHtm(NULL),
+ m_pFileWatcherEntry(NULL), m_pConfiguration(NULL)
{
InitializeSRWLock(&m_srwLock);
}
@@ -159,92 +159,14 @@ public:
return &m_applicationKey;
}
- VOID
- SetAppOfflineFound(
- BOOL found
- )
- {
- m_fAppOfflineFound = found;
- }
-
- BOOL
- AppOfflineFound()
- {
- return m_fAppOfflineFound;
- }
+ virtual
+ ~APPLICATION();
+ virtual
HRESULT
- GetProcess(
- _In_ IHttpContext *context,
- _In_ ASPNETCORE_CONFIG *pConfig,
- _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;
- }
+ Initialize(
+ _In_ APPLICATION_MANAGER *pApplicationManager,
+ _In_ ASPNETCORE_CONFIG *pConfiguration) = 0;
VOID
ReferenceApplication() const
@@ -266,14 +188,15 @@ public:
return m_pAppOfflineHtm;
}
- ~APPLICATION();
+ BOOL
+ AppOfflineFound()
+ {
+ return m_fAppOfflineFound;
+ }
- HRESULT
- Initialize(
- _In_ APPLICATION_MANAGER *pApplicationManager,
- _In_ LPCWSTR pszApplication,
- _In_ LPCWSTR pszPhysicalPath
- );
+ virtual
+ VOID
+ OnAppOfflineHandleChange() = 0;
VOID
UpdateAppOfflineFileHandle();
@@ -281,18 +204,29 @@ public:
HRESULT
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;
APPLICATION_KEY m_applicationKey;
- PROCESS_MANAGER* m_pProcessManager;
APPLICATION_MANAGER *m_pApplicationManager;
BOOL m_fAppOfflineFound;
APP_OFFLINE_HTM *m_pAppOfflineHtm;
FILE_WATCHER_ENTRY *m_pFileWatcherEntry;
- ASPNETCORE_APPLICATION *m_pAspNetCoreApplication;
+ ASPNETCORE_CONFIG *m_pConfiguration;
SRWLOCK m_srwLock;
+
};
class APPLICATION_HASH :
diff --git a/src/AspNetCore/Inc/applicationmanager.h b/src/AspNetCore/Inc/applicationmanager.h
index 1389d651e9..9b413341bd 100644
--- a/src/AspNetCore/Inc/applicationmanager.h
+++ b/src/AspNetCore/Inc/applicationmanager.h
@@ -39,6 +39,7 @@ public:
HRESULT
GetApplication(
_In_ IHttpContext* pContext,
+ _In_ ASPNETCORE_CONFIG* pConfig,
_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
//
- 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(
" \
\
\
@@ -155,4 +158,5 @@ private:
SRWLOCK m_srwLock;
HTTP_DATA_CHUNK *m_pHttp502ErrorPage;
LPSTR m_pstrErrorInfo;
+ APP_HOSTING_MODEL m_hostingModel;
};
\ No newline at end of file
diff --git a/src/AspNetCore/Inc/aspnetcoreconfig.h b/src/AspNetCore/Inc/aspnetcoreconfig.h
index b2a62695dc..5a4fbf398a 100644
--- a/src/AspNetCore/Inc/aspnetcoreconfig.h
+++ b/src/AspNetCore/Inc/aspnetcoreconfig.h
@@ -38,6 +38,14 @@
extern HTTP_MODULE_ID g_pModuleId;
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
{
@@ -123,13 +131,13 @@ public:
return &m_struApplication;
}
- STRU*
- QueryApplicationFullPath(
+ STRU*
+ QueryApplicationFullPath(
VOID
- )
- {
- return &m_struApplicationFullPath;
- }
+ )
+ {
+ return &m_struApplicationFullPath;
+ }
STRU*
QueryProcessPath(
@@ -139,6 +147,22 @@ public:
return &m_struProcessPath;
}
+ APP_HOSTING_MODEL
+ QueryHostingModel(
+ VOID
+ )
+ {
+ return m_hostingModel;
+ }
+
+ STRU*
+ QueryHostingModelStr(
+ VOID
+ )
+ {
+ return &m_strHostingModel;
+ }
+
BOOL
QueryStdoutLogEnabled()
{
@@ -175,19 +199,6 @@ public:
return m_fDisableStartUpErrorPage;
}
- BOOL
- QueryIsInProcess()
- {
- return m_fIsInProcess;
- }
-
- BOOL
- QueryIsOutOfProcess()
- {
- return m_fIsOutOfProcess;
- }
-
-
STRU*
QueryStdoutLogFile()
{
@@ -201,7 +212,8 @@ private:
//
ASPNETCORE_CONFIG():
m_fStdoutLogEnabled( FALSE ),
- m_pEnvironmentVariables( NULL )
+ m_pEnvironmentVariables( NULL ),
+ m_hostingModel( HOSTING_UNKNOWN )
{
}
@@ -220,13 +232,13 @@ private:
STRU m_struProcessPath;
STRU m_struStdoutLogFile;
STRU m_struApplicationFullPath;
+ STRU m_strHostingModel;
BOOL m_fStdoutLogEnabled;
BOOL m_fForwardWindowsAuthToken;
BOOL m_fDisableStartUpErrorPage;
BOOL m_fWindowsAuthEnabled;
BOOL m_fBasicAuthEnabled;
BOOL m_fAnonymousAuthEnabled;
- BOOL m_fIsInProcess;
- BOOL m_fIsOutOfProcess;
+ APP_HOSTING_MODEL m_hostingModel;
ENVIRONMENT_VAR_HASH* m_pEnvironmentVariables;
};
diff --git a/src/AspNetCore/Inc/forwardinghandler.h b/src/AspNetCore/Inc/forwardinghandler.h
index c66fa5c77f..0213114b18 100644
--- a/src/AspNetCore/Inc/forwardinghandler.h
+++ b/src/AspNetCore/Inc/forwardinghandler.h
@@ -47,7 +47,8 @@ class FORWARDING_HANDLER
public:
FORWARDING_HANDLER(
- __in IHttpContext * pW3Context
+ __in IHttpContext * pW3Context,
+ __in APPLICATION * pApplication
);
static void * operator new(size_t size);
diff --git a/src/AspNetCore/Inc/aspnetcoreapplication.h b/src/AspNetCore/Inc/inprocessapplication.h
similarity index 54%
rename from src/AspNetCore/Inc/aspnetcoreapplication.h
rename to src/AspNetCore/Inc/inprocessapplication.h
index 725535ccc8..ca36067d11 100644
--- a/src/AspNetCore/Inc/aspnetcoreapplication.h
+++ b/src/AspNetCore/Inc/inprocessapplication.h
@@ -1,5 +1,5 @@
// 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
@@ -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 BOOL(*PFN_SHUTDOWN_HANDLER) (void* pvShutdownHandlerContext);
-class ASPNETCORE_APPLICATION
+#include "application.h"
+
+class IN_PROCESS_APPLICATION : public APPLICATION
{
public:
+ IN_PROCESS_APPLICATION();
- ASPNETCORE_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;
- }
- }
+ ~IN_PROCESS_APPLICATION();
+ __override
HRESULT
- Initialize(
- _In_ ASPNETCORE_CONFIG* pConfig
+ Initialize(_In_ APPLICATION_MANAGER* pApplicationManager,
+ _In_ ASPNETCORE_CONFIG* pConfiguration);
+
+ VOID
+ Recycle(
+ VOID
);
+ __override
+ VOID OnAppOfflineHandleChange();
+
+ __override
REQUEST_NOTIFICATION_STATUS
ExecuteRequest(
_In_ IHttpContext* pHttpContext
);
- VOID
- Shutdown(
- VOID
- );
-
VOID
SetCallbackHandles(
_In_ PFN_REQUEST_HANDLER request_callback,
@@ -61,16 +49,13 @@ public:
VOID
);
- ASPNETCORE_CONFIG*
- GetConfig(
- VOID
- )
- {
- return m_pConfiguration;
- }
+ HRESULT
+ LoadManagedApplication(
+ VOID
+ );
static
- ASPNETCORE_APPLICATION*
+ IN_PROCESS_APPLICATION*
GetInstance(
VOID
)
@@ -78,13 +63,11 @@ public:
return s_Application;
}
+
private:
// Thread executing the .NET Core process
HANDLE m_hThread;
- // Configuration for this application
- ASPNETCORE_CONFIG* m_pConfiguration;
-
// The request handler callback from managed code
PFN_REQUEST_HANDLER m_RequestHandler;
VOID* m_RequstHandlerContext;
@@ -99,29 +82,41 @@ private:
// The exit code of the .NET Core process
INT m_ProcessExitCode;
- static ASPNETCORE_APPLICATION* s_Application;
+ BOOL m_fManagedAppLoaded;
+ BOOL m_fLoadManagedAppError;
- static VOID
- FindDotNetFolders(
- _In_ STRU *pstrPath,
- _Out_ std::vector *pvFolders
- );
+ static IN_PROCESS_APPLICATION* s_Application;
- static HRESULT
- FindHighestDotNetVersion(
- _In_ std::vector vFolders,
- _Out_ STRU *pstrResult
- );
+ static
+ VOID
+ FindDotNetFolders(
+ _In_ PCWSTR pszPath,
+ _Out_ std::vector *pvFolders
+ );
+
+ static
+ HRESULT
+ FindHighestDotNetVersion(
+ _In_ std::vector vFolders,
+ _Out_ STRU *pstrResult
+ );
+
+ static
+ BOOL
+ DirectoryExists(
+ _In_ STRU *pstrPath //todo: this does not need to be stru, can be PCWSTR
+ );
static BOOL
- DirectoryExists(
- _In_ STRU *pstrPath
- );
+ GetEnv(
+ _In_ PCWSTR pszEnvironmentVariable,
+ _Out_ STRU *pstrResult
+ );
- static BOOL
- GetEnv(
- _In_ PCWSTR pszEnvironmentVariable,
- _Out_ STRU *pstrResult
- );
-};
+ static
+ VOID
+ ExecuteAspNetCoreProcess(
+ _In_ LPVOID pContext
+ );
+};
\ No newline at end of file
diff --git a/src/AspNetCore/Inc/outprocessapplication.h b/src/AspNetCore/Inc/outprocessapplication.h
new file mode 100644
index 0000000000..57e6022c0c
--- /dev/null
+++ b/src/AspNetCore/Inc/outprocessapplication.h
@@ -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;
+};
diff --git a/src/AspNetCore/Inc/resource.h b/src/AspNetCore/Inc/resource.h
index a7f1b30543..f2982b2a27 100644
--- a/src/AspNetCore/Inc/resource.h
+++ b/src/AspNetCore/Inc/resource.h
@@ -17,4 +17,7 @@
#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_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'."
\ No newline at end of file
diff --git a/src/AspNetCore/Src/application.cxx b/src/AspNetCore/Src/application.cxx
index b723162814..2bd5b688b8 100644
--- a/src/AspNetCore/Src/application.cxx
+++ b/src/AspNetCore/Src/application.cxx
@@ -20,92 +20,16 @@ APPLICATION::~APPLICATION()
m_pFileWatcherEntry->StopMonitor();
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
APPLICATION::StartMonitoringAppOffline()
{
HRESULT hr = S_OK;
-
- hr = m_pFileWatcherEntry->Create(m_strAppPhysicalPath.QueryStr(), L"app_offline.htm", this, NULL);
-
+ if (m_pFileWatcherEntry != NULL)
+ {
+ hr = m_pFileWatcherEntry->Create(m_pConfiguration->QueryApplicationFullPath()->QueryStr(), L"app_offline.htm", this, NULL);
+ }
return hr;
}
@@ -113,7 +37,7 @@ VOID
APPLICATION::UpdateAppOfflineFileHandle()
{
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 *pNewAppOfflineHtm = NULL;
@@ -124,18 +48,6 @@ APPLICATION::UpdateAppOfflineFileHandle()
else
{
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());
if ( pNewAppOfflineHtm != NULL )
@@ -160,5 +72,7 @@ APPLICATION::UpdateAppOfflineFileHandle()
pNewAppOfflineHtm = NULL;
}
}
+
+ OnAppOfflineHandleChange();
}
}
\ No newline at end of file
diff --git a/src/AspNetCore/Src/applicationmanager.cxx b/src/AspNetCore/Src/applicationmanager.cxx
index 6d288af7b3..6c2ce6d235 100644
--- a/src/AspNetCore/Src/applicationmanager.cxx
+++ b/src/AspNetCore/Src/applicationmanager.cxx
@@ -8,6 +8,7 @@ APPLICATION_MANAGER* APPLICATION_MANAGER::sm_pApplicationManager = NULL;
HRESULT
APPLICATION_MANAGER::GetApplication(
_In_ IHttpContext* pContext,
+ _In_ ASPNETCORE_CONFIG* pConfig,
_Out_ APPLICATION ** ppApplication
)
{
@@ -15,12 +16,17 @@ APPLICATION_MANAGER::GetApplication(
APPLICATION *pApplication = NULL;
APPLICATION_KEY key;
BOOL fExclusiveLock = FALSE;
+ BOOL fMixedHostingModelError = FALSE;
+ BOOL fDuplicatedInProcessApp = FALSE;
PCWSTR pszApplicationId = NULL;
+ LPCWSTR apsz[1];
+ STACK_STRU ( strEventMsg, 256 );
*ppApplication = NULL;
DBG_ASSERT(pContext != NULL);
DBG_ASSERT(pContext->GetApplication() != NULL);
+
pszApplicationId = pContext->GetApplication()->GetApplicationId();
hr = key.Initialize(pszApplicationId);
@@ -33,8 +39,28 @@ APPLICATION_MANAGER::GetApplication(
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)
{
hr = E_OUTOFMEMORY;
@@ -53,18 +79,39 @@ APPLICATION_MANAGER::GetApplication(
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))
{
goto Finished;
}
hr = m_pApplicationHash->InsertRecord( pApplication );
-
if (FAILED(hr))
{
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);
fExclusiveLock = FALSE;
@@ -76,8 +123,10 @@ APPLICATION_MANAGER::GetApplication(
Finished:
- if (fExclusiveLock == TRUE)
+ if (fExclusiveLock)
+ {
ReleaseSRWLockExclusive(&m_srwLock);
+ }
if (FAILED(hr))
{
@@ -86,12 +135,77 @@ Finished:
pApplication->DereferenceApplication();
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;
}
-
HRESULT
APPLICATION_MANAGER::RecycleApplication(
_In_ LPCWSTR pszApplication
diff --git a/src/AspNetCore/Src/aspnetcore_msg.mc b/src/AspNetCore/Src/aspnetcore_msg.mc
index 09cc4615e0..cc3d5d1c9c 100644
--- a/src/AspNetCore/Src/aspnetcore_msg.mc
+++ b/src/AspNetCore/Src/aspnetcore_msg.mc
@@ -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:
;
@@ -67,6 +68,30 @@ Language=English
%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_
;
\ No newline at end of file
diff --git a/src/AspNetCore/Src/aspnetcoreconfig.cxx b/src/AspNetCore/Src/aspnetcoreconfig.cxx
index 26ed858cdc..a63c3bf1ae 100644
--- a/src/AspNetCore/Src/aspnetcoreconfig.cxx
+++ b/src/AspNetCore/Src/aspnetcoreconfig.cxx
@@ -5,24 +5,37 @@
ASPNETCORE_CONFIG::~ASPNETCORE_CONFIG()
{
- //
- // the destructor will be called once IIS decides to recycle the module context (i.e., application)
- //
- // shutting down core application first
- if (ASPNETCORE_APPLICATION::GetInstance() != NULL) {
- ASPNETCORE_APPLICATION::GetInstance()->Shutdown();
- }
- m_struApplicationFullPath.Reset();
- if (!m_struApplication.IsEmpty())
+ if (QueryHostingModel() == HOSTING_IN_PROCESS &&
+ !g_fRecycleProcessCalled &&
+ !g_pHttpServer->IsCommandLineLaunch())
{
- 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)
{
m_pEnvironmentVariables->Clear();
delete m_pEnvironmentVariables;
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
@@ -124,7 +137,6 @@ ASPNETCORE_CONFIG::Populate(
STRU strEnvValue;
STRU strExpandedEnvValue;
STRU strApplicationFullPath;
- STRU strHostingModel;
IAppHostAdminManager *pAdminManager = NULL;
IAppHostElement *pAspNetCoreElement = NULL;
IAppHostElement *pWindowsAuthenticationElement = NULL;
@@ -238,19 +250,27 @@ ASPNETCORE_CONFIG::Populate(
hr = GetElementStringProperty(pAspNetCoreElement,
CS_ASPNETCORE_HOSTING_MODEL,
- &strHostingModel);
+ &m_strHostingModel);
if (FAILED(hr))
{
+ // Swallow this error for backward compatability
+ // Use default behavior for empty string
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,
diff --git a/src/AspNetCore/Src/dllmain.cpp b/src/AspNetCore/Src/dllmain.cpp
index e510dc535d..8de9227579 100644
--- a/src/AspNetCore/Src/dllmain.cpp
+++ b/src/AspNetCore/Src/dllmain.cpp
@@ -15,6 +15,7 @@ HTTP_MODULE_ID g_pModuleId = NULL;
IHttpServer * g_pHttpServer = NULL;
BOOL g_fAsyncDisconnectAvailable = FALSE;
BOOL g_fWinHttpNonBlockingCallbackAvailable = FALSE;
+BOOL g_fRecycleProcessCalled = FALSE;
PCWSTR g_pszModuleName = NULL;
HINSTANCE g_hModule;
HINSTANCE g_hWinHttpModule;
diff --git a/src/AspNetCore/Src/forwardinghandler.cxx b/src/AspNetCore/Src/forwardinghandler.cxx
index 665a82d8be..6023449b9f 100644
--- a/src/AspNetCore/Src/forwardinghandler.cxx
+++ b/src/AspNetCore/Src/forwardinghandler.cxx
@@ -23,10 +23,12 @@ TRACE_LOG * FORWARDING_HANDLER::sm_pTraceLog = NULL;
PROTOCOL_CONFIG FORWARDING_HANDLER::sm_ProtocolConfig;
FORWARDING_HANDLER::FORWARDING_HANDLER(
- __in IHttpContext * pW3Context
+ __in IHttpContext * pW3Context,
+ __in APPLICATION * pApplication
) : m_Signature(FORWARDING_HANDLER_SIGNATURE),
m_cRefs(1),
m_pW3Context(pW3Context),
+m_pApplication(pApplication),
m_pChildRequestContext(NULL),
m_hRequest(NULL),
m_fHandleClosedDueToClient(FALSE),
@@ -48,7 +50,6 @@ m_cchHeaders(0),
m_fWebSocketEnabled(FALSE),
m_cContentLength(0),
m_pWebSocket(NULL),
-m_pApplication(NULL),
m_pAppOfflineHtm(NULL),
m_fErrorHandled(FALSE),
m_fWebSocketUpgrade(FALSE),
@@ -120,13 +121,13 @@ FORWARDING_HANDLER::~FORWARDING_HANDLER(
m_hRequest = NULL;
}
- if(m_pApplication != NULL)
+ if (m_pApplication != NULL)
{
m_pApplication->DereferenceApplication();
m_pApplication = NULL;
}
- if(m_pAppOfflineHtm != NULL)
+ if (m_pAppOfflineHtm != NULL)
{
m_pAppOfflineHtm->DereferenceAppOfflineHtm();
m_pAppOfflineHtm = NULL;
@@ -262,7 +263,8 @@ FORWARDING_HANDLER::SetStatusAndHeaders(
((*pchEndofHeaderValue == ' ') ||
(*pchEndofHeaderValue == '\r'));
pchEndofHeaderValue--)
- {}
+ {
+ }
//
// Copy the status description
@@ -324,7 +326,8 @@ FORWARDING_HANDLER::SetStatusAndHeaders(
(pchEndofHeaderName >= pszHeaders + index) &&
(*pchEndofHeaderName == ' ');
pchEndofHeaderName--)
- {}
+ {
+ }
pchEndofHeaderName++;
@@ -344,7 +347,8 @@ FORWARDING_HANDLER::SetStatusAndHeaders(
for (index = static_cast(pchColon - pszHeaders) + 1;
pszHeaders[index] == ' ';
index++)
- {}
+ {
+ }
//
@@ -355,7 +359,8 @@ FORWARDING_HANDLER::SetStatusAndHeaders(
((*pchEndofHeaderValue == ' ') ||
(*pchEndofHeaderValue == '\r'));
pchEndofHeaderValue--)
- {}
+ {
+ }
pchEndofHeaderValue++;
@@ -1042,9 +1047,11 @@ FORWARDING_HANDLER::OnExecuteRequestHandler(
USHORT cchHostName = 0;
BOOL fSecure = FALSE;
BOOL fProcessStartFailure = FALSE;
+ BOOL fInternalError = FALSE;
HTTP_DATA_CHUNK *pDataChunk = NULL;
DBG_ASSERT(m_RequestStatus == FORWARDER_START);
+ DBG_ASSERT(m_pApplication);
//
// Take a reference so that object does not go away as a result of
@@ -1052,37 +1059,11 @@ FORWARDING_HANDLER::OnExecuteRequestHandler(
//
ReferenceForwardingHandler();
- m_pszOriginalHostHeader = pRequest->GetHeader(HttpHeaderHost, &cchHostName);
-
- // read per site aspNetCore configuration.
- hr = ASPNETCORE_CONFIG::GetConfig(m_pW3Context, &pAspNetCoreConfig);
- if (FAILED(hr))
- {
- // configuration error.
- goto Failure;
- }
-
- // override Protocol related config from aspNetCore config
- pProtocol->OverrideConfig(pAspNetCoreConfig);
-
- //
- // parse original url
- //
- if (FAILED(hr = PATH::SplitUrl(pRequest->GetRawHttpRequest()->CookedUrl.pFullUrl,
- &fSecure,
- &strDestination,
- &strUrl)))
- {
- goto Failure;
- }
-
- if (FAILED(hr = PATH::EscapeAbsPath(pRequest, &struEscapedUrl)))
- {
- goto Failure;
- }
-
- m_fDoReverseRewriteHeaders = pProtocol->QueryReverseRewriteHeaders();
+ // get application configuation
+ pAspNetCoreConfig = m_pApplication->QueryConfig();
+
+ // check connection
IHttpConnection * pClientConnection = m_pW3Context->GetConnection();
if (pClientConnection == NULL ||
!pClientConnection->IsConnected())
@@ -1091,26 +1072,7 @@ FORWARDING_HANDLER::OnExecuteRequestHandler(
goto Failure;
}
- m_cMinBufferLimit = pProtocol->QueryMinResponseBuffer();
-
- //
- // Find the application that is supposed to service this request.
- //
-
- pApplicationManager = APPLICATION_MANAGER::GetInstance();
- if (pApplicationManager == NULL)
- {
- hr = E_OUTOFMEMORY;
- goto Failure;
- }
-
- hr = pApplicationManager->GetApplication(m_pW3Context,
- &m_pApplication);
- if (FAILED(hr))
- {
- goto Failure;
- }
-
+ // check offline
m_pAppOfflineHtm = m_pApplication->QueryAppOfflineHtm();
if (m_pAppOfflineHtm != NULL)
{
@@ -1176,196 +1138,200 @@ FORWARDING_HANDLER::OnExecuteRequestHandler(
goto Finished;
}
- hr = m_pApplication->GetProcess(m_pW3Context,
- pAspNetCoreConfig,
- &pServerProcess);
- if (FAILED(hr))
+ switch (pAspNetCoreConfig->QueryHostingModel())
{
- fProcessStartFailure = TRUE;
- goto Failure;
- }
-
- if (pServerProcess == NULL)
+ case HOSTING_IN_PROCESS:
{
- hr = HRESULT_FROM_WIN32(ERROR_CREATE_FAILED);
- goto Failure;
- }
+ // Allow reading and writing to simultaneously
+ ((IHttpContext3*)m_pW3Context)->EnableFullDuplex();
- if (pServerProcess->QueryWinHttpConnection() == NULL)
- {
- hr = HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE);
- goto Failure;
- }
+ // Disable response buffering by default, we'll do a write behind buffering in managed code
+ ((IHttpResponse2*)m_pW3Context->GetResponse())->DisableBuffering();
- hConnect = pServerProcess->QueryWinHttpConnection()->QueryHandle();
-
- //
- // Mark request as websocket if upgrade header is present.
- //
-
- if (g_fWebSocketSupported)
- {
- USHORT cchHeader = 0;
- PCSTR pszWebSocketHeader = pRequest->GetHeader("Upgrade", &cchHeader);
-
- if (cchHeader == 9 && _stricmp(pszWebSocketHeader, "websocket") == 0)
+ hr = ((IN_PROCESS_APPLICATION*)m_pApplication)->LoadManagedApplication();
+ if (FAILED(hr))
{
- m_fWebSocketEnabled = TRUE;
+ fInternalError = TRUE;
+ goto Failure;
}
+ return m_pApplication->ExecuteRequest(m_pW3Context);
}
-
- hr = CreateWinHttpRequest(pRequest,
- pProtocol,
- hConnect,
- &struEscapedUrl,
- pAspNetCoreConfig,
- pServerProcess);
-
- if (FAILED(hr))
+ case HOSTING_OUT_PROCESS:
{
- goto Failure;
- }
+ m_pszOriginalHostHeader = pRequest->GetHeader(HttpHeaderHost, &cchHostName);
- //
- // Register for connection disconnect notification with http.sys.
- // N.B. This feature is currently disabled due to synchronization conditions.
- //
+ // override Protocol related config from aspNetCore config
+ pProtocol->OverrideConfig(pAspNetCoreConfig);
- // disabling this disconnect notification as it causes synchronization/AV issue
- // will re-enable it in the future after investigation
+ //
+ // parse original url
+ //
+ if (FAILED(hr = PATH::SplitUrl(pRequest->GetRawHttpRequest()->CookedUrl.pFullUrl,
+ &fSecure,
+ &strDestination,
+ &strUrl)))
+ {
+ goto Failure;
+ }
- //if (g_fAsyncDisconnectAvailable)
- //{
- // m_pDisconnect = static_cast(
- // pClientConnection->GetModuleContextContainer()->
- // GetConnectionModuleContext(g_pModuleId));
- // if (m_pDisconnect == NULL)
- // {
- // m_pDisconnect = new ASYNC_DISCONNECT_CONTEXT;
- // if (m_pDisconnect == NULL)
- // {
- // hr = E_OUTOFMEMORY;
- // goto Failure;
- // }
+ if (FAILED(hr = PATH::EscapeAbsPath(pRequest, &struEscapedUrl)))
+ {
+ goto Failure;
+ }
- // hr = pClientConnection->GetModuleContextContainer()->
- // SetConnectionModuleContext(m_pDisconnect,
- // g_pModuleId);
- // DBG_ASSERT(hr != HRESULT_FROM_WIN32(ERROR_ALREADY_ASSIGNED));
- // if (FAILED(hr))
- // {
- // goto Failure;
- // }
- // }
+ m_fDoReverseRewriteHeaders = pProtocol->QueryReverseRewriteHeaders();
+ m_cMinBufferLimit = pProtocol->QueryMinResponseBuffer();
+ hr = ((OUT_OF_PROCESS_APPLICATION*)m_pApplication)->GetProcess(
+ m_pW3Context,
+ &pServerProcess);
+ if (FAILED(hr))
+ {
+ fProcessStartFailure = TRUE;
+ goto Failure;
+ }
- // //
- // // Issue: There is a window of opportunity to miss on the disconnect
- // // notification if it happens before the SetHandler() call is made.
- // // It is suboptimal for performance, but should functionally be OK.
- // //
+ if (pServerProcess == NULL)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_CREATE_FAILED);
+ goto Failure;
+ }
- // m_pDisconnect->SetHandler(this);
- //}
+ if (pServerProcess->QueryWinHttpConnection() == NULL)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE);
+ goto Failure;
+ }
- //
- // Read lock on the WinHTTP handle to protect from server closing
- // the handle while it is in use.
- //
+ hConnect = pServerProcess->QueryWinHttpConnection()->QueryHandle();
- AcquireSRWLockShared(&m_RequestLock);
- fRequestLocked = TRUE;
+ //
+ // Mark request as websocket if upgrade header is present.
+ //
- if (m_hRequest == NULL)
- {
- hr = HRESULT_FROM_WIN32(WSAECONNRESET);
- goto Failure;
- }
+ if (g_fWebSocketSupported)
+ {
+ USHORT cchHeader = 0;
+ PCSTR pszWebSocketHeader = pRequest->GetHeader("Upgrade", &cchHeader);
- //
- // Begins normal request handling. Send request to server.
- //
+ if (cchHeader == 9 && _stricmp(pszWebSocketHeader, "websocket") == 0)
+ {
+ m_fWebSocketEnabled = TRUE;
+ }
+ }
- m_RequestStatus = FORWARDER_SENDING_REQUEST;
+ hr = CreateWinHttpRequest(pRequest,
+ pProtocol,
+ hConnect,
+ &struEscapedUrl,
+ pAspNetCoreConfig,
+ pServerProcess);
- //
- // Calculate the bytes to receive from the content length.
- //
+ if (FAILED(hr))
+ {
+ goto Failure;
+ }
- DWORD cbContentLength = 0;
- PCSTR pszContentLength = pRequest->GetHeader(HttpHeaderContentLength);
- if (pszContentLength != NULL)
- {
- cbContentLength = m_BytesToReceive = atol(pszContentLength);
- if (m_BytesToReceive == INFINITE)
+ AcquireSRWLockShared(&m_RequestLock);
+ fRequestLocked = TRUE;
+
+ if (m_hRequest == NULL)
{
hr = HRESULT_FROM_WIN32(WSAECONNRESET);
goto Failure;
}
- }
- else if (pRequest->GetHeader(HttpHeaderTransferEncoding) != NULL)
- {
- m_BytesToReceive = INFINITE;
- }
- if (m_fWebSocketEnabled)
- {
//
- // Set the upgrade flag for a websocket request.
+ // Begins normal request handling. Send request to server.
//
- if (!WinHttpSetOption(m_hRequest,
- WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET,
+ m_RequestStatus = FORWARDER_SENDING_REQUEST;
+
+ //
+ // Calculate the bytes to receive from the content length.
+ //
+
+ DWORD cbContentLength = 0;
+ PCSTR pszContentLength = pRequest->GetHeader(HttpHeaderContentLength);
+ if (pszContentLength != NULL)
+ {
+ cbContentLength = m_BytesToReceive = atol(pszContentLength);
+ if (m_BytesToReceive == INFINITE)
+ {
+ hr = HRESULT_FROM_WIN32(WSAECONNRESET);
+ goto Failure;
+ }
+ }
+ else if (pRequest->GetHeader(HttpHeaderTransferEncoding) != NULL)
+ {
+ m_BytesToReceive = INFINITE;
+ }
+
+ if (m_fWebSocketEnabled)
+ {
+ //
+ // Set the upgrade flag for a websocket request.
+ //
+
+ if (!WinHttpSetOption(m_hRequest,
+ WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET,
+ NULL,
+ 0))
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ goto Finished;
+ }
+ }
+
+ m_cchLastSend = m_cchHeaders;
+
+ //
+ // Remember the handler being processed in the current thread
+ // before staring a WinHTTP operation.
+ //
+
+ DBG_ASSERT(fRequestLocked);
+ DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == NULL);
+ TlsSetValue(g_dwTlsIndex, this);
+ DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == this);
+
+ //
+ // WinHttpSendRequest can operate asynchronously.
+ //
+ // Take reference so that object does not go away as a result of
+ // async completion.
+ //
+ ReferenceForwardingHandler();
+ if (!WinHttpSendRequest(m_hRequest,
+ m_pszHeaders,
+ m_cchHeaders,
NULL,
- 0))
+ 0,
+ cbContentLength,
+ reinterpret_cast(static_cast(this))))
{
hr = HRESULT_FROM_WIN32(GetLastError());
- goto Finished;
+ DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO,
+ "FORWARDING_HANDLER::OnExecuteRequestHandler, Send request failed");
+ DereferenceForwardingHandler();
+ goto Failure;
}
+
+ //
+ // Async WinHTTP operation is in progress. Release this thread meanwhile,
+ // OnWinHttpCompletion method should resume the work by posting an IIS completion.
+ //
+ retVal = RQ_NOTIFICATION_PENDING;
+ goto Finished;
+
}
- m_cchLastSend = m_cchHeaders;
-
- //
- // Remember the handler being processed in the current thread
- // before staring a WinHTTP operation.
- //
-
- DBG_ASSERT(fRequestLocked);
- DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == NULL);
- TlsSetValue(g_dwTlsIndex, this);
- DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == this);
-
- //
- // WinHttpSendRequest can operate asynchronously.
- //
- // Take reference so that object does not go away as a result of
- // async completion.
- //
- ReferenceForwardingHandler();
- if (!WinHttpSendRequest(m_hRequest,
- m_pszHeaders,
- m_cchHeaders,
- NULL,
- 0,
- cbContentLength,
- reinterpret_cast(static_cast(this))))
- {
- hr = HRESULT_FROM_WIN32(GetLastError());
- DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO,
- "FORWARDING_HANDLER::OnExecuteRequestHandler, Send request failed");
- DereferenceForwardingHandler();
+ default:
+ hr = E_UNEXPECTED;
+ fInternalError = TRUE;
goto Failure;
}
- //
- // Async WinHTTP operation is in progress. Release this thread meanwhile,
- // OnWinHttpCompletion method should resume the work by posting an IIS completion.
- //
- retVal = RQ_NOTIFICATION_PENDING;
- goto Finished;
-
Failure:
-
//
// Reset status for consistency.
//
@@ -1384,6 +1350,12 @@ Failure:
pResponse->SetStatus(400, "Bad Request", 0, hr);
goto Finished;
}
+ else if (fInternalError)
+ {
+ // set a special sub error code indicating loading application error
+ pResponse->SetStatus(500, "Internal Server Error", 19, hr);
+ goto Finished;
+ }
else if (fProcessStartFailure && !pAspNetCoreConfig->QueryDisableStartUpErrorPage())
{
PCSTR pszANCMHeader;
@@ -1476,7 +1448,6 @@ Failure:
}
Finished:
-
if (pConnection != NULL)
{
pConnection->DereferenceForwarderConnection();
@@ -1511,7 +1482,6 @@ Finished:
//
// Do not use this object after dereferencing it, it may be gone.
//
-
return retVal;
}
@@ -1564,239 +1534,242 @@ REQUEST_NOTIFICATION_STATUS
reinterpret_cast(static_cast(hrCompletionStatus)));
}
- //
- // Take a reference so that object does not go away as a result of
- // async completion.
- //
- // Read lock on the WinHTTP handle to protect from server closing
- // the handle while it is in use.
- //
- ReferenceForwardingHandler();
-
- DBG_ASSERT(m_pW3Context != NULL);
- __analysis_assume(m_pW3Context != NULL);
-
- //
- // OnAsyncCompletion can be called on a Winhttp io completion thread.
- // Hence we need to check the TLS before we acquire the shared lock.
- //
-
- if (TlsGetValue(g_dwTlsIndex) != this)
+ if (m_pApplication->QueryConfig()->QueryHostingModel() == HOSTING_OUT_PROCESS)
{
- DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == NULL);
- AcquireSRWLockShared(&m_RequestLock);
- TlsSetValue(g_dwTlsIndex, this);
- DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == this);
+ //
+ // Take a reference so that object does not go away as a result of
+ // async completion.
+ //
+ // Read lock on the WinHTTP handle to protect from server closing
+ // the handle while it is in use.
+ //
+ ReferenceForwardingHandler();
- fLocked = TRUE;
- }
+ DBG_ASSERT(m_pW3Context != NULL);
+ __analysis_assume(m_pW3Context != NULL);
- if (m_hRequest == NULL)
- {
- if (m_RequestStatus == FORWARDER_DONE && m_fFinishRequest)
+ //
+ // OnAsyncCompletion can be called on a Winhttp io completion thread.
+ // Hence we need to check the TLS before we acquire the shared lock.
+ //
+
+ if (TlsGetValue(g_dwTlsIndex) != this)
{
- if (m_fHasError)
+ DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == NULL);
+ AcquireSRWLockShared(&m_RequestLock);
+ TlsSetValue(g_dwTlsIndex, this);
+ DBG_ASSERT(TlsGetValue(g_dwTlsIndex) == this);
+
+ fLocked = TRUE;
+ }
+
+ if (m_hRequest == NULL)
+ {
+ if (m_RequestStatus == FORWARDER_DONE && m_fFinishRequest)
{
- retVal = RQ_NOTIFICATION_FINISH_REQUEST;
+ if (m_fHasError)
+ {
+ retVal = RQ_NOTIFICATION_FINISH_REQUEST;
+ }
+ goto Finished;
}
+
+ fClientError = m_fHandleClosedDueToClient;
+ goto Failure;
+ }
+ else if (m_RequestStatus == FORWARDER_RECEIVED_WEBSOCKET_RESPONSE)
+ {
+ DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO,
+ "FORWARDING_HANDLER::OnAsyncCompletion, Send completed for 101 response");
+ //
+ // This should be the write completion of the 101 response.
+ //
+
+ m_pWebSocket = new WEBSOCKET_HANDLER();
+ if (m_pWebSocket == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto Finished;
+ }
+
+ hr = m_pWebSocket->ProcessRequest(this, m_pW3Context, m_hRequest);
+ if (FAILED(hr))
+ {
+ goto Failure;
+ }
+
+ //
+ // WebSocket upgrade is successful. Close the WinHttpRequest Handle
+ //
+ WinHttpSetStatusCallback(m_hRequest,
+ FORWARDING_HANDLER::OnWinHttpCompletion,
+ WINHTTP_CALLBACK_FLAG_HANDLES,
+ NULL);
+ fClosed = WinHttpCloseHandle(m_hRequest);
+ DBG_ASSERT(fClosed);
+ if (fClosed)
+ {
+ m_fWebSocketUpgrade = TRUE;
+ m_hRequest = NULL;
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ goto Failure;
+ }
+ retVal = RQ_NOTIFICATION_PENDING;
+ goto Finished;
+ }
+ else if (m_RequestStatus == FORWARDER_RESET_CONNECTION)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_INVALID_SERVER_RESPONSE);
+ goto Failure;
+
+ }
+
+ //
+ // Begins normal completion handling. There is already a shared acquired
+ // for protecting the WinHTTP request handle from being closed.
+ //
+ switch (m_RequestStatus)
+ {
+ case FORWARDER_RECEIVING_RESPONSE:
+
+ //
+ // This is a completion of a write (send) to http.sys, abort in case of
+ // failure, if there is more data available from WinHTTP, read it
+ // or else ask if there is more.
+ //
+ if (FAILED(hrCompletionStatus))
+ {
+ hr = hrCompletionStatus;
+ fClientError = TRUE;
+ goto Failure;
+ }
+
+ hr = OnReceivingResponse();
+ if (FAILED(hr))
+ {
+ goto Failure;
+ }
+ break;
+
+ case FORWARDER_SENDING_REQUEST:
+
+ hr = OnSendingRequest(cbCompletion,
+ hrCompletionStatus,
+ &fClientError);
+ if (FAILED(hr))
+ {
+ goto Failure;
+ }
+ break;
+
+ default:
+ DBG_ASSERT(m_RequestStatus == FORWARDER_DONE);
goto Finished;
}
- fClientError = m_fHandleClosedDueToClient;
- goto Failure;
- }
- else if (m_RequestStatus == FORWARDER_RECEIVED_WEBSOCKET_RESPONSE)
- {
- DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO,
- "FORWARDING_HANDLER::OnAsyncCompletion, Send completed for 101 response");
//
- // This should be the write completion of the 101 response.
+ // Either OnReceivingResponse or OnSendingRequest initiated an
+ // async WinHTTP operation, release this thread meanwhile,
+ // OnWinHttpCompletion method should resume the work by posting an IIS completion.
//
-
- m_pWebSocket = new WEBSOCKET_HANDLER();
- if (m_pWebSocket == NULL)
- {
- hr = E_OUTOFMEMORY;
- goto Finished;
- }
-
- hr = m_pWebSocket->ProcessRequest(this, m_pW3Context, m_hRequest);
- if (FAILED(hr))
- {
- goto Failure;
- }
-
- //
- // WebSocket upgrade is successful. Close the WinHttpRequest Handle
- //
- WinHttpSetStatusCallback(m_hRequest,
- FORWARDING_HANDLER::OnWinHttpCompletion,
- WINHTTP_CALLBACK_FLAG_HANDLES,
- NULL);
- fClosed = WinHttpCloseHandle(m_hRequest);
- DBG_ASSERT(fClosed);
- if (fClosed)
- {
- m_fWebSocketUpgrade = TRUE;
- m_hRequest = NULL;
- }
- else
- {
- hr = HRESULT_FROM_WIN32(GetLastError());
- goto Failure;
- }
retVal = RQ_NOTIFICATION_PENDING;
goto Finished;
- }
- else if (m_RequestStatus == FORWARDER_RESET_CONNECTION)
- {
- hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_INVALID_SERVER_RESPONSE);
- goto Failure;
- }
-
- //
- // Begins normal completion handling. There is already a shared acquired
- // for protecting the WinHTTP request handle from being closed.
- //
- switch (m_RequestStatus)
- {
- case FORWARDER_RECEIVING_RESPONSE:
+ Failure:
//
- // This is a completion of a write (send) to http.sys, abort in case of
- // failure, if there is more data available from WinHTTP, read it
- // or else ask if there is more.
+ // Reset status for consistency.
//
- if (FAILED(hrCompletionStatus))
+ m_RequestStatus = FORWARDER_DONE;
+ m_fHasError = TRUE;
+ //
+ // Do the right thing based on where the error originated from.
+ //
+ IHttpResponse *pResponse = m_pW3Context->GetResponse();
+ pResponse->DisableKernelCache();
+ pResponse->GetRawHttpResponse()->EntityChunkCount = 0;
+
+ // double check to set right status code
+ if (!m_pW3Context->GetConnection()->IsConnected())
{
- hr = hrCompletionStatus;
fClientError = TRUE;
- goto Failure;
}
- hr = OnReceivingResponse();
- if (FAILED(hr))
+ if (fClientError)
{
- goto Failure;
- }
- break;
-
- case FORWARDER_SENDING_REQUEST:
-
- hr = OnSendingRequest(cbCompletion,
- hrCompletionStatus,
- &fClientError);
- if (FAILED(hr))
- {
- goto Failure;
- }
- break;
-
- default:
- DBG_ASSERT(m_RequestStatus == FORWARDER_DONE);
- goto Finished;
- }
-
- //
- // Either OnReceivingResponse or OnSendingRequest initiated an
- // async WinHTTP operation, release this thread meanwhile,
- // OnWinHttpCompletion method should resume the work by posting an IIS completion.
- //
- retVal = RQ_NOTIFICATION_PENDING;
- goto Finished;
-
-Failure:
-
- //
- // Reset status for consistency.
- //
- m_RequestStatus = FORWARDER_DONE;
- m_fHasError = TRUE;
- //
- // Do the right thing based on where the error originated from.
- //
- IHttpResponse *pResponse = m_pW3Context->GetResponse();
- pResponse->DisableKernelCache();
- pResponse->GetRawHttpResponse()->EntityChunkCount = 0;
-
- // double check to set right status code
- if (!m_pW3Context->GetConnection()->IsConnected())
- {
- fClientError = TRUE;
- }
-
- if (fClientError)
- {
- if (!m_fResponseHeadersReceivedAndSet)
- {
- pResponse->SetStatus(400, "Bad Request", 0, HRESULT_FROM_WIN32(WSAECONNRESET));
+ if (!m_fResponseHeadersReceivedAndSet)
+ {
+ pResponse->SetStatus(400, "Bad Request", 0, HRESULT_FROM_WIN32(WSAECONNRESET));
+ }
+ else
+ {
+ //
+ // Response headers from origin server were
+ // already received and set for the current response.
+ // Honor the response status.
+ //
+ }
}
else
{
- //
- // Response headers from origin server were
- // already received and set for the current response.
- // Honor the response status.
- //
- }
- }
- else
- {
- STACK_STRU(strDescription, 128);
+ STACK_STRU(strDescription, 128);
- pResponse->SetStatus(502, "Bad Gateway", 3, hr);
+ pResponse->SetStatus(502, "Bad Gateway", 3, hr);
- if (hr > HRESULT_FROM_WIN32(WINHTTP_ERROR_BASE) &&
- hr <= HRESULT_FROM_WIN32(WINHTTP_ERROR_LAST))
- {
+ if (hr > HRESULT_FROM_WIN32(WINHTTP_ERROR_BASE) &&
+ hr <= HRESULT_FROM_WIN32(WINHTTP_ERROR_LAST))
+ {
#pragma prefast (suppress : __WARNING_FUNCTION_NEEDS_REVIEW, "Function and parameters reviewed.")
- FormatMessage(
- FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
- g_hWinHttpModule,
- HRESULT_CODE(hr),
- 0,
- strDescription.QueryStr(),
- strDescription.QuerySizeCCH(),
- NULL);
- }
- else
- {
- LoadString(g_hModule,
- IDS_SERVER_ERROR,
- strDescription.QueryStr(),
- strDescription.QuerySizeCCH());
- }
- (VOID)strDescription.SyncWithBuffer();
- if (strDescription.QueryCCH() != 0)
- {
- pResponse->SetErrorDescription(
- strDescription.QueryStr(),
- strDescription.QueryCCH(),
- FALSE);
+ FormatMessage(
+ FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
+ g_hWinHttpModule,
+ HRESULT_CODE(hr),
+ 0,
+ strDescription.QueryStr(),
+ strDescription.QuerySizeCCH(),
+ NULL);
+ }
+ else
+ {
+ LoadString(g_hModule,
+ IDS_SERVER_ERROR,
+ strDescription.QueryStr(),
+ strDescription.QuerySizeCCH());
+ }
+ (VOID)strDescription.SyncWithBuffer();
+ if (strDescription.QueryCCH() != 0)
+ {
+ pResponse->SetErrorDescription(
+ strDescription.QueryStr(),
+ strDescription.QueryCCH(),
+ FALSE);
+ }
+
+ if (hr == HRESULT_FROM_WIN32(ERROR_WINHTTP_INVALID_SERVER_RESPONSE))
+ {
+ pResponse->ResetConnection();
+ goto Finished;
+ }
}
- if (hr == HRESULT_FROM_WIN32(ERROR_WINHTTP_INVALID_SERVER_RESPONSE))
+ //
+ // Finish the request on failure.
+ // Let IIS pipeline continue only after receiving handle close callback
+ // from WinHttp. This ensures no more callback from WinHttp
+ //
+ if (m_hRequest != NULL)
{
- pResponse->ResetConnection();
- goto Finished;
+ if (WinHttpCloseHandle(m_hRequest))
+ {
+ m_hRequest = NULL;
+ }
}
+ retVal = RQ_NOTIFICATION_PENDING;
}
- //
- // Finish the request on failure.
- // Let IIS pipeline continue only after receiving handle close callback
- // from WinHttp. This ensures no more callback from WinHttp
- //
- if (m_hRequest != NULL)
- {
- if (WinHttpCloseHandle(m_hRequest))
- {
- m_hRequest = NULL;
- }
- }
- retVal = RQ_NOTIFICATION_PENDING;
-
Finished:
if (fLocked)
@@ -1849,12 +1822,12 @@ FORWARDING_HANDLER::OnSendingRequest(
m_BytesToReceive = 0;
m_cchLastSend = 5; // "0\r\n\r\n"
- //
- // WinHttpWriteData can operate asynchronously.
- //
- // Take reference so that object does not go away as a result of
- // async completion.
- //
+ //
+ // WinHttpWriteData can operate asynchronously.
+ //
+ // Take reference so that object does not go away as a result of
+ // async completion.
+ //
ReferenceForwardingHandler();
if (!WinHttpWriteData(m_hRequest,
"0\r\n\r\n",
@@ -2134,7 +2107,7 @@ None
#endif // DEBUG
fEndRequest = (dwInternetStatus == WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING);
- if (!fEndRequest)
+ if (!fEndRequest)
{
if (!m_pW3Context->GetConnection()->IsConnected())
{
@@ -2169,7 +2142,7 @@ None
fDerefForwardingHandler = FALSE;
fAnotherCompletionExpected = TRUE;
- if(m_pWebSocket == NULL)
+ if (m_pWebSocket == NULL)
{
goto Finished;
}
@@ -2194,8 +2167,8 @@ None
case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
m_pWebSocket->OnWinHttpIoError(
- (WINHTTP_WEB_SOCKET_ASYNC_RESULT*)lpvStatusInformation
- );
+ (WINHTTP_WEB_SOCKET_ASYNC_RESULT*)lpvStatusInformation
+ );
break;
}
goto Finished;
@@ -2255,7 +2228,7 @@ None
case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
if (m_RequestStatus != FORWARDER_DONE)
- {
+ {
hr = ERROR_CONNECTION_ABORTED;
fClientError = m_fHandleClosedDueToClient;
}
@@ -2400,7 +2373,7 @@ Finished:
FORWARDING_HANDLER::OnWinHttpCompletion,
WINHTTP_CALLBACK_FLAG_HANDLES,
NULL);
- if(WinHttpCloseHandle(m_hRequest))
+ if (WinHttpCloseHandle(m_hRequest))
{
m_hRequest = NULL;
}
@@ -2414,7 +2387,7 @@ Finished:
m_pWebSocket->TerminateRequest();
}
- if(fEndRequest)
+ if (fEndRequest)
{
// only postCompletion after WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING
// so that no further WinHttp callback will be called
@@ -2432,7 +2405,7 @@ Finished:
// IndicateCompletion to allow cleaning up the TLS before thread reuse.
//
- m_pW3Context->PostCompletion(0);
+ m_pW3Context->PostCompletion(0);
//
// No code executed after posting the completion.
@@ -3111,7 +3084,7 @@ FORWARDING_HANDLER::TerminateRequest(
FORWARDING_HANDLER::OnWinHttpCompletion,
WINHTTP_CALLBACK_FLAG_HANDLES,
NULL);
- if (WinHttpCloseHandle(m_hRequest))
+ if (WinHttpCloseHandle(m_hRequest))
{
m_hRequest = NULL;
}
diff --git a/src/AspNetCore/Src/aspnetcoreapplication.cxx b/src/AspNetCore/Src/inprocessapplication.cxx
similarity index 59%
rename from src/AspNetCore/Src/aspnetcoreapplication.cxx
rename to src/AspNetCore/Src/inprocessapplication.cxx
index 3e2f727f5a..f9a2f65ff6 100644
--- a/src/AspNetCore/Src/aspnetcoreapplication.cxx
+++ b/src/AspNetCore/Src/inprocessapplication.cxx
@@ -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 "fx_ver.h"
#include
typedef DWORD(*hostfxr_main_fn) (CONST DWORD argc, CONST WCHAR* argv[]);
+//
// Initialization export
-
+//
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
VOID
register_callbacks(
@@ -15,7 +18,7 @@ register_callbacks(
_In_ VOID* pvShutdownHandlerContext
)
{
- ASPNETCORE_APPLICATION::GetInstance()->SetCallbackHandles(
+ IN_PROCESS_APPLICATION::GetInstance()->SetCallbackHandles(
request_handler,
shutdown_handler,
pvRequstHandlerContext,
@@ -81,11 +84,21 @@ http_get_completion_info(
*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
BSTR // TODO probably should make this a wide string
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
@@ -176,311 +189,45 @@ http_flush_response_bytes(
pfCompletionExpected);
return hr;
}
+// End of export
-// Thread execution callback
-static
-VOID
-ExecuteAspNetCoreProcess(
- _In_ LPVOID pContext
-)
+
+IN_PROCESS_APPLICATION* IN_PROCESS_APPLICATION::s_Application = NULL;
+
+IN_PROCESS_APPLICATION::IN_PROCESS_APPLICATION():
+ 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
-ASPNETCORE_APPLICATION::SetCallbackHandles(
- _In_ PFN_REQUEST_HANDLER request_handler,
- _In_ PFN_SHUTDOWN_HANDLER shutdown_handler,
- _In_ VOID* pvRequstHandlerContext,
- _In_ VOID* pvShutdownHandlerContext
-)
+IN_PROCESS_APPLICATION::~IN_PROCESS_APPLICATION()
{
- m_RequestHandler = request_handler;
- 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 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;
+ Recycle();
}
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,
_Out_ STRU *pstrResult
)
{
DWORD dwLength;
- PWSTR pszBuffer= NULL;
+ PWSTR pszBuffer = NULL;
BOOL fSucceeded = FALSE;
if (pszEnvironmentVariable == NULL)
@@ -513,15 +260,15 @@ Finished:
}
VOID
-ASPNETCORE_APPLICATION::FindDotNetFolders(
- _In_ STRU *pstrPath,
+IN_PROCESS_APPLICATION::FindDotNetFolders(
+ _In_ PCWSTR pszPath,
_Out_ std::vector *pvFolders
)
{
HANDLE handle = NULL;
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)
{
return;
@@ -536,8 +283,489 @@ ASPNETCORE_APPLICATION::FindDotNetFolders(
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
-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 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 vFolders,
_Out_ STRU *pstrResult
)
@@ -558,44 +786,3 @@ ASPNETCORE_APPLICATION::FindHighestDotNetVersion(
// we check FAILED(hr) outside of function
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;
-}
\ No newline at end of file
diff --git a/src/AspNetCore/Src/outprocessapplication.cxx b/src/AspNetCore/Src/outprocessapplication.cxx
new file mode 100644
index 0000000000..ce76d3be14
--- /dev/null
+++ b/src/AspNetCore/Src/outprocessapplication.cxx
@@ -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;
+}
\ No newline at end of file
diff --git a/src/AspNetCore/Src/precomp.hxx b/src/AspNetCore/Src/precomp.hxx
index 178c66698f..0e267a301d 100644
--- a/src/AspNetCore/Src/precomp.hxx
+++ b/src/AspNetCore/Src/precomp.hxx
@@ -114,12 +114,13 @@ inline bool IsSpace(char ch)
#include "environmentvariablehash.h"
#include "..\aspnetcore_msg.h"
#include "aspnetcoreconfig.h"
-#include "aspnetcoreapplication.h"
#include "serverprocess.h"
#include "processmanager.h"
#include "filewatcher.h"
#include "application.h"
#include "applicationmanager.h"
+#include "inprocessapplication.h"
+#include "outprocessapplication.h"
#include "resource.h"
#include "path.h"
#include "debugutil.h"
@@ -130,6 +131,7 @@ inline bool IsSpace(char ch)
#include "websockethandler.h"
#include "forwardinghandler.h"
#include "proxymodule.h"
+#include "fx_ver.h"
FORCEINLINE
DWORD
diff --git a/src/AspNetCore/Src/proxymodule.cxx b/src/AspNetCore/Src/proxymodule.cxx
index 9616d8485a..3ee33002d7 100644
--- a/src/AspNetCore/Src/proxymodule.cxx
+++ b/src/AspNetCore/Src/proxymodule.cxx
@@ -78,55 +78,42 @@ CProxyModule::OnExecuteRequestHandler(
IHttpEventProvider *
)
{
- HRESULT hr;
- APPLICATION_MANAGER* pApplicationManager;
- APPLICATION* pApplication;
- ASPNETCORE_CONFIG* config;
- ASPNETCORE_APPLICATION* pAspNetCoreApplication;
- ASPNETCORE_CONFIG::GetConfig(pHttpContext, &config);
-
- if (config->QueryIsOutOfProcess())// case insensitive
+ HRESULT hr = S_OK;
+ ASPNETCORE_CONFIG *pConfig = NULL;
+ APPLICATION_MANAGER *pApplicationManager = NULL;
+ APPLICATION *pApplication = NULL;
+ hr = ASPNETCORE_CONFIG::GetConfig(pHttpContext, &pConfig);
+ if (FAILED(hr))
{
- m_pHandler = new FORWARDING_HANDLER(pHttpContext);
- if (m_pHandler == NULL)
- {
- hr = E_OUTOFMEMORY;
- goto Failed;
- }
-
- return m_pHandler->OnExecuteRequestHandler();
+ goto Failed;
}
- else if (config->QueryIsInProcess())
+
+ pApplicationManager = APPLICATION_MANAGER::GetInstance();
+ if (pApplicationManager == NULL)
{
- pApplicationManager = APPLICATION_MANAGER::GetInstance();
- if (pApplicationManager == NULL)
- {
- 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 = E_OUTOFMEMORY;
+ goto Failed;
}
+
+ 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:
pHttpContext->GetResponse()->SetStatus(500, "Internal Server Error", 0, hr);
return REQUEST_NOTIFICATION_STATUS::RQ_NOTIFICATION_FINISH_REQUEST;
@@ -142,21 +129,7 @@ CProxyModule::OnAsyncCompletion(
IHttpCompletionInfo * pCompletionInfo
)
{
- // TODO store whether we are inproc or outofproc so we don't need to check the config everytime?
- ASPNETCORE_CONFIG* config;
- ASPNETCORE_CONFIG::GetConfig(pHttpContext, &config);
-
- 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;
+ return m_pHandler->OnAsyncCompletion(
+ pCompletionInfo->GetCompletionBytes(),
+ pCompletionInfo->GetCompletionStatus());
}
\ No newline at end of file
diff --git a/src/AspNetCore/Src/serverprocess.cxx b/src/AspNetCore/Src/serverprocess.cxx
index 8296d6df9c..aac67ff8c6 100644
--- a/src/AspNetCore/Src/serverprocess.cxx
+++ b/src/AspNetCore/Src/serverprocess.cxx
@@ -1064,7 +1064,6 @@ Finished:
{
if (!fDonePrepareCommandLine)
{
-
strEventMsg.SafeSnwprintf(
m_struAppFullPath.QueryStr(),
ASPNETCORE_EVENT_PROCESS_START_INTERNAL_ERROR_MSG,
@@ -1073,11 +1072,11 @@ Finished:
else
{
strEventMsg.SafeSnwprintf(
- ASPNETCORE_EVENT_PROCESS_START_POSTCREATE_ERROR_MSG,
- m_struAppFullPath.QueryStr(),
- m_pszRootApplicationPath.QueryStr(),
- struCommandLine.QueryStr(),
- hr);
+ ASPNETCORE_EVENT_PROCESS_START_POSTCREATE_ERROR_MSG,
+ m_struAppFullPath.QueryStr(),
+ m_pszRootApplicationPath.QueryStr(),
+ struCommandLine.QueryStr(),
+ hr);
}
}