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