Adds source code from VSTS

This commit is contained in:
Justin Kotalik 2018-04-09 13:53:19 -07:00
parent 157e7f4fdd
commit b594130020
39 changed files with 12541 additions and 529 deletions

View File

@ -0,0 +1,293 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
//
// The key used for hash-table lookups, consists of the port on which the http process is created.
//
class APPLICATION_KEY
{
public:
APPLICATION_KEY(
VOID
) : INLINE_STRU_INIT(m_struKey)
{
}
HRESULT
Initialize(
_In_ LPCWSTR pszKey
)
{
return m_struKey.Copy(pszKey);
}
BOOL
GetIsEqual(
const APPLICATION_KEY * key2
) const
{
return m_struKey.Equals(key2->m_struKey);
}
DWORD CalcKeyHash() const
{
return Hash(m_struKey.QueryStr());
}
private:
INLINE_STRU(m_struKey, 1024);
};
class APP_OFFLINE_HTM
{
public:
APP_OFFLINE_HTM(LPCWSTR pszPath) : m_cRefs(1)
{
m_Path.Copy( pszPath );
}
VOID
ReferenceAppOfflineHtm() const
{
InterlockedIncrement(&m_cRefs);
}
VOID
DereferenceAppOfflineHtm() const
{
if (InterlockedDecrement(&m_cRefs) == 0)
{
delete this;
}
}
BOOL
Load(
VOID
)
{
BOOL fResult = TRUE;
LARGE_INTEGER li = {0};
CHAR *pszBuff = NULL;
HANDLE handle = INVALID_HANDLE_VALUE;
handle = CreateFile( m_Path.QueryStr(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if( handle == INVALID_HANDLE_VALUE )
{
if ( GetLastError() == ERROR_FILE_NOT_FOUND )
{
fResult = FALSE;
}
// This Load() member function is supposed be called only when the change notification event of file creation or file modification happens.
// If file is currenlty locked exclusively by other processes, we might get INVALID_HANDLE_VALUE even though the file exists. In that case, we should return TRUE here.
goto Finished;
}
if(!GetFileSizeEx( handle, &li ))
{
goto Finished;
}
if( li.HighPart != 0 )
{
// > 4gb file size not supported
// todo: log a warning at event log
goto Finished;
}
DWORD bytesRead = 0;
if(li.LowPart > 0)
{
pszBuff = new CHAR[ li.LowPart + 1 ];
if( ReadFile( handle, pszBuff, li.LowPart, &bytesRead, NULL ) )
{
m_Contents.Copy( pszBuff, bytesRead );
}
}
Finished:
if( handle != INVALID_HANDLE_VALUE )
{
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
}
if( pszBuff != NULL )
{
delete[] pszBuff;
pszBuff = NULL;
}
return fResult;
}
mutable LONG m_cRefs;
STRA m_Contents;
STRU m_Path;
};
class APPLICATION_MANAGER;
class APPLICATION
{
public:
APPLICATION() : m_pProcessManager(NULL), m_pApplicationManager(NULL), m_cRefs(1),
m_fAppOfflineFound(FALSE), m_pAppOfflineHtm(NULL), m_pFileWatcherEntry(NULL)
{
}
APPLICATION_KEY *
QueryApplicationKey()
{
return &m_applicationKey;
}
VOID
SetAppOfflineFound(
BOOL found
)
{
m_fAppOfflineFound = found;
}
BOOL
AppOfflineFound()
{
return m_fAppOfflineFound;
}
HRESULT
GetProcess(
_In_ IHttpContext *context,
_In_ ASPNETCORE_CONFIG *pConfig,
_Out_ SERVER_PROCESS **ppServerProcess
)
{
return m_pProcessManager->GetProcess( context, pConfig, ppServerProcess );
}
HRESULT
Recycle()
{
HRESULT hr = S_OK;
m_pProcessManager->ShutdownAllProcesses();
return hr;
}
VOID
ReferenceApplication() const
{
InterlockedIncrement(&m_cRefs);
}
VOID
DereferenceApplication() const
{
if (InterlockedDecrement(&m_cRefs) == 0)
{
delete this;
}
}
APP_OFFLINE_HTM* QueryAppOfflineHtm()
{
return m_pAppOfflineHtm;
}
~APPLICATION();
HRESULT
Initialize(
_In_ APPLICATION_MANAGER *pApplicationManager,
_In_ LPCWSTR pszApplication,
_In_ LPCWSTR pszPhysicalPath
);
VOID
UpdateAppOfflineFileHandle();
HRESULT
StartMonitoringAppOffline();
private:
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;
};
class APPLICATION_HASH :
public HASH_TABLE<APPLICATION, APPLICATION_KEY *>
{
public:
APPLICATION_HASH()
{}
APPLICATION_KEY *
ExtractKey(
APPLICATION *pApplication
)
{
return pApplication->QueryApplicationKey();
}
DWORD
CalcKeyHash(
APPLICATION_KEY *key
)
{
return key->CalcKeyHash();
}
BOOL
EqualKeys(
APPLICATION_KEY *key1,
APPLICATION_KEY *key2
)
{
return key1->GetIsEqual(key2);
}
VOID
ReferenceRecord(
APPLICATION *pApplication
)
{
pApplication->ReferenceApplication();
}
VOID
DereferenceRecord(
APPLICATION *pApplication
)
{
pApplication->DereferenceApplication();
}
private:
APPLICATION_HASH(const APPLICATION_HASH &);
void operator=(const APPLICATION_HASH &);
};

View File

@ -0,0 +1,158 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
#define DEFAULT_HASH_BUCKETS 293
class APPLICATION_MANAGER
{
public:
static
APPLICATION_MANAGER*
GetInstance(
VOID
)
{
if( sm_pApplicationManager == NULL )
{
sm_pApplicationManager = new APPLICATION_MANAGER();
}
return sm_pApplicationManager;
}
static
VOID
Cleanup(
VOID
)
{
if(sm_pApplicationManager != NULL)
{
delete sm_pApplicationManager;
sm_pApplicationManager = NULL;
}
}
HRESULT
GetApplication(
_In_ IHttpContext* pContext,
_Out_ APPLICATION ** ppApplication
);
HRESULT
RecycleApplication(
_In_ LPCWSTR pszApplication
);
HRESULT
Get502ErrorPage(
_Out_ HTTP_DATA_CHUNK** ppErrorPage
);
~APPLICATION_MANAGER()
{
if(m_pApplicationHash != NULL)
{
m_pApplicationHash->Clear();
delete m_pApplicationHash;
m_pApplicationHash = NULL;
}
if( m_pFileWatcher!= NULL )
{
delete m_pFileWatcher;
m_pFileWatcher = NULL;
}
if(m_pHttp502ErrorPage != NULL)
{
delete m_pHttp502ErrorPage;
m_pHttp502ErrorPage = NULL;
}
}
FILE_WATCHER*
GetFileWatcher()
{
return m_pFileWatcher;
}
HRESULT Initialize()
{
HRESULT hr = S_OK;
if(m_pApplicationHash == NULL)
{
m_pApplicationHash = new APPLICATION_HASH();
if(m_pApplicationHash == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
hr = m_pApplicationHash->Initialize(DEFAULT_HASH_BUCKETS);
if(FAILED(hr))
{
goto Finished;
}
}
if( m_pFileWatcher == NULL )
{
m_pFileWatcher = new FILE_WATCHER;
if(m_pFileWatcher == NULL)
{
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
goto Finished;
}
m_pFileWatcher->Create();
}
Finished:
return hr;
}
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(
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"> \
<html xmlns=\"http://www.w3.org/1999/xhtml\"> \
<head> \
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" /> \
<title> IIS 502.5 Error </title><style type=\"text/css\"></style></head> \
<body> <div id = \"content\"> \
<div class = \"content-container\"><h3> HTTP Error 502.5 - Process Failure </h3></div> \
<div class = \"content-container\"> \
<fieldset> <h4> Common causes of this issue: </h4> \
<ul><li> The application process failed to start </li> \
<li> The application process started but then stopped </li> \
<li> The application process started but failed to listen on the configured port </li></ul></fieldset> \
</div> \
<div class = \"content-container\"> \
<fieldset><h4> Troubleshooting steps: </h4> \
<ul><li> Check the system event log for error messages </li> \
<li> Enable logging the application process' stdout messages </li> \
<li> Attach a debugger to the application process and inspect </li></ul></fieldset> \
<fieldset><h4> For more information visit: \
<a href=\"https://go.microsoft.com/fwlink/?linkid=808681\"> <cite> https://go.microsoft.com/fwlink/?LinkID=808681 </cite></a></h4> \
</fieldset> \
</div> \
</div></body></html>")
{
InitializeSRWLock(&m_srwLock);
}
FILE_WATCHER *m_pFileWatcher;
APPLICATION_HASH *m_pApplicationHash;
static APPLICATION_MANAGER *sm_pApplicationManager;
SRWLOCK m_srwLock;
HTTP_DATA_CHUNK *m_pHttp502ErrorPage;
LPSTR m_pstrErrorInfo;
};

View File

@ -0,0 +1,127 @@
/*++
Copyright (c) 2014 Microsoft Corporation
Module Name:
aspnetcore_msg.mc
Abstract:
Asp.Net Core Module localizable messages.
--*/
#ifndef _ASPNETCORE_MSG_H_
#define _ASPNETCORE_MSG_H_
//
// Values are 32 bit values laid out as follows:
//
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +---+-+-+-----------------------+-------------------------------+
// |Sev|C|R| Facility | Code |
// +---+-+-+-----------------------+-------------------------------+
//
// where
//
// Sev - is the severity code
//
// 00 - Success
// 01 - Informational
// 10 - Warning
// 11 - Error
//
// C - is the Customer code flag
//
// R - is a reserved bit
//
// Facility - is the facility code
//
// Code - is the facility's status code
//
//
// Define the facility codes
//
//
// Define the severity codes
//
//
// MessageId: ASPNETCORE_EVENT_PROCESS_START_ERROR
//
// MessageText:
//
// %1
//
#define ASPNETCORE_EVENT_PROCESS_START_ERROR ((DWORD)0x000003E8L)
//
// MessageId: ASPNETCORE_EVENT_PROCESS_START_SUCCESS
//
// MessageText:
//
// %1
//
#define ASPNETCORE_EVENT_PROCESS_START_SUCCESS ((DWORD)0x000003E9L)
//
// MessageId: ASPNETCORE_EVENT_PROCESS_CRASH
//
// MessageText:
//
// %1
//
#define ASPNETCORE_EVENT_PROCESS_CRASH ((DWORD)0x000003EAL)
//
// MessageId: ASPNETCORE_EVENT_RAPID_FAIL_COUNT_EXCEEDED
//
// MessageText:
//
// %1
//
#define ASPNETCORE_EVENT_RAPID_FAIL_COUNT_EXCEEDED ((DWORD)0x000003EBL)
//
// MessageId: ASPNETCORE_EVENT_CONFIG_ERROR
//
// MessageText:
//
// %1
//
#define ASPNETCORE_EVENT_CONFIG_ERROR ((DWORD)0x000003ECL)
//
// MessageId: ASPNETCORE_EVENT_GRACEFUL_SHUTDOWN_FAILURE
//
// MessageText:
//
// %1
//
#define ASPNETCORE_EVENT_GRACEFUL_SHUTDOWN_FAILURE ((DWORD)0x000003EDL)
//
// MessageId: ASPNETCORE_EVENT_SENT_SHUTDOWN_HTTP_REQUEST
//
// MessageText:
//
// %1
//
#define ASPNETCORE_EVENT_SENT_SHUTDOWN_HTTP_REQUEST ((DWORD)0x000003EEL)
//
// MessageId: ASPNETCORE_EVENT_RECYCLE_APPOFFLINE
//
// MessageText:
//
// %1
//
#define ASPNETCORE_EVENT_RECYCLE_APPOFFLINE ((DWORD)0x000003F4L)
#endif // _ASPNETCORE_MODULE_MSG_H_

View File

@ -0,0 +1,207 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
#define CS_ROOTWEB_CONFIG L"MACHINE/WEBROOT/APPHOST/"
#define CS_ROOTWEB_CONFIG_LEN _countof(CS_ROOTWEB_CONFIG)-1
#define CS_ASPNETCORE_SECTION L"system.webServer/aspNetCore"
#define CS_WINDOWS_AUTHENTICATION_SECTION L"system.webServer/security/authentication/windowsAuthentication"
#define CS_BASIC_AUTHENTICATION_SECTION L"system.webServer/security/authentication/basicAuthentication"
#define CS_ANONYMOUS_AUTHENTICATION_SECTION L"system.webServer/security/authentication/anonymousAuthentication"
#define CS_AUTHENTICATION_ENABLED L"enabled"
#define CS_ASPNETCORE_PROCESS_EXE_PATH L"processPath"
#define CS_ASPNETCORE_PROCESS_ARGUMENTS L"arguments"
#define CS_ASPNETCORE_PROCESS_STARTUP_TIME_LIMIT L"startupTimeLimit"
#define CS_ASPNETCORE_PROCESS_SHUTDOWN_TIME_LIMIT L"shutdownTimeLimit"
#define CS_ASPNETCORE_WINHTTP_REQUEST_TIMEOUT L"requestTimeout"
#define CS_ASPNETCORE_RAPID_FAILS_PER_MINUTE L"rapidFailsPerMinute"
#define CS_ASPNETCORE_STDOUT_LOG_ENABLED L"stdoutLogEnabled"
#define CS_ASPNETCORE_STDOUT_LOG_FILE L"stdoutLogFile"
#define CS_ASPNETCORE_ENVIRONMENT_VARIABLES L"environmentVariables"
#define CS_ASPNETCORE_ENVIRONMENT_VARIABLE L"environmentVariable"
#define CS_ASPNETCORE_ENVIRONMENT_VARIABLE_NAME L"name"
#define CS_ASPNETCORE_ENVIRONMENT_VARIABLE_VALUE L"value"
#define CS_ASPNETCORE_PROCESSES_PER_APPLICATION L"processesPerApplication"
#define CS_ASPNETCORE_FORWARD_WINDOWS_AUTH_TOKEN L"forwardWindowsAuthToken"
#define CS_ASPNETCORE_DISABLE_START_UP_ERROR_PAGE L"disableStartUpErrorPage"
#define CS_ASPNETCORE_RECYCLE_ON_FILE_CHANGE L"recycleOnFileChange"
#define CS_ASPNETCORE_RECYCLE_ON_FILE_CHANGE_FILE L"file"
#define CS_ASPNETCORE_RECYCLE_ON_FILE_CHANGE_FILE_PATH L"path"
#define MAX_RAPID_FAILS_PER_MINUTE 100
#define MILLISECONDS_IN_ONE_SECOND 1000
#define MIN_PORT 1025
#define MAX_PORT 48000
#define HEX_TO_ASCII(c) ((CHAR)(((c) < 10) ? ((c) + '0') : ((c) + 'a' - 10)))
extern HTTP_MODULE_ID g_pModuleId;
extern IHttpServer * g_pHttpServer;
class ASPNETCORE_CONFIG : IHttpStoredContext
{
public:
virtual
~ASPNETCORE_CONFIG();
VOID
CleanupStoredContext()
{
delete this;
}
static
HRESULT
GetConfig(
_In_ IHttpContext *pHttpContext,
_Out_ ASPNETCORE_CONFIG **ppAspNetCoreConfig
);
ENVIRONMENT_VAR_HASH*
QueryEnvironmentVariables(
VOID
)
{
return m_pEnvironmentVariables;
}
DWORD
QueryRapidFailsPerMinute(
VOID
)
{
return m_dwRapidFailsPerMinute;
}
DWORD
QueryStartupTimeLimitInMS(
VOID
)
{
return m_dwStartupTimeLimitInMS;
}
DWORD
QueryShutdownTimeLimitInMS(
VOID
)
{
return m_dwShutdownTimeLimitInMS;
}
DWORD
QueryProcessesPerApplication(
VOID
)
{
return m_dwProcessesPerApplication;
}
DWORD
QueryRequestTimeoutInMS(
VOID
)
{
return m_dwRequestTimeoutInMS;
}
STRU*
QueryArguments(
VOID
)
{
return &m_struArguments;
}
STRU*
QueryApplicationPath(
VOID
)
{
return &m_struApplication;
}
STRU*
QueryProcessPath(
VOID
)
{
return &m_struProcessPath;
}
BOOL
QueryStdoutLogEnabled()
{
return m_fStdoutLogEnabled;
}
BOOL
QueryForwardWindowsAuthToken()
{
return m_fForwardWindowsAuthToken;
}
BOOL
QueryWindowsAuthEnabled()
{
return m_fWindowsAuthEnabled;
}
BOOL
QueryBasicAuthEnabled()
{
return m_fBasicAuthEnabled;
}
BOOL
QueryAnonymousAuthEnabled()
{
return m_fAnonymousAuthEnabled;
}
BOOL
QueryDisableStartUpErrorPage()
{
return m_fDisableStartUpErrorPage;
}
STRU*
QueryStdoutLogFile()
{
return &m_struStdoutLogFile;
}
private:
//
// private constructor
//
ASPNETCORE_CONFIG():
m_fStdoutLogEnabled( FALSE ),
m_pEnvironmentVariables( NULL )
{
}
HRESULT
Populate(
IHttpContext *pHttpContext
);
DWORD m_dwRequestTimeoutInMS;
DWORD m_dwStartupTimeLimitInMS;
DWORD m_dwShutdownTimeLimitInMS;
DWORD m_dwRapidFailsPerMinute;
DWORD m_dwProcessesPerApplication;
STRU m_struApplication;
STRU m_struArguments;
STRU m_struProcessPath;
STRU m_struStdoutLogFile;
BOOL m_fStdoutLogEnabled;
BOOL m_fForwardWindowsAuthToken;
BOOL m_fDisableStartUpErrorPage;
BOOL m_fWindowsAuthEnabled;
BOOL m_fBasicAuthEnabled;
BOOL m_fAnonymousAuthEnabled;
ENVIRONMENT_VAR_HASH* m_pEnvironmentVariables;
};

View File

@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
#define ASPNETCORE_DEBUG_FLAG_INFO 0x00000001
#define ASPNETCORE_DEBUG_FLAG_WARNING 0x00000002
#define ASPNETCORE_DEBUG_FLAG_ERROR 0x00000004
extern DWORD g_dwAspNetCoreDebugFlags;
static
BOOL
IfDebug(
DWORD dwFlag
)
{
return ( dwFlag & g_dwAspNetCoreDebugFlags );
}
static
VOID
DebugPrint(
DWORD dwFlag,
LPCSTR szString
)
{
STACK_STRA (strOutput, 256);
HRESULT hr = S_OK;
if ( IfDebug( dwFlag ) )
{
hr = strOutput.SafeSnprintf(
"[aspnetcore.dll] %s\r\n",
szString );
if (FAILED (hr))
{
goto Finished;
}
OutputDebugStringA( strOutput.QueryStr() );
}
Finished:
return;
}
static
VOID
DebugPrintf(
DWORD dwFlag,
LPCSTR szFormat,
...
)
{
STACK_STRA (strCooked,256);
va_list args;
HRESULT hr = S_OK;
if ( IfDebug( dwFlag ) )
{
va_start( args, szFormat );
hr = strCooked.SafeVsnprintf(szFormat, args );
va_end( args );
if (FAILED (hr))
{
goto Finished;
}
DebugPrint( dwFlag, strCooked.QueryStr() );
}
Finished:
return;
}

View File

@ -0,0 +1,155 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
//
// The key used for hash-table lookups, consists of the port on which the http process is created.
//
class ENVIRONMENT_VAR_ENTRY
{
public:
ENVIRONMENT_VAR_ENTRY():
_cRefs(1)
{
}
HRESULT
Initialize(
PCWSTR pszName,
PCWSTR pszValue
)
{
HRESULT hr = S_OK;
if (FAILED(hr = _strName.Copy(pszName)) ||
FAILED(hr = _strValue.Copy(pszValue)))
{
}
return hr;
}
VOID
Reference() const
{
InterlockedIncrement(&_cRefs);
}
VOID
Dereference() const
{
if (InterlockedDecrement(&_cRefs) == 0)
{
delete this;
}
}
PWSTR const
QueryName()
{
return _strName.QueryStr();
}
PWSTR const
QueryValue()
{
return _strValue.QueryStr();
}
private:
~ENVIRONMENT_VAR_ENTRY()
{
}
STRU _strName;
STRU _strValue;
mutable LONG _cRefs;
};
class ENVIRONMENT_VAR_HASH : public HASH_TABLE<ENVIRONMENT_VAR_ENTRY, PWSTR>
{
public:
ENVIRONMENT_VAR_HASH()
{}
PWSTR
ExtractKey(
ENVIRONMENT_VAR_ENTRY * pEntry
)
{
return pEntry->QueryName();
}
DWORD
CalcKeyHash(
PWSTR pszName
)
{
return HashStringNoCase(pszName);
}
BOOL
EqualKeys(
PWSTR pszName1,
PWSTR pszName2
)
{
return (_wcsicmp(pszName1, pszName2) == 0);
}
VOID
ReferenceRecord(
ENVIRONMENT_VAR_ENTRY * pEntry
)
{
pEntry->Reference();
}
VOID
DereferenceRecord(
ENVIRONMENT_VAR_ENTRY * pEntry
)
{
pEntry->Dereference();
}
static
VOID
CopyToMultiSz(
ENVIRONMENT_VAR_ENTRY * pEntry,
PVOID pvData
)
{
STRU strTemp;
MULTISZ *pMultiSz = static_cast<MULTISZ *>(pvData);
DBG_ASSERT(pMultiSz);
DBG_ASSERT(pEntry);
strTemp.Copy(pEntry->QueryName());
strTemp.Append(pEntry->QueryValue());
pMultiSz->Append(strTemp.QueryStr());
}
static
VOID
CopyToTable(
ENVIRONMENT_VAR_ENTRY * pEntry,
PVOID pvData
)
{
// best effort copy, ignore the failure
ENVIRONMENT_VAR_ENTRY * pNewEntry = new ENVIRONMENT_VAR_ENTRY();
if (pNewEntry != NULL)
{
pNewEntry->Initialize(pEntry->QueryName(), pEntry->QueryValue());
ENVIRONMENT_VAR_HASH *pHash = static_cast<ENVIRONMENT_VAR_HASH *>(pvData);
DBG_ASSERT(pHash);
pHash->InsertRecord(pNewEntry);
// Need to dereference as InsertRecord references it now
pNewEntry->Dereference();
}
}
private:
ENVIRONMENT_VAR_HASH(const ENVIRONMENT_VAR_HASH &);
void operator=(const ENVIRONMENT_VAR_HASH &);
};

View File

@ -0,0 +1,126 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
#define FILE_WATCHER_SHUTDOWN_KEY (ULONG_PTR)(-1)
#define FILE_WATCHER_ENTRY_BUFFER_SIZE 4096
#ifndef CONTAINING_RECORD
//
// Calculate the address of the base of the structure given its type, and an
// address of a field within the structure.
//
#define CONTAINING_RECORD(address, type, field) \
((type *)((PCHAR)(address)-(ULONG_PTR)(&((type *)0)->field)))
#endif // !CONTAINING_RECORD
#define FILE_NOTIFY_VALID_MASK 0x00000fff
#define FILE_WATCHER_ENTRY_SIGNATURE ((DWORD) 'FWES')
#define FILE_WATCHER_ENTRY_SIGNATURE_FREE ((DWORD) 'sewf')
class APPLICATION;
class FILE_WATCHER{
public:
FILE_WATCHER();
~FILE_WATCHER();
HRESULT Create();
HANDLE
QueryCompletionPort(
VOID
) const
{
return m_hCompletionPort;
}
static
DWORD
WINAPI ChangeNotificationThread(LPVOID);
static
void
WINAPI FileWatcherCompletionRoutine
(
DWORD dwCompletionStatus,
DWORD cbCompletion,
OVERLAPPED * pOverlapped
);
private:
HANDLE m_hCompletionPort;
HANDLE m_hChangeNotificationThread;
};
class FILE_WATCHER_ENTRY
{
public:
FILE_WATCHER_ENTRY(FILE_WATCHER * pFileMonitor);
OVERLAPPED _overlapped;
HRESULT
Create(
_In_ PCWSTR pszDirectoryToMonitor,
_In_ PCWSTR pszFileNameToMonitor,
_In_ APPLICATION* pApplication,
_In_ HANDLE hImpersonationToken
);
VOID
ReferenceFileWatcherEntry() const
{
InterlockedIncrement(&_cRefs);
}
VOID
DereferenceFileWatcherEntry() const
{
if (InterlockedDecrement(&_cRefs) == 0)
{
delete this;
}
}
BOOL
QueryIsValid() const
{
return _fIsValid;
}
VOID
MarkEntryInValid()
{
_fIsValid = FALSE;
}
HRESULT Monitor();
VOID StopMonitor();
HRESULT
HandleChangeCompletion(
_In_ DWORD dwCompletionStatus,
_In_ DWORD cbCompletion
);
private:
virtual ~FILE_WATCHER_ENTRY();
DWORD _dwSignature;
BUFFER _buffDirectoryChanges;
HANDLE _hImpersonationToken;
HANDLE _hDirectory;
FILE_WATCHER* _pFileMonitor;
APPLICATION* _pApplication;
STRU _strFileName;
STRU _strDirectoryName;
LONG _lStopMonitorCalled;
mutable LONG _cRefs;
BOOL _fIsValid;
SRWLOCK _srwLock;
};

View File

@ -0,0 +1,157 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
//
// The key used for hash-table lookups, consists of the port on which the http process is created.
//
class FORWARDER_CONNECTION_KEY
{
public:
FORWARDER_CONNECTION_KEY(
VOID
)
{
}
HRESULT
Initialize(
_In_ DWORD dwPort
)
{
m_dwPort = dwPort;
return S_OK;
}
BOOL
GetIsEqual(
const FORWARDER_CONNECTION_KEY * key2
) const
{
return m_dwPort == key2->m_dwPort;
}
DWORD CalcKeyHash() const
{
// TODO: Review hash distribution.
return Hash(m_dwPort);
}
private:
DWORD m_dwPort;
};
class FORWARDER_CONNECTION
{
public:
FORWARDER_CONNECTION(
VOID
);
HRESULT
Initialize(
DWORD dwPort
);
HINTERNET
QueryHandle() const
{
return m_hConnection;
}
VOID
ReferenceForwarderConnection() const
{
InterlockedIncrement(&m_cRefs);
}
VOID
DereferenceForwarderConnection() const
{
if (InterlockedDecrement(&m_cRefs) == 0)
{
delete this;
}
}
FORWARDER_CONNECTION_KEY *
QueryConnectionKey()
{
return &m_ConnectionKey;
}
private:
~FORWARDER_CONNECTION()
{
if (m_hConnection != NULL)
{
WinHttpCloseHandle(m_hConnection);
m_hConnection = NULL;
}
}
mutable LONG m_cRefs;
FORWARDER_CONNECTION_KEY m_ConnectionKey;
HINTERNET m_hConnection;
};
class FORWARDER_CONNECTION_HASH :
public HASH_TABLE<FORWARDER_CONNECTION, FORWARDER_CONNECTION_KEY *>
{
public:
FORWARDER_CONNECTION_HASH()
{}
FORWARDER_CONNECTION_KEY *
ExtractKey(
FORWARDER_CONNECTION *pConnection
)
{
return pConnection->QueryConnectionKey();
}
DWORD
CalcKeyHash(
FORWARDER_CONNECTION_KEY *key
)
{
return key->CalcKeyHash();
}
BOOL
EqualKeys(
FORWARDER_CONNECTION_KEY *key1,
FORWARDER_CONNECTION_KEY *key2
)
{
return key1->GetIsEqual(key2);
}
VOID
ReferenceRecord(
FORWARDER_CONNECTION *pConnection
)
{
pConnection->ReferenceForwarderConnection();
}
VOID
DereferenceRecord(
FORWARDER_CONNECTION *pConnection
)
{
pConnection->DereferenceForwarderConnection();
}
private:
FORWARDER_CONNECTION_HASH(const FORWARDER_CONNECTION_HASH &);
void operator=(const FORWARDER_CONNECTION_HASH &);
};

View File

@ -0,0 +1,479 @@
#pragma once
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
forwardinghandler.h
Abstract:
Handler for handling URLs from out-of-box.
--*/
#include "forwarderconnection.h"
#include "protocolconfig.h"
#include "serverprocess.h"
#include "application.h"
#include "tracelog.h"
#include "websockethandler.h"
#define ASPNETCORE_DEBUG_STRU_BUFFER_SIZE 100
#define ASPNETCORE_DEBUG_STRU_ARRAY_SIZE 100
enum FORWARDING_REQUEST_STATUS
{
FORWARDER_START,
FORWARDER_SENDING_REQUEST,
FORWARDER_RECEIVING_RESPONSE,
FORWARDER_RECEIVED_WEBSOCKET_RESPONSE,
FORWARDER_RESET_CONNECTION,
FORWARDER_DONE
};
extern HTTP_MODULE_ID g_pModuleId;
extern IHttpServer * g_pHttpServer;
extern BOOL g_fAsyncDisconnectAvailable;
extern PCWSTR g_pszModuleName;
extern HMODULE g_hModule;
extern HMODULE g_hWinHttpModule;
extern DWORD g_dwTlsIndex;
extern DWORD g_OptionalWinHttpFlags;
#ifdef DEBUG
extern STRA g_strLogs[ASPNETCORE_DEBUG_STRU_ARRAY_SIZE];
extern DWORD g_dwLogCounter;
#endif // DEBUG
enum MULTI_PART_POSITION
{
MULTI_PART_IN_BOUNDARY,
MULTI_PART_IN_HEADER,
MULTI_PART_IN_CHUNK,
MULTI_PART_IN_CHUNK_END
};
class ASYNC_DISCONNECT_CONTEXT;
#define FORWARDING_HANDLER_SIGNATURE ((DWORD)'FHLR')
#define FORWARDING_HANDLER_SIGNATURE_FREE ((DWORD)'fhlr')
class FORWARDING_HANDLER
{
public:
FORWARDING_HANDLER(
__in IHttpContext * pW3Context
);
static void * operator new(size_t size);
static void operator delete(void * pMemory);
VOID
ReferenceForwardingHandler(
VOID
) const;
VOID
DereferenceForwardingHandler(
VOID
) const;
REQUEST_NOTIFICATION_STATUS
OnExecuteRequestHandler();
REQUEST_NOTIFICATION_STATUS
OnAsyncCompletion(
DWORD cbCompletion,
HRESULT hrCompletionStatus
);
IHttpTraceContext *
QueryTraceContext()
{
return m_pW3Context->GetTraceContext();
}
IHttpContext *
QueryHttpContext(
VOID
)
{
return m_pW3Context;
}
static
VOID
CALLBACK
OnWinHttpCompletion(
HINTERNET hRequest,
DWORD_PTR dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength
)
{
FORWARDING_HANDLER * pThis = static_cast<FORWARDING_HANDLER *>(reinterpret_cast<PVOID>(dwContext));
if (pThis == NULL)
{
//error happened, nothing can be done here
return;
}
DBG_ASSERT(pThis->m_Signature == FORWARDING_HANDLER_SIGNATURE);
pThis->OnWinHttpCompletionInternal(hRequest,
dwInternetStatus,
lpvStatusInformation,
dwStatusInformationLength);
}
static
HRESULT
StaticInitialize(
BOOL fEnableReferenceCountTracing
);
static
VOID
StaticTerminate();
static
PCWSTR
QueryErrorFormat()
{
return sm_strErrorFormat.QueryStr();
}
static
HANDLE
QueryEventLog()
{
return sm_hEventLog;
}
VOID
TerminateRequest(
BOOL fClientInitiated
);
static HINTERNET sm_hSession;
HRESULT
SetStatusAndHeaders(
PCSTR pszHeaders,
DWORD cchHeaders
);
HRESULT
OnSharedRequestEntity(
ULONGLONG ulOffset,
LPCBYTE pvBuffer,
DWORD cbBuffer
);
VOID
SetStatus(
FORWARDING_REQUEST_STATUS status
)
{
m_RequestStatus = status;
}
virtual
~FORWARDING_HANDLER(
VOID
);
private:
//
// Begin OnMapRequestHandler phases.
//
HRESULT
CreateWinHttpRequest(
__in const IHttpRequest * pRequest,
__in const PROTOCOL_CONFIG * pProtocol,
__in HINTERNET hConnect,
__inout STRU * pstrUrl,
ASPNETCORE_CONFIG* pAspNetCoreConfig,
SERVER_PROCESS* pServerProcess
);
//
// End OnMapRequestHandler phases.
//
VOID
RemoveRequest();
HRESULT
GetHeaders(
const PROTOCOL_CONFIG * pProtocol,
PCWSTR * ppszHeaders,
DWORD * pcchHeaders,
ASPNETCORE_CONFIG* pAspNetCoreConfig,
SERVER_PROCESS* pServerProcess
);
HRESULT
DoReverseRewrite(
__in IHttpResponse *pResponse
);
BYTE *
GetNewResponseBuffer(
DWORD dwBufferSize
);
VOID
FreeResponseBuffers();
VOID
OnWinHttpCompletionInternal(
HINTERNET hRequest,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength
);
HRESULT
OnWinHttpCompletionSendRequestOrWriteComplete(
HINTERNET hRequest,
DWORD dwInternetStatus,
__out BOOL * pfClientError,
__out BOOL * pfAnotherCompletionExpected
);
HRESULT
OnWinHttpCompletionStatusHeadersAvailable(
HINTERNET hRequest,
__out BOOL * pfAnotherCompletionExpected
);
HRESULT
OnWinHttpCompletionStatusDataAvailable(
HINTERNET hRequest,
DWORD dwBytes,
__out BOOL * pfAnotherCompletionExpected
);
HRESULT
OnWinHttpCompletionStatusReadComplete(
__in IHttpResponse * pResponse,
DWORD dwStatusInformationLength,
__out BOOL * pfAnotherCompletionExpected
);
HRESULT
OnSendingRequest(
DWORD cbCompletion,
HRESULT hrCompletionStatus,
__out BOOL * pfClientError
);
HRESULT
OnReceivingResponse();
HRESULT
OnWebSocketWinHttpSendComplete(
HINTERNET hRequest,
LPVOID pvStatus,
DWORD hrCompletion,
DWORD cbCompletion,
BOOL * pfAnotherCompletionExpected
);
HRESULT
OnWebSocketWinHttpReceiveComplete(
HINTERNET hRequest,
LPVOID pvStatus,
DWORD hrCompletion,
DWORD cbCompletion,
BOOL * pfAnotherCompletionExpected
);
HRESULT
OnWebSocketIisSendComplete(
DWORD hrCompletion,
DWORD cbCompletion
);
HRESULT
OnWebSocketIisReceiveComplete(
DWORD hrCompletion,
DWORD cbCompletion
);
HRESULT
DoIisWebSocketReceive(
VOID
);
VOID
TerminateWebsocket(
VOID
);
DWORD m_Signature;
mutable LONG m_cRefs;
IHttpContext * m_pW3Context;
//
// WinHTTP request handle is protected using a read-write lock.
//
SRWLOCK m_RequestLock;
HINTERNET m_hRequest;
APP_OFFLINE_HTM *m_pAppOfflineHtm;
APPLICATION *m_pApplication;
BOOL m_fResponseHeadersReceivedAndSet;
volatile BOOL m_fClientDisconnected;
//
// A safety guard flag indicating no more IIS PostCompletion is allowed
//
volatile BOOL m_fFinishRequest;
//
// A safety guard flag to prevent from unexpect callback which may signal IIS pipeline
// more than once with non-pending status
//
volatile BOOL m_fDoneAsyncCompletion;
volatile BOOL m_fHasError;
//
// WinHttp may hit AV under race if handle got closed more than once simultaneously
// Use two bool variables to guard
//
volatile BOOL m_fHttpHandleInClose;
volatile BOOL m_fWebSocketHandleInClose;
//
// Record the number of winhttp handles in use
// release IIS pipeline only after all handles got closed
//
volatile LONG m_dwHandlers;
BOOL m_fDoReverseRewriteHeaders;
BOOL m_fServerResetConn;
DWORD m_msStartTime;
DWORD m_BytesToReceive;
DWORD m_BytesToSend;
BYTE * m_pEntityBuffer;
DWORD m_cchLastSend;
static const SIZE_T INLINE_ENTITY_BUFFERS = 8;
DWORD m_cEntityBuffers;
BUFFER_T<BYTE*,INLINE_ENTITY_BUFFERS> m_buffEntityBuffers;
DWORD m_cBytesBuffered;
DWORD m_cMinBufferLimit;
PCSTR m_pszOriginalHostHeader;
volatile FORWARDING_REQUEST_STATUS m_RequestStatus;
ASYNC_DISCONNECT_CONTEXT * m_pDisconnect;
PCWSTR m_pszHeaders;
DWORD m_cchHeaders;
BOOL m_fWebSocketEnabled;
STRU m_strFullUri;
ULONGLONG m_cContentLength;
WEBSOCKET_HANDLER * m_pWebSocket;
static PROTOCOL_CONFIG sm_ProtocolConfig;
static STRU sm_strErrorFormat;
static HANDLE sm_hEventLog;
static ALLOC_CACHE_HANDLER * sm_pAlloc;
//
// Reference cout tracing for debugging purposes.
//
static TRACE_LOG * sm_pTraceLog;
};
class ASYNC_DISCONNECT_CONTEXT : public IHttpConnectionStoredContext
{
public:
ASYNC_DISCONNECT_CONTEXT()
{
m_pHandler = NULL;
}
VOID
CleanupStoredContext()
{
DBG_ASSERT(m_pHandler == NULL);
delete this;
}
VOID
NotifyDisconnect()
{
FORWARDING_HANDLER *pInitialValue = (FORWARDING_HANDLER*)
InterlockedExchangePointer((PVOID*) &m_pHandler, NULL);
if (pInitialValue != NULL)
{
pInitialValue->TerminateRequest(TRUE);
pInitialValue->DereferenceForwardingHandler();
}
}
VOID
SetHandler(
FORWARDING_HANDLER *pHandler
)
{
//
// Take a reference on the forwarding handler.
// This reference will be released on either of two conditions:
//
// 1. When the request processing ends, in which case a ResetHandler()
// is called.
//
// 2. When a disconnect notification arrives.
//
// We need to make sure that only one of them ends up dereferencing
// the object.
//
DBG_ASSERT (pHandler != NULL);
DBG_ASSERT (m_pHandler == NULL);
pHandler->ReferenceForwardingHandler();
InterlockedExchangePointer((PVOID*)&m_pHandler, pHandler);
}
VOID
ResetHandler(
VOID
)
{
FORWARDING_HANDLER *pInitialValue = (FORWARDING_HANDLER*)
InterlockedExchangePointer( (PVOID*)&m_pHandler, NULL);
if (pInitialValue != NULL)
{
pInitialValue->DereferenceForwardingHandler();
}
}
private:
~ASYNC_DISCONNECT_CONTEXT()
{}
FORWARDING_HANDLER * m_pHandler;
};

View File

@ -0,0 +1,49 @@
// 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.
#ifndef __FX_VER_H__
#define __FX_VER_H__
#include <string>
// Note: This is not SemVer (esp., in comparing pre-release part, fx_ver_t does not
// compare multiple dot separated identifiers individually.) ex: 1.0.0-beta.2 vs. 1.0.0-beta.11
struct fx_ver_t
{
fx_ver_t(int major, int minor, int patch);
fx_ver_t(int major, int minor, int patch, const std::wstring& pre);
fx_ver_t(int major, int minor, int patch, const std::wstring& pre, const std::wstring& build);
int get_major() const { return m_major; }
int get_minor() const { return m_minor; }
int get_patch() const { return m_patch; }
void set_major(int m) { m_major = m; }
void set_minor(int m) { m_minor = m; }
void set_patch(int p) { m_patch = p; }
bool is_prerelease() const { return !m_pre.empty(); }
std::wstring as_str() const;
std::wstring prerelease_glob() const;
std::wstring patch_glob() const;
bool operator ==(const fx_ver_t& b) const;
bool operator !=(const fx_ver_t& b) const;
bool operator <(const fx_ver_t& b) const;
bool operator >(const fx_ver_t& b) const;
bool operator <=(const fx_ver_t& b) const;
bool operator >=(const fx_ver_t& b) const;
static bool parse(const std::wstring& ver, fx_ver_t* fx_ver, bool parse_only_production = false);
private:
int m_major;
int m_minor;
int m_patch;
std::wstring m_pre;
std::wstring m_build;
static int compare(const fx_ver_t&a, const fx_ver_t& b);
};
#endif // __FX_VER_H__

View File

@ -0,0 +1,37 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
class ASPNET_CORE_GLOBAL_MODULE : public CGlobalModule
{
public:
ASPNET_CORE_GLOBAL_MODULE(
APPLICATION_MANAGER* pApplicationManager
);
~ASPNET_CORE_GLOBAL_MODULE()
{
}
VOID Terminate()
{
// Remove the class from memory.
delete this;
}
GLOBAL_NOTIFICATION_STATUS
OnGlobalStopListening(
_In_ IGlobalStopListeningProvider * pProvider
);
GLOBAL_NOTIFICATION_STATUS
OnGlobalConfigurationChange(
_In_ IGlobalConfigurationChangeProvider * pProvider
);
private:
APPLICATION_MANAGER * m_pApplicationManager;
};

View File

@ -0,0 +1,95 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
class PATH
{
public:
static
HRESULT
SplitUrl(
PCWSTR pszDestinationUrl,
BOOL *pfSecure,
STRU *pstrDestination,
STRU *pstrUrl
);
static
HRESULT
UnEscapeUrl(
PCWSTR pszUrl,
DWORD cchUrl,
bool fCopyQuery,
STRA * pstrResult
);
static
HRESULT
UnEscapeUrl(
PCWSTR pszUrl,
DWORD cchUrl,
STRU * pstrResult
);
static HRESULT
EscapeAbsPath(
IHttpRequest * pRequest,
STRU * strEscapedUrl
);
static
bool
IsValidAttributeNameChar(
WCHAR ch
);
static
bool
IsValidQueryStringName(
PCWSTR pszName
);
static
bool
IsValidHeaderName(
PCWSTR pszName
);
static
bool
FindInMultiString(
PCWSTR pszMultiString,
PCWSTR pszStringToFind
);
static
HRESULT
IsPathUnc(
__in LPCWSTR pszPath,
__out BOOL * pfIsUnc
);
static
HRESULT
ConvertPathToFullPath(
_In_ LPCWSTR pszPath,
_In_ LPCWSTR pszRootPath,
_Out_ STRU* pStrFullPath
);
private:
PATH() {}
~PATH() {}
static
CHAR
ToHexDigit(
UINT nDigit
)
{
return static_cast<CHAR>(nDigit > 9 ? nDigit - 10 + 'A' : nDigit + '0');
}
};

View File

@ -0,0 +1,193 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
#define ONE_MINUTE_IN_MILLISECONDS 60000
class PROCESS_MANAGER
{
public:
virtual
~PROCESS_MANAGER();
VOID
ReferenceProcessManager() const
{
InterlockedIncrement(&m_cRefs);
}
VOID
DereferenceProcessManager() const
{
if (InterlockedDecrement(&m_cRefs) == 0)
{
delete this;
}
}
HRESULT
GetProcess(
_In_ IHttpContext *context,
_In_ ASPNETCORE_CONFIG *pConfig,
_Out_ SERVER_PROCESS **ppServerProcess
);
HANDLE
QueryNULHandle()
{
return m_hNULHandle;
}
HRESULT
Initialize(
VOID
);
VOID
SendShutdownSignal()
{
AcquireSRWLockExclusive( &m_srwLock );
for(DWORD i = 0; i < m_dwProcessesPerApplication; ++i )
{
if( m_ppServerProcessList != NULL &&
m_ppServerProcessList[i] != NULL )
{
m_ppServerProcessList[i]->SendSignal();
m_ppServerProcessList[i]->DereferenceServerProcess();
m_ppServerProcessList[i] = NULL;
}
}
ReleaseSRWLockExclusive( &m_srwLock );
}
VOID
ShutdownProcess(
SERVER_PROCESS* pServerProcess
)
{
AcquireSRWLockExclusive( &m_srwLock );
ShutdownProcessNoLock( pServerProcess );
ReleaseSRWLockExclusive( &m_srwLock );
}
VOID
ShutdownAllProcesses(
)
{
AcquireSRWLockExclusive( &m_srwLock );
ShutdownAllProcessesNoLock();
ReleaseSRWLockExclusive( &m_srwLock );
}
VOID
IncrementRapidFailCount(
VOID
)
{
InterlockedIncrement(&m_cRapidFailCount);
}
PROCESS_MANAGER() :
m_ppServerProcessList( NULL ),
m_hNULHandle( NULL ),
m_cRapidFailCount( 0 ),
m_dwProcessesPerApplication( 1 ),
m_dwRouteToProcessIndex( 0 ),
m_fServerProcessListReady(FALSE),
m_cRefs( 1 )
{
InitializeSRWLock( &m_srwLock );
}
private:
BOOL
RapidFailsPerMinuteExceeded(
LONG dwRapidFailsPerMinute
)
{
DWORD dwCurrentTickCount = GetTickCount();
if( (dwCurrentTickCount - m_dwRapidFailTickStart)
>= ONE_MINUTE_IN_MILLISECONDS )
{
//
// reset counters every minute.
//
InterlockedExchange(&m_cRapidFailCount, 0);
m_dwRapidFailTickStart = dwCurrentTickCount;
}
return m_cRapidFailCount > dwRapidFailsPerMinute;
}
VOID
ShutdownProcessNoLock(
SERVER_PROCESS* pServerProcess
)
{
for(DWORD i = 0; i < m_dwProcessesPerApplication; ++i )
{
if( m_ppServerProcessList != NULL &&
m_ppServerProcessList[i] != NULL &&
m_ppServerProcessList[i]->GetPort() == pServerProcess->GetPort() )
{
// shutdown pServerProcess if not already shutdown.
m_ppServerProcessList[i]->StopProcess();
m_ppServerProcessList[i]->DereferenceServerProcess();
m_ppServerProcessList[i] = NULL;
}
}
}
VOID
ShutdownAllProcessesNoLock(
VOID
)
{
for(DWORD i = 0; i < m_dwProcessesPerApplication; ++i )
{
if( m_ppServerProcessList != NULL &&
m_ppServerProcessList[i] != NULL )
{
// shutdown pServerProcess if not already shutdown.
m_ppServerProcessList[i]->SendSignal();
m_ppServerProcessList[i]->DereferenceServerProcess();
m_ppServerProcessList[i] = NULL;
}
}
}
volatile LONG m_cRapidFailCount;
DWORD m_dwRapidFailTickStart;
DWORD m_dwProcessesPerApplication;
volatile DWORD m_dwRouteToProcessIndex;
SRWLOCK m_srwLock;
SERVER_PROCESS **m_ppServerProcessList;
//
// m_hNULHandle is used to redirect stdout/stderr to NUL.
// If Createprocess is called to launch a batch file for example,
// it tries to write to the console buffer by default. It fails to
// start if the console buffer is owned by the parent process i.e
// in our case w3wp.exe. So we have to redirect the stdout/stderr
// of the child process to NUL or to a file (anything other than
// the console buffer of the parent process).
//
HANDLE m_hNULHandle;
mutable LONG m_cRefs;
volatile static BOOL sm_fWSAStartupDone;
volatile BOOL m_fServerProcessListReady;
};

View File

@ -0,0 +1,105 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
#include "aspnetcoreconfig.h"
class PROTOCOL_CONFIG
{
public:
PROTOCOL_CONFIG()
{
}
HRESULT
Initialize();
VOID
OverrideConfig(
ASPNETCORE_CONFIG *pAspNetCoreConfig
);
BOOL
QueryDoKeepAlive() const
{
return m_fKeepAlive;
}
DWORD
QueryTimeout() const
{
return m_msTimeout;
}
BOOL
QueryPreserveHostHeader() const
{
return m_fPreserveHostHeader;
}
BOOL
QueryReverseRewriteHeaders() const
{
return m_fReverseRewriteHeaders;
}
const STRA *
QueryXForwardedForName() const
{
return &m_strXForwardedForName;
}
BOOL
QueryIncludePortInXForwardedFor() const
{
return m_fIncludePortInXForwardedFor;
}
DWORD
QueryMinResponseBuffer() const
{
return m_dwMinResponseBuffer;
}
DWORD
QueryResponseBufferLimit() const
{
return m_dwResponseBufferLimit;
}
DWORD
QueryMaxResponseHeaderSize() const
{
return m_dwMaxResponseHeaderSize;
}
const STRA*
QuerySslHeaderName() const
{
return &m_strSslHeaderName;
}
const STRA *
QueryClientCertName() const
{
return &m_strClientCertName;
}
private:
BOOL m_fKeepAlive;
BOOL m_fPreserveHostHeader;
BOOL m_fReverseRewriteHeaders;
BOOL m_fIncludePortInXForwardedFor;
DWORD m_msTimeout;
DWORD m_dwMinResponseBuffer;
DWORD m_dwResponseBufferLimit;
DWORD m_dwMaxResponseHeaderSize;
STRA m_strXForwardedForName;
STRA m_strSslHeaderName;
STRA m_strClientCertName;
};

View File

@ -0,0 +1,64 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
extern HTTP_MODULE_ID g_pModuleId;
extern IHttpServer *g_pHttpServer;
extern HMODULE g_hAspnetCoreRH;
class ASPNET_CORE_PROXY_MODULE : public CHttpModule
{
public:
ASPNET_CORE_PROXY_MODULE();
~ASPNET_CORE_PROXY_MODULE();
void * operator new(size_t size, IModuleAllocator * pPlacement)
{
return pPlacement->AllocateMemory(static_cast<DWORD>(size));
}
VOID
operator delete(
void *
)
{}
__override
REQUEST_NOTIFICATION_STATUS
OnExecuteRequestHandler(
IHttpContext * pHttpContext,
IHttpEventProvider * pProvider
);
__override
REQUEST_NOTIFICATION_STATUS
OnAsyncCompletion(
IHttpContext * pHttpContext,
DWORD dwNotification,
BOOL fPostNotification,
IHttpEventProvider * pProvider,
IHttpCompletionInfo * pCompletionInfo
);
private:
APPLICATION_INFO *m_pApplicationInfo;
APPLICATION *m_pApplication;
REQUEST_HANDLER *m_pHandler;
};
class ASPNET_CORE_PROXY_MODULE_FACTORY : public IHttpModuleFactory
{
public:
HRESULT
GetHttpModule(
CHttpModule ** ppModule,
IModuleAllocator * pAllocator
);
VOID
Terminate();
};

View File

@ -0,0 +1,17 @@
#pragma once
#define IDS_INVALID_PROPERTY 1000
#define IDS_SERVER_ERROR 1001
#define ASPNETCORE_EVENT_MSG_BUFFER_SIZE 256
#define ASPNETCORE_EVENT_PROCESS_START_SUCCESS_MSG L"Application '%s' started process '%d' successfully and is listening on port '%d'."
#define ASPNETCORE_EVENT_RAPID_FAIL_COUNT_EXCEEDED_MSG L"Maximum rapid fail count per minute of '%d' exceeded."
#define ASPNETCORE_EVENT_PROCESS_START_INTERNAL_ERROR_MSG L"Application '%s' failed to parse processPath and arguments due to internal error, ErrorCode = '0x%x'."
#define ASPNETCORE_EVENT_PROCESS_START_POSTCREATE_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s'but failed to get its status, ErrorCode = '0x%x'."
#define ASPNETCORE_EVENT_PROCESS_START_ERROR_MSG L"Application '%s' with physical root '%s' failed to start process with commandline '%s', ErrorCode = '0x%x : %x."
#define ASPNETCORE_EVENT_PROCESS_START_WRONGPORT_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s' but failed to listen on the given port '%d'"
#define ASPNETCORE_EVENT_PROCESS_START_NOTREADY_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s' but either crashed or did not reponse or did not listen on the given port '%d', ErrorCode = '0x%x'"
#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_RECYCLE_APPOFFLINE_MSG L"App_offline file '%s' was detected."

View File

@ -0,0 +1,110 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
//
// *_HEADER_HASH maps strings to UlHeader* values
//
#define UNKNOWN_INDEX (0xFFFFFFFF)
struct HEADER_RECORD
{
PCSTR _pszName;
ULONG _ulHeaderIndex;
};
class RESPONSE_HEADER_HASH: public HASH_TABLE<HEADER_RECORD, PCSTR>
{
public:
RESPONSE_HEADER_HASH()
{}
VOID
ReferenceRecord(
HEADER_RECORD *
)
{}
VOID
DereferenceRecord(
HEADER_RECORD *
)
{}
PCSTR
ExtractKey(
HEADER_RECORD * pRecord
)
{
return pRecord->_pszName;
}
DWORD
CalcKeyHash(
PCSTR key
)
{
return HashStringNoCase(key);
}
BOOL
EqualKeys(
PCSTR key1,
PCSTR key2
)
{
return (_stricmp(key1, key2) == 0);
}
HRESULT
Initialize(
VOID
);
VOID
Terminate(
VOID
);
DWORD
GetIndex(
PCSTR pszName
)
{
HEADER_RECORD * pRecord = NULL;
FindKey(pszName, &pRecord);
if (pRecord != NULL)
{
return pRecord->_ulHeaderIndex;
}
return UNKNOWN_INDEX;
}
static
PCSTR
GetString(
ULONG ulIndex
)
{
if (ulIndex < HttpHeaderResponseMaximum)
{
DBG_ASSERT(sm_rgHeaders[ulIndex]._ulHeaderIndex == ulIndex);
return sm_rgHeaders[ulIndex]._pszName;
}
return NULL;
}
private:
static HEADER_RECORD sm_rgHeaders[];
RESPONSE_HEADER_HASH(const RESPONSE_HEADER_HASH &);
void operator=(const RESPONSE_HEADER_HASH &);
};
extern RESPONSE_HEADER_HASH * g_pResponseHeaderHash;

View File

@ -0,0 +1,325 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
#define MIN_PORT 1025
#define MAX_PORT 48000
#define MAX_RETRY 10
#define MAX_ACTIVE_CHILD_PROCESSES 16
#define LOCALHOST "127.0.0.1"
#define ASPNETCORE_PORT_STR L"ASPNETCORE_PORT"
#define ASPNETCORE_PORT_ENV_STR L"ASPNETCORE_PORT="
#define ASPNETCORE_APP_PATH_ENV_STR L"ASPNETCORE_APPL_PATH="
#define ASPNETCORE_APP_TOKEN_ENV_STR L"ASPNETCORE_TOKEN="
#define ASPNETCORE_APP_PATH_ENV_STR L"ASPNETCORE_APPL_PATH="
#define HOSTING_STARTUP_ASSEMBLIES_ENV_STR L"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES"
#define HOSTING_STARTUP_ASSEMBLIES_NAME L"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES="
#define HOSTING_STARTUP_ASSEMBLIES_VALUE L"Microsoft.AspNetCore.Server.IISIntegration"
#define ASPNETCORE_IIS_AUTH_ENV_STR L"ASPNETCORE_IIS_HTTPAUTH="
#define ASPNETCORE_IIS_AUTH_WINDOWS L"windows;"
#define ASPNETCORE_IIS_AUTH_BASIC L"basic;"
#define ASPNETCORE_IIS_AUTH_ANONYMOUS L"anonymous;"
#define ASPNETCORE_IIS_AUTH_NONE L"none"
class PROCESS_MANAGER;
class FORWARDER_CONNECTION;
class SERVER_PROCESS
{
public:
SERVER_PROCESS();
HRESULT
Initialize(
_In_ PROCESS_MANAGER *pProcessManager,
_In_ STRU *pszProcessExePath,
_In_ STRU *pszArguments,
_In_ DWORD dwStartupTimeLimitInMS,
_In_ DWORD dwShtudownTimeLimitInMS,
_In_ BOOL fWindowsAuthEnabled,
_In_ BOOL fBasicAuthEnabled,
_In_ BOOL fAnonymousAuthEnabled,
_In_ ENVIRONMENT_VAR_HASH* pEnvironmentVariables,
_In_ BOOL fStdoutLogEnabled,
_In_ STRU *pstruStdoutLogFile
);
HRESULT
StartProcess(
_In_ IHttpContext *context
);
HRESULT
SetWindowsAuthToken(
_In_ HANDLE hToken,
_Out_ LPHANDLE pTargeTokenHandle
);
BOOL
IsReady(
VOID
)
{
return m_fReady;
}
VOID
StopProcess(
VOID
);
DWORD
GetPort()
{
return m_dwPort;
}
VOID
ReferenceServerProcess(
VOID
)
{
InterlockedIncrement(&m_cRefs);
}
VOID
DereferenceServerProcess(
VOID
)
{
_ASSERT(m_cRefs != 0 );
if (InterlockedDecrement(&m_cRefs) == 0)
{
delete this;
}
}
virtual
~SERVER_PROCESS();
HRESULT
HandleProcessExit(
VOID
);
FORWARDER_CONNECTION*
QueryWinHttpConnection(
VOID
)
{
return m_pForwarderConnection;
}
static
VOID
CALLBACK
TimerCallback(
_In_ PTP_CALLBACK_INSTANCE Instance,
_In_ PVOID Context,
_In_ PTP_TIMER Timer
);
LPCWSTR
QueryPortStr()
{
return m_struPort.QueryStr();
}
LPCWSTR
QueryFullLogPath()
{
return m_struFullLogFile.QueryStr();
}
LPCSTR
QueryGuid()
{
return m_straGuid.QueryStr();
}
DWORD
QueryProcessGroupId()
{
return m_dwProcessId;
}
VOID
SendSignal(
VOID
);
private:
BOOL
IsDebuggerIsAttached(
VOID
);
HRESULT
StopAllProcessesInJobObject(
VOID
);
HRESULT
SetupStdHandles(
_In_ IHttpContext *context,
_In_ LPSTARTUPINFOW pStartupInfo
);
HRESULT
CheckIfServerIsUp(
_In_ DWORD dwPort,
_Out_ DWORD * pdwProcessId,
_Out_ BOOL * pfReady
);
HRESULT
RegisterProcessWait(
_In_ PHANDLE phWaitHandle,
_In_ HANDLE hProcessToWaitOn
);
HRESULT
GetChildProcessHandles(
);
HRESULT
SetupListenPort(
ENVIRONMENT_VAR_HASH *pEnvironmentVarTable
);
HRESULT
SetupAppPath(
IHttpContext* pContext,
ENVIRONMENT_VAR_HASH* pEnvironmentVarTable
);
HRESULT
SetupAppToken(
ENVIRONMENT_VAR_HASH* pEnvironmentVarTable
);
HRESULT
InitEnvironmentVariablesTable(
ENVIRONMENT_VAR_HASH** pEnvironmentVarTable
);
HRESULT
OutputEnvironmentVariables(
MULTISZ* pmszOutput,
ENVIRONMENT_VAR_HASH* pEnvironmentVarTable
);
HRESULT
SetupCommandLine(
STRU* pstrCommandLine
);
HRESULT
PostStartCheck(
const STRU* const pStruCommandline,
STRU* pStruErrorMessage
);
HRESULT
GetRandomPort(
DWORD* pdwPickedPort,
DWORD dwExcludedPort
);
DWORD
GetNumberOfDigits(
_In_ DWORD dwNumber
)
{
DWORD digits = 0;
if( dwNumber == 0 )
{
digits = 1;
goto Finished;
}
while( dwNumber > 0)
{
dwNumber = dwNumber / 10;
digits ++;
}
Finished:
return digits;
}
static
VOID
SendShutDownSignal(
LPVOID lpParam
);
VOID
SendShutDownSignalInternal(
VOID
);
HRESULT
SendShutdownHttpMessage(
VOID
);
VOID
TerminateBackendProcess(
VOID
);
FORWARDER_CONNECTION *m_pForwarderConnection;
BOOL m_fStdoutLogEnabled;
BOOL m_fWindowsAuthEnabled;
BOOL m_fBasicAuthEnabled;
BOOL m_fAnonymousAuthEnabled;
STTIMER m_Timer;
SOCKET m_socket;
STRU m_struLogFile;
STRU m_struFullLogFile;
STRU m_ProcessPath;
STRU m_Arguments;
STRU m_struAppPath;
STRU m_struAppFullPath;
STRU m_struPort;
STRU m_pszRootApplicationPath;
volatile LONG m_lStopping;
volatile BOOL m_fReady;
mutable LONG m_cRefs;
DWORD m_dwPort;
DWORD m_dwStartupTimeLimitInMS;
DWORD m_dwShutdownTimeLimitInMS;
DWORD m_cChildProcess;
DWORD m_dwChildProcessIds[MAX_ACTIVE_CHILD_PROCESSES];
DWORD m_dwProcessId;
DWORD m_dwListeningProcessId;
STRA m_straGuid;
HANDLE m_hJobObject;
HANDLE m_hStdoutHandle;
//
// m_hProcessHandle is the handle to process this object creates.
//
HANDLE m_hProcessHandle;
HANDLE m_hListeningProcessHandle;
HANDLE m_hProcessWaitHandle;
HANDLE m_hShutdownHandle;
//
// m_hChildProcessHandle is the handle to process created by
// m_hProcessHandle process if it does.
//
HANDLE m_hChildProcessHandles[MAX_ACTIVE_CHILD_PROCESSES];
HANDLE m_hChildProcessWaitHandles[MAX_ACTIVE_CHILD_PROCESSES];
PROCESS_MANAGER *m_pProcessManager;
ENVIRONMENT_VAR_HASH *m_pEnvironmentVarTable ;
};

View File

@ -0,0 +1,221 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
class FORWARDING_HANDLER;
class WEBSOCKET_HANDLER
{
public:
WEBSOCKET_HANDLER();
virtual
~WEBSOCKET_HANDLER();
static
HRESULT
StaticInitialize(
BOOL fEnableReferenceTraceLogging
);
static
VOID
StaticTerminate(
VOID
);
VOID
Terminate(
VOID
);
VOID
TerminateRequest(
VOID
)
{
Cleanup(ServerStateUnavailable);
}
HRESULT
ProcessRequest(
FORWARDING_HANDLER *pHandler,
IHttpContext * pHttpContext,
HINTERNET hRequest,
BOOL* fHandleCreated
);
REQUEST_NOTIFICATION_STATUS
OnAsyncCompletion(
VOID
);
HRESULT
OnWinHttpSendComplete(
WINHTTP_WEB_SOCKET_STATUS * pCompletionStatus
);
HRESULT
OnWinHttpShutdownComplete(
VOID
);
HRESULT
OnWinHttpReceiveComplete(
WINHTTP_WEB_SOCKET_STATUS * pCompletionStatus
);
HRESULT
OnWinHttpIoError(
WINHTTP_WEB_SOCKET_ASYNC_RESULT *pCompletionStatus
);
private:
enum CleanupReason
{
CleanupReasonUnknown = 0,
IdleTimeout = 1,
ConnectFailed = 2,
ClientDisconnect = 3,
ServerDisconnect = 4,
ServerStateUnavailable = 5
};
WEBSOCKET_HANDLER(const WEBSOCKET_HANDLER &);
void operator=(const WEBSOCKET_HANDLER &);
VOID
InsertRequest(
VOID
);
VOID
RemoveRequest(
VOID
);
static
VOID
WINAPI
OnReadIoCompletion(
HRESULT hrError,
VOID * pvCompletionContext,
DWORD cbIO,
BOOL fUTF8Encoded,
BOOL fFinalFragment,
BOOL fClose
);
static
VOID
WINAPI
OnWriteIoCompletion(
HRESULT hrError,
VOID * pvCompletionContext,
DWORD cbIO,
BOOL fUTF8Encoded,
BOOL fFinalFragment,
BOOL fClose
);
VOID
Cleanup(
CleanupReason reason
);
HRESULT
DoIisWebSocketReceive(
VOID
);
HRESULT
DoWinHttpWebSocketReceive(
VOID
);
HRESULT
DoIisWebSocketSend(
DWORD cbData,
WINHTTP_WEB_SOCKET_BUFFER_TYPE eBufferType
);
HRESULT
DoWinHttpWebSocketSend(
DWORD cbData,
WINHTTP_WEB_SOCKET_BUFFER_TYPE eBufferType
);
HRESULT
OnIisSendComplete(
HRESULT hrError,
DWORD cbIO
);
HRESULT
OnIisReceiveComplete(
HRESULT hrError,
DWORD cbIO,
BOOL fUTF8Encoded,
BOOL fFinalFragment,
BOOL fClose
);
VOID
IncrementOutstandingIo(
VOID
);
VOID
DecrementOutstandingIo(
VOID
);
VOID
IndicateCompletionToIIS(
VOID
);
private:
static const
DWORD RECEIVE_BUFFER_SIZE = 4*1024;
LIST_ENTRY _listEntry;
IHttpContext3 * _pHttpContext;
IWebSocketContext * _pWebSocketContext;
FORWARDING_HANDLER *_pHandler;
HINTERNET _hWebSocketRequest;
BYTE _WinHttpReceiveBuffer[RECEIVE_BUFFER_SIZE];
BYTE _IisReceiveBuffer[RECEIVE_BUFFER_SIZE];
CRITICAL_SECTION _RequestLock;
LONG _dwOutstandingIo;
volatile
BOOL _fHandleClosed;
volatile
BOOL _fCleanupInProgress;
volatile
BOOL _fIndicateCompletionToIis;
volatile
BOOL _fReceivedCloseMsg;
static
LIST_ENTRY sm_RequestsListHead;
static
SRWLOCK sm_RequestsListLock;
static
TRACE_LOG * sm_pTraceLog;
};

View File

@ -0,0 +1,91 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
typedef
HINTERNET
(WINAPI * PFN_WINHTTP_WEBSOCKET_COMPLETE_UPGRADE)(
_In_ HINTERNET hRequest,
_In_opt_ DWORD_PTR pContext
);
typedef
DWORD
(WINAPI * PFN_WINHTTP_WEBSOCKET_SEND)(
_In_ HINTERNET hWebSocket,
_In_ WINHTTP_WEB_SOCKET_BUFFER_TYPE eBufferType,
_In_reads_opt_(dwBufferLength) PVOID pvBuffer,
_In_ DWORD dwBufferLength
);
typedef
DWORD
(WINAPI * PFN_WINHTTP_WEBSOCKET_RECEIVE)(
_In_ HINTERNET hWebSocket,
_Out_writes_bytes_to_(dwBufferLength, *pdwBytesRead) PVOID pvBuffer,
_In_ DWORD dwBufferLength,
_Out_range_(0, dwBufferLength) DWORD *pdwBytesRead,
_Out_ WINHTTP_WEB_SOCKET_BUFFER_TYPE *peBufferType
);
typedef
DWORD
(WINAPI * PFN_WINHTTP_WEBSOCKET_SHUTDOWN)(
_In_ HINTERNET hWebSocket,
_In_ USHORT usStatus,
_In_reads_bytes_opt_(dwReasonLength) PVOID pvReason,
_In_range_(0, WINHTTP_WEB_SOCKET_MAX_CLOSE_REASON_LENGTH) DWORD dwReasonLength
);
typedef
DWORD
(WINAPI * PFN_WINHTTP_WEBSOCKET_QUERY_CLOSE_STATUS)(
_In_ HINTERNET hWebSocket,
_Out_ USHORT *pusStatus,
_Out_writes_bytes_to_opt_(dwReasonLength, *pdwReasonLengthConsumed) PVOID pvReason,
_In_range_(0, WINHTTP_WEB_SOCKET_MAX_CLOSE_REASON_LENGTH) DWORD dwReasonLength,
_Out_range_(0, WINHTTP_WEB_SOCKET_MAX_CLOSE_REASON_LENGTH) DWORD *pdwReasonLengthConsumed
);
class WINHTTP_HELPER
{
public:
static
HRESULT
StaticInitialize();
static
VOID
GetFlagsFromBufferType(
__in WINHTTP_WEB_SOCKET_BUFFER_TYPE BufferType,
__out BOOL * pfUtf8Encoded,
__out BOOL * pfFinalFragment,
__out BOOL * pfClose
);
static
VOID
GetBufferTypeFromFlags(
__in BOOL fUtf8Encoded,
__in BOOL fFinalFragment,
__in BOOL fClose,
__out WINHTTP_WEB_SOCKET_BUFFER_TYPE* pBufferType
);
static
PFN_WINHTTP_WEBSOCKET_COMPLETE_UPGRADE sm_pfnWinHttpWebSocketCompleteUpgrade;
static
PFN_WINHTTP_WEBSOCKET_SEND sm_pfnWinHttpWebSocketSend;
static
PFN_WINHTTP_WEBSOCKET_RECEIVE sm_pfnWinHttpWebSocketReceive;
static
PFN_WINHTTP_WEBSOCKET_SHUTDOWN sm_pfnWinHttpWebSocketShutdown;
static
PFN_WINHTTP_WEBSOCKET_QUERY_CLOSE_STATUS sm_pfnWinHttpWebSocketQueryCloseStatus;
};

View File

@ -0,0 +1,2 @@
LANGUAGE 0x9,0x1
1 11 "MSG00001.bin"

View File

@ -11,37 +11,29 @@
-->
<configSchema>
<sectionSchema name="system.webServer/aspNetCore">
<attribute name="processPath" type="string" expanded="true"/>
<attribute name="arguments" type="string" expanded="true" defaultValue=""/>
<attribute name="startupTimeLimit" type="uint" defaultValue="120" validationType="integerRange" validationParameter="0,3600"/>
<!-- in seconds -->
<attribute name="shutdownTimeLimit" type="uint" defaultValue="10" validationType="integerRange" validationParameter="0,600"/>
<!-- in seconds -->
<attribute name="rapidFailsPerMinute" type="uint" defaultValue="10" validationType="integerRange" validationParameter="0,100"/>
<attribute name="requestTimeout" type="timeSpan" defaultValue="00:02:00" validationType="timeSpanRange" validationParameter="0,1296000,1"/>
<attribute name="stdoutLogEnabled" type="bool" defaultValue="false" />
<attribute name="stdoutLogFile" type="string" defaultValue=".\aspnetcore-stdout" expanded="true"/>
<attribute name="processesPerApplication" type="uint" defaultValue="1" validationType="integerRange" validationParameter="1,100"/>
<attribute name="forwardWindowsAuthToken" type="bool" defaultValue="true" />
<attribute name="disableStartUpErrorPage" type="bool" defaultValue="false" />
<attribute name="hostingModel" type="string" />
<element name="recycleOnFileChange">
<collection addElement="file" clearElement="clear">
<attribute name="path" type="string" required="true" validationType="nonEmptyString" expanded="true"/>
</collection>
</element>
<element name="environmentVariables">
<collection addElement="environmentVariable" clearElement="clear" >
<attribute name="name" type="string" required="true" validationType="nonEmptyString"/>
<attribute name="value" type="string" required="true"/>
</collection>
</element>
<element name="handlerSettings">
<collection addElement="handlerSetting" clearElement="clear" >
<attribute name="name" type="string" required="true" validationType="nonEmptyString"/>
<attribute name="value" type="string" required="true"/>
</collection>
</element>
</sectionSchema>
</configSchema>
<sectionSchema name="system.webServer/aspNetCore">
<attribute name="processPath" type="string" expanded="true"/>
<attribute name="arguments" type="string" expanded="true" defaultValue=""/>
<attribute name="startupTimeLimit" type="uint" defaultValue="120" validationType="integerRange" validationParameter="0,3600"/> <!-- in seconds -->
<attribute name="shutdownTimeLimit" type="uint" defaultValue="10" validationType="integerRange" validationParameter="0,600"/> <!-- in seconds -->
<attribute name="rapidFailsPerMinute" type="uint" defaultValue="10" validationType="integerRange" validationParameter="0,100"/>
<attribute name="requestTimeout" type="timeSpan" defaultValue="00:02:00" validationType="timeSpanRange" validationParameter="0,1296000,60"/>
<attribute name="stdoutLogEnabled" type="bool" defaultValue="false" />
<attribute name="stdoutLogFile" type="string" defaultValue=".\aspnetcore-stdout" expanded="true"/>
<attribute name="processesPerApplication" type="uint" defaultValue="1" validationType="integerRange" validationParameter="1,100"/>
<attribute name="forwardWindowsAuthToken" type="bool" defaultValue="true" />
<attribute name="disableStartUpErrorPage" type="bool" defaultValue="false" />
<attribute name="hostingModel" type="string" />
<element name="recycleOnFileChange">
<collection addElement="file" clearElement="clear">
<attribute name="path" type="string" required="true" validationType="nonEmptyString" expanded="true"/>
</collection>
</element>
<element name="environmentVariables">
<collection addElement="environmentVariable" clearElement="clear" >
<attribute name="name" type="string" required="true" validationType="nonEmptyString"/>
<attribute name="value" type="string" required="true"/>
</collection>
</element>
</sectionSchema>
</configSchema>

View File

@ -1,120 +0,0 @@
// Microsoft Visual C++ generated resource script.
//
#include <windows.h>
#include "version.h"
#include "..\CommonLib\Aspnetcore_msg.rc"
#include "..\CommonLib\resources.h"
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#define FileDescription "IIS AspNetCore Module. Commit: " CommitHash
/////////////////////////////////////////////////////////////////////////////
//
// 11
//
//1 11
//BEGIN
// 0x0001, 0x0000, 0x03e8, 0x0000, 0x03ed, 0x0000, 0x0010, 0x0000, 0x0010,
// 0x0001, 0x0025, 0x0031, 0x000d, 0x000a, 0x0000, 0x0000, 0x0010, 0x0001,
// 0x0025, 0x0031, 0x000d, 0x000a, 0x0000, 0x0000, 0x0010, 0x0001, 0x0025,
// 0x0031, 0x000d, 0x000a, 0x0000, 0x0000, 0x0010, 0x0001, 0x0025, 0x0031,
// 0x000d, 0x000a, 0x0000, 0x0000, 0x0010, 0x0001, 0x0025, 0x0031, 0x000d,
// 0x000a, 0x0000, 0x0000, 0x0010, 0x0001, 0x0025, 0x0031, 0x000d, 0x000a,
// 0x0000, 0x0000
//END
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"..\CommonLib\resources.h\0"
END
2 TEXTINCLUDE
BEGIN
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION FileVersion
PRODUCTVERSION ProductVersion
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Microsoft Corporation"
VALUE "FileDescription", FileDescription
VALUE "FileVersion", FileVersionStr
VALUE "InternalName", "aspnetcore"
VALUE "LegalCopyright", "Copyright (C) Microsoft Corporation"
VALUE "OriginalFilename", "aspnetcore.dll"
VALUE "ProductName", "ASP.NET Core Module"
VALUE "ProductVersion", ProductVersionStr
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
/////////////////////////////////////////////////////////////////////////////
//
// String Table
//
STRINGTABLE
BEGIN
IDS_INVALID_PROPERTY "Property name '%s' in system.webServer/aspNetCore section has invalid value '%s' which does not conform to the prescribed format"
IDS_SERVER_ERROR "There was a connection error while trying to route the request."
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

View File

@ -0,0 +1,164 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "precomp.hxx"
APPLICATION::~APPLICATION()
{
if (m_pAppOfflineHtm != NULL)
{
m_pAppOfflineHtm->DereferenceAppOfflineHtm();
m_pAppOfflineHtm = NULL;
}
if (m_pFileWatcherEntry != NULL)
{
// Mark the entry as invalid,
// StopMonitor will close the file handle and trigger a FCN
// the entry will delete itself when processing this FCN
m_pFileWatcherEntry->MarkEntryInValid();
m_pFileWatcherEntry->StopMonitor();
m_pFileWatcherEntry = NULL;
}
if (m_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);
return hr;
}
VOID
APPLICATION::UpdateAppOfflineFileHandle()
{
STRU strFilePath;
PATH::ConvertPathToFullPath(L".\\app_offline.htm", m_strAppPhysicalPath.QueryStr(), &strFilePath);
APP_OFFLINE_HTM *pOldAppOfflineHtm = NULL;
APP_OFFLINE_HTM *pNewAppOfflineHtm = NULL;
if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(strFilePath.QueryStr()) && GetLastError() == ERROR_FILE_NOT_FOUND)
{
m_fAppOfflineFound = FALSE;
}
else
{
m_fAppOfflineFound = TRUE;
//
// 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 )
{
if (pNewAppOfflineHtm->Load())
{
//
// loaded the new app_offline.htm
//
pOldAppOfflineHtm = (APP_OFFLINE_HTM *)InterlockedExchangePointer((VOID**)&m_pAppOfflineHtm, pNewAppOfflineHtm);
if (pOldAppOfflineHtm != NULL)
{
pOldAppOfflineHtm->DereferenceAppOfflineHtm();
pOldAppOfflineHtm = NULL;
}
}
else
{
// ignored the new app_offline file because the file does not exist.
pNewAppOfflineHtm->DereferenceAppOfflineHtm();
pNewAppOfflineHtm = NULL;
}
}
}
}

View File

@ -1,81 +1,41 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "precomp.hxx"
// The application manager is a singleton across ANCM.
APPLICATION_MANAGER* APPLICATION_MANAGER::sm_pApplicationManager = NULL;
//
// Retrieves the application info from the application manager
// Will create the application info if it isn't initalized
//
HRESULT
APPLICATION_MANAGER::GetOrCreateApplicationInfo(
_In_ IHttpServer* pServer,
_In_ ASPNETCORE_CONFIG* pConfig,
_Out_ APPLICATION_INFO ** ppApplicationInfo
APPLICATION_MANAGER::GetApplication(
_In_ IHttpContext* pContext,
_Out_ APPLICATION ** ppApplication
)
{
HRESULT hr = S_OK;
APPLICATION_INFO *pApplicationInfo = NULL;
APPLICATION_INFO_KEY key;
BOOL fExclusiveLock = FALSE;
BOOL fMixedHostingModelError = FALSE;
BOOL fDuplicatedInProcessApp = FALSE;
PCWSTR pszApplicationId = NULL;
HRESULT hr = S_OK;
APPLICATION *pApplication = NULL;
APPLICATION_KEY key;
BOOL fExclusiveLock = FALSE;
PCWSTR pszApplicationId = NULL;
DBG_ASSERT(pServer);
DBG_ASSERT(pConfig);
DBG_ASSERT(ppApplicationInfo);
*ppApplication = NULL;
DBG_ASSERT(pContext != NULL);
DBG_ASSERT(pContext->GetApplication() != NULL);
pszApplicationId = pContext->GetApplication()->GetApplicationId();
*ppApplicationInfo = NULL;
// The configuration path is unique for each application and is used for the
// key in the applicationInfoHash.
pszApplicationId = pConfig->QueryConfigPath()->QueryStr();
hr = key.Initialize(pszApplicationId);
if (FAILED(hr))
{
goto Finished;
}
// When accessing the m_pApplicationInfoHash, we need to acquire the application manager
// lock to avoid races on setting state.
AcquireSRWLockShared(&m_srwLock);
if (g_fInShutdown)
m_pApplicationHash->FindKey(&key, ppApplication);
if (*ppApplication == NULL)
{
ReleaseSRWLockShared(&m_srwLock);
hr = HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS);
goto Finished;
}
m_pApplicationInfoHash->FindKey(&key, ppApplicationInfo);
ReleaseSRWLockShared(&m_srwLock);
if (*ppApplicationInfo == NULL)
{
// Check which hosting model we want to support
switch (pConfig->QueryHostingModel())
{
case HOSTING_IN_PROCESS:
if (m_pApplicationInfoHash->Count() > 0)
{
// Only one inprocess app is allowed per IIS worker process
fDuplicatedInProcessApp = TRUE;
hr = HRESULT_FROM_WIN32(ERROR_APP_INIT_FAILURE);
goto Finished;
}
break;
case HOSTING_OUT_PROCESS:
break;
default:
hr = E_UNEXPECTED;
goto Finished;
}
pApplicationInfo = new APPLICATION_INFO(pServer);
if (pApplicationInfo == NULL)
pApplication = new APPLICATION();
if (pApplication == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
@ -83,353 +43,136 @@ APPLICATION_MANAGER::GetOrCreateApplicationInfo(
AcquireSRWLockExclusive(&m_srwLock);
fExclusiveLock = TRUE;
if (g_fInShutdown)
{
// Already in shuting down. No need to create the application
hr = HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS);
goto Finished;
}
m_pApplicationInfoHash->FindKey(&key, ppApplicationInfo);
m_pApplicationHash->FindKey(&key, ppApplication);
if (*ppApplicationInfo != NULL)
if (*ppApplication != NULL)
{
// someone else created the application
delete pApplicationInfo;
pApplicationInfo = NULL;
delete pApplication;
pApplication = NULL;
goto Finished;
}
// 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 = pApplicationInfo->Initialize(pConfig, m_pFileWatcher);
hr = pApplication->Initialize(this, pszApplicationId, pContext->GetApplication()->GetApplicationPhysicalPath());
if (FAILED(hr))
{
goto Finished;
}
hr = m_pApplicationInfoHash->InsertRecord( pApplicationInfo );
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();
}
*ppApplicationInfo = pApplicationInfo;
pApplicationInfo->StartMonitoringAppOffline();
// Release the locko after starting to monitor for app offline to avoid races with it being dropped.
ReleaseSRWLockExclusive(&m_srwLock);
fExclusiveLock = FALSE;
pApplicationInfo = NULL;
pApplication->StartMonitoringAppOffline();
*ppApplication = pApplication;
pApplication = NULL;
}
Finished:
if (fExclusiveLock == TRUE)
ReleaseSRWLockExclusive(&m_srwLock);
if (FAILED(hr))
{
if (pApplication != NULL)
{
pApplication->DereferenceApplication();
pApplication = NULL;
}
}
return hr;
}
HRESULT
APPLICATION_MANAGER::RecycleApplication(
_In_ LPCWSTR pszApplication
)
{
HRESULT hr = S_OK;
APPLICATION_KEY key;
hr = key.Initialize(pszApplication);
if (FAILED(hr))
{
goto Finished;
}
AcquireSRWLockExclusive(&m_srwLock);
m_pApplicationHash->DeleteKey(&key);
ReleaseSRWLockExclusive(&m_srwLock);
Finished:
return hr;
}
HRESULT
APPLICATION_MANAGER::Get502ErrorPage(
_Out_ HTTP_DATA_CHUNK** ppErrorPage
)
{
HRESULT hr = S_OK;
BOOL fExclusiveLock = FALSE;
HTTP_DATA_CHUNK *pHttp502ErrorPage = NULL;
DBG_ASSERT(ppErrorPage != NULL);
//on-demand create the error page
if (m_pHttp502ErrorPage != NULL)
{
*ppErrorPage = m_pHttp502ErrorPage;
}
else
{
AcquireSRWLockExclusive(&m_srwLock);
fExclusiveLock = TRUE;
if (m_pHttp502ErrorPage != NULL)
{
*ppErrorPage = m_pHttp502ErrorPage;
}
else
{
size_t maxsize = 5000;
pHttp502ErrorPage = new HTTP_DATA_CHUNK();
if (pHttp502ErrorPage == NULL)
{
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
goto Finished;
}
pHttp502ErrorPage->DataChunkType = HttpDataChunkFromMemory;
pHttp502ErrorPage->FromMemory.pBuffer = (PVOID)m_pstrErrorInfo;
pHttp502ErrorPage->FromMemory.BufferLength = (ULONG)strnlen(m_pstrErrorInfo, maxsize); //(ULONG)(wcslen(m_pstrErrorInfo)); // *sizeof(WCHAR);
if(m_pHttp502ErrorPage != NULL)
{
delete m_pHttp502ErrorPage;
}
m_pHttp502ErrorPage = pHttp502ErrorPage;
*ppErrorPage = m_pHttp502ErrorPage;
}
}
Finished:
if (fExclusiveLock)
{
ReleaseSRWLockExclusive(&m_srwLock);
}
if (pApplicationInfo != NULL)
{
pApplicationInfo->DereferenceApplicationInfo();
pApplicationInfo = NULL;
}
if (FAILED(hr))
{
if (fDuplicatedInProcessApp)
if (pHttp502ErrorPage != NULL)
{
UTILITY::LogEventF(g_hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP,
ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP_MSG,
pszApplicationId);
}
else if (fMixedHostingModelError)
{
UTILITY::LogEventF(g_hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR,
ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG,
pszApplicationId,
pConfig->QueryHostingModel());
}
else
{
UTILITY::LogEventF(g_hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_ADD_APPLICATION_ERROR,
ASPNETCORE_EVENT_ADD_APPLICATION_ERROR_MSG,
pszApplicationId,
hr);
delete pHttp502ErrorPage;
}
}
return hr;
}
//
// If the application's configuration was changed,
// append the configuration path to the config change context.
//
BOOL
APPLICATION_MANAGER::FindConfigChangedApplication(
_In_ APPLICATION_INFO * pEntry,
_In_ PVOID pvContext)
{
DBG_ASSERT(pEntry);
DBG_ASSERT(pvContext);
// Config Change context contains the original config path that changed
// and a multiStr containing
CONFIG_CHANGE_CONTEXT* pContext = static_cast<CONFIG_CHANGE_CONTEXT*>(pvContext);
STRU* pstruConfigPath = pEntry->QueryConfig()->QueryConfigPath();
// check if the application path contains our app/subapp by seeing if the config path
// starts with the notification path.
BOOL fChanged = pstruConfigPath->StartsWith(pContext->pstrPath, true);
if (fChanged)
{
DWORD dwLen = (DWORD)wcslen(pContext->pstrPath);
WCHAR wChar = pstruConfigPath->QueryStr()[dwLen];
// We need to check that the last character of the config path
// is either a null terminator or a slash.
// This checks the case where the config path was
// MACHINE/WEBROOT/site and your site path is MACHINE/WEBROOT/siteTest
if (wChar != L'\0' && wChar != L'/')
{
// not current app or sub app
fChanged = FALSE;
}
else
{
pContext->MultiSz.Append(*pstruConfigPath);
}
}
return fChanged;
}
//
// Finds any applications affected by a configuration change and calls Recycle on them
// InProcess: Triggers g_httpServer->RecycleProcess() and keep the application inside of the manager.
// This will cause a shutdown event to occur through the global stop listening event.
// OutOfProcess: Removes all applications in the application manager and calls Recycle, which will call Shutdown,
// on each application.
//
HRESULT
APPLICATION_MANAGER::RecycleApplicationFromManager(
_In_ LPCWSTR pszApplicationId
)
{
HRESULT hr = S_OK;
APPLICATION_INFO_KEY key;
DWORD dwPreviousCounter = 0;
APPLICATION_INFO_HASH* table = NULL;
CONFIG_CHANGE_CONTEXT context;
BOOL fKeepTable = FALSE;
if (g_fInShutdown)
{
// We are already shutting down, ignore this event as a global configuration change event
// can occur after global stop listening for some reason.
return hr;
}
AcquireSRWLockExclusive(&m_srwLock);
if (g_fInShutdown)
{
ReleaseSRWLockExclusive(&m_srwLock);
return hr;
}
hr = key.Initialize(pszApplicationId);
if (FAILED(hr))
{
goto Finished;
}
// Make a shallow copy of existing hashtable as we may need to remove nodes
// This will be used for finding differences in which applications are affected by a config change.
table = new APPLICATION_INFO_HASH();
if (table == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
// few application expected, small bucket size for hash table
if (FAILED(hr = table->Initialize(17 /*prime*/)))
{
goto Finished;
}
context.pstrPath = pszApplicationId;
// Keep track of the preview count of applications to know whether there are applications to delete
dwPreviousCounter = m_pApplicationInfoHash->Count();
// We don't want to hold the application manager lock for long time as it will block all incoming requests
// Don't call application shutdown inside the lock
m_pApplicationInfoHash->Apply(APPLICATION_INFO_HASH::ReferenceCopyToTable, static_cast<PVOID>(table));
DBG_ASSERT(dwPreviousCounter == table->Count());
// Removed the applications which are impacted by the configurtion change
m_pApplicationInfoHash->DeleteIf(FindConfigChangedApplication, (PVOID)&context);
if (dwPreviousCounter != m_pApplicationInfoHash->Count())
{
if (m_hostingModel == HOSTING_IN_PROCESS)
{
// When we are inprocess, we need to keep the application the
// application manager that is being deleted. This is because we will always need to recycle the worker
// process and any requests that hit this worker process must be rejected (while out of process can
// start a new dotnet process). We will immediately call Recycle after this call.
DBG_ASSERT(m_pApplicationInfoHash->Count() == 0);
delete m_pApplicationInfoHash;
// We don't want to delete the table as m_pApplicationInfoHash = table
fKeepTable = TRUE;
m_pApplicationInfoHash = table;
}
}
if (m_pApplicationInfoHash->Count() == 0)
{
m_hostingModel = HOSTING_UNKNOWN;
}
ReleaseSRWLockExclusive(&m_srwLock);
// If we receive a request at this point.
// OutOfProcess: we will create a new application with new configuration
// InProcess: the request would have to be rejected, as we are about to call g_HttpServer->RecycleProcess
// on the worker proocess
if (!context.MultiSz.IsEmpty())
{
PCWSTR path = context.MultiSz.First();
// Iterate through each of the paths that were shut down,
// calling RecycleApplication on each of them.
while (path != NULL)
{
APPLICATION_INFO* pRecord;
// Application got recycled. Log an event
UTILITY::LogEventF(g_hEventLog,
EVENTLOG_INFORMATION_TYPE,
ASPNETCORE_EVENT_RECYCLE_CONFIGURATION,
ASPNETCORE_EVENT_RECYCLE_CONFIGURATION_MSG,
path);
hr = key.Initialize(path);
if (FAILED(hr))
{
goto Finished;
}
table->FindKey(&key, &pRecord);
DBG_ASSERT(pRecord != NULL);
// RecycleApplication is called on a separate thread.
pRecord->RecycleApplication();
pRecord->DereferenceApplicationInfo();
path = context.MultiSz.Next(path);
}
}
Finished:
if (table != NULL && !fKeepTable)
{
table->Clear();
delete table;
}
if (FAILED(hr))
{
// Failed to recycle an application. Log an event
UTILITY::LogEventF(g_hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_RECYCLE_APP_FAILURE,
ASPNETCORE_EVENT_RECYCLE_FAILURE_CONFIGURATION_MSG,
pszApplicationId);
// Need to recycle the process as we cannot recycle the application
if (!g_fRecycleProcessCalled)
{
g_fRecycleProcessCalled = TRUE;
g_pHttpServer->RecycleProcess(L"AspNetCore Recycle Process on Demand Due Application Recycle Error");
}
}
return hr;
}
//
// Shutsdown all applications in the application hashtable
// Only called by OnGlobalStopListening.
//
VOID
APPLICATION_MANAGER::ShutDown()
{
// We are guaranteed to only have one outstanding OnGlobalStopListening event at a time
// However, it is possible to receive multiple OnGlobalStopListening events
// Protect against this by checking if we already shut down.
g_fInShutdown = TRUE;
if (m_pApplicationInfoHash != NULL)
{
if (m_pFileWatcher != NULL)
{
delete m_pFileWatcher;
m_pFileWatcher = NULL;
}
DBG_ASSERT(m_pApplicationInfoHash);
// During shutdown we lock until we delete the application
AcquireSRWLockExclusive(&m_srwLock);
// Call shutdown on each application in the application manager
m_pApplicationInfoHash->Apply(ShutdownApplication, NULL);
m_pApplicationInfoHash->Clear();
delete m_pApplicationInfoHash;
m_pApplicationInfoHash = NULL;
ReleaseSRWLockExclusive(&m_srwLock);
}
}
//
// Calls shutdown on each application. ApplicationManager's lock is held for duration of
// each shutdown call, guaranteeing another application cannot be created.
//
// static
VOID
APPLICATION_MANAGER::ShutdownApplication(
_In_ APPLICATION_INFO * pEntry,
_In_ PVOID pvContext
)
{
UNREFERENCED_PARAMETER(pvContext);
DBG_ASSERT(pEntry != NULL);
pEntry->ShutDownApplication();
}

View File

@ -0,0 +1,416 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "precomp.hxx"
ASPNETCORE_CONFIG::~ASPNETCORE_CONFIG()
{
//
// the destructor will be called once IIS decides to recycle the module context (i.e., application)
//
if (!m_struApplication.IsEmpty())
{
APPLICATION_MANAGER::GetInstance()->RecycleApplication(m_struApplication.QueryStr());
}
if(m_pEnvironmentVariables != NULL)
{
m_pEnvironmentVariables->Clear();
delete m_pEnvironmentVariables;
m_pEnvironmentVariables = NULL;
}
}
HRESULT
ASPNETCORE_CONFIG::GetConfig(
_In_ IHttpContext *pHttpContext,
_Out_ ASPNETCORE_CONFIG **ppAspNetCoreConfig
)
{
HRESULT hr = S_OK;
IHttpApplication *pHttpApplication = pHttpContext->GetApplication();
ASPNETCORE_CONFIG *pAspNetCoreConfig = NULL;
if (ppAspNetCoreConfig == NULL)
{
hr = E_INVALIDARG;
goto Finished;
}
*ppAspNetCoreConfig = NULL;
// potential bug if user sepcific config at virtual dir level
pAspNetCoreConfig = (ASPNETCORE_CONFIG*)
pHttpApplication->GetModuleContextContainer()->GetModuleContext(g_pModuleId);
if (pAspNetCoreConfig != NULL)
{
*ppAspNetCoreConfig = pAspNetCoreConfig;
pAspNetCoreConfig = NULL;
goto Finished;
}
pAspNetCoreConfig = new ASPNETCORE_CONFIG;
if (pAspNetCoreConfig == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
hr = pAspNetCoreConfig->Populate(pHttpContext);
if (FAILED(hr))
{
goto Finished;
}
hr = pHttpApplication->GetModuleContextContainer()->
SetModuleContext(pAspNetCoreConfig, g_pModuleId);
if (FAILED(hr))
{
if (hr == HRESULT_FROM_WIN32(ERROR_ALREADY_ASSIGNED))
{
delete pAspNetCoreConfig;
pAspNetCoreConfig = (ASPNETCORE_CONFIG*)pHttpApplication->
GetModuleContextContainer()->
GetModuleContext(g_pModuleId);
_ASSERT(pAspNetCoreConfig != NULL);
hr = S_OK;
}
else
{
goto Finished;
}
}
else
{
// set appliction info here instead of inside Populate()
// as the destructor will delete the backend process
hr = pAspNetCoreConfig->QueryApplicationPath()->Copy(pHttpApplication->GetApplicationId());
if (FAILED(hr))
{
goto Finished;
}
}
*ppAspNetCoreConfig = pAspNetCoreConfig;
pAspNetCoreConfig = NULL;
Finished:
if (pAspNetCoreConfig != NULL)
{
delete pAspNetCoreConfig;
pAspNetCoreConfig = NULL;
}
return hr;
}
HRESULT
ASPNETCORE_CONFIG::Populate(
IHttpContext *pHttpContext
)
{
HRESULT hr = S_OK;
STACK_STRU(strSiteConfigPath, 256);
STRU strEnvName;
STRU strEnvValue;
STRU strExpandedEnvValue;
IAppHostAdminManager *pAdminManager = NULL;
IAppHostElement *pAspNetCoreElement = NULL;
IAppHostElement *pWindowsAuthenticationElement = NULL;
IAppHostElement *pBasicAuthenticationElement = NULL;
IAppHostElement *pAnonymousAuthenticationElement = NULL;
IAppHostElement *pEnvVarList = NULL;
IAppHostElement *pEnvVar = NULL;
IAppHostElementCollection *pEnvVarCollection = NULL;
ULONGLONG ullRawTimeSpan = 0;
ENUM_INDEX index;
ENVIRONMENT_VAR_ENTRY* pEntry = NULL;
m_pEnvironmentVariables = new ENVIRONMENT_VAR_HASH();
if (m_pEnvironmentVariables == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
if (FAILED(hr = m_pEnvironmentVariables->Initialize(37 /*prime*/)))
{
delete m_pEnvironmentVariables;
m_pEnvironmentVariables = NULL;
goto Finished;
}
pAdminManager = g_pHttpServer->GetAdminManager();
hr = strSiteConfigPath.Copy(pHttpContext->GetApplication()->GetAppConfigPath());
if (FAILED(hr))
{
goto Finished;
}
hr = pAdminManager->GetAdminSection(CS_WINDOWS_AUTHENTICATION_SECTION,
strSiteConfigPath.QueryStr(),
&pWindowsAuthenticationElement);
if (FAILED(hr))
{
// assume the corresponding authen was not enabled
// as the section may get deleted by user in some HWC case
// ToDo: log a warning to event log
m_fWindowsAuthEnabled = FALSE;
}
else
{
hr = GetElementBoolProperty(pWindowsAuthenticationElement,
CS_AUTHENTICATION_ENABLED,
&m_fWindowsAuthEnabled);
if (FAILED(hr))
{
goto Finished;
}
}
hr = pAdminManager->GetAdminSection(CS_BASIC_AUTHENTICATION_SECTION,
strSiteConfigPath.QueryStr(),
&pBasicAuthenticationElement);
if (FAILED(hr))
{
m_fBasicAuthEnabled = FALSE;
}
else
{
hr = GetElementBoolProperty(pBasicAuthenticationElement,
CS_AUTHENTICATION_ENABLED,
&m_fBasicAuthEnabled);
if (FAILED(hr))
{
goto Finished;
}
}
hr = pAdminManager->GetAdminSection(CS_ANONYMOUS_AUTHENTICATION_SECTION,
strSiteConfigPath.QueryStr(),
&pAnonymousAuthenticationElement);
if (FAILED(hr))
{
m_fAnonymousAuthEnabled = FALSE;
}
else
{
hr = GetElementBoolProperty(pAnonymousAuthenticationElement,
CS_AUTHENTICATION_ENABLED,
&m_fAnonymousAuthEnabled);
if (FAILED(hr))
{
goto Finished;
}
}
hr = pAdminManager->GetAdminSection(CS_ASPNETCORE_SECTION,
strSiteConfigPath.QueryStr(),
&pAspNetCoreElement);
if (FAILED(hr))
{
goto Finished;
}
hr = GetElementStringProperty(pAspNetCoreElement,
CS_ASPNETCORE_PROCESS_EXE_PATH,
&m_struProcessPath);
if (FAILED(hr))
{
goto Finished;
}
hr = GetElementStringProperty(pAspNetCoreElement,
CS_ASPNETCORE_PROCESS_ARGUMENTS,
&m_struArguments);
if (FAILED(hr))
{
goto Finished;
}
hr = GetElementDWORDProperty(pAspNetCoreElement,
CS_ASPNETCORE_RAPID_FAILS_PER_MINUTE,
&m_dwRapidFailsPerMinute);
if (FAILED(hr))
{
goto Finished;
}
//
// rapidFailsPerMinute cannot be greater than 100.
//
if (m_dwRapidFailsPerMinute > MAX_RAPID_FAILS_PER_MINUTE)
{
m_dwRapidFailsPerMinute = MAX_RAPID_FAILS_PER_MINUTE;
}
hr = GetElementDWORDProperty(pAspNetCoreElement,
CS_ASPNETCORE_PROCESSES_PER_APPLICATION,
&m_dwProcessesPerApplication);
if (FAILED(hr))
{
goto Finished;
}
hr = GetElementDWORDProperty(
pAspNetCoreElement,
CS_ASPNETCORE_PROCESS_STARTUP_TIME_LIMIT,
&m_dwStartupTimeLimitInMS
);
if (FAILED(hr))
{
goto Finished;
}
m_dwStartupTimeLimitInMS *= MILLISECONDS_IN_ONE_SECOND;
hr = GetElementDWORDProperty(
pAspNetCoreElement,
CS_ASPNETCORE_PROCESS_SHUTDOWN_TIME_LIMIT,
&m_dwShutdownTimeLimitInMS
);
if (FAILED(hr))
{
goto Finished;
}
m_dwShutdownTimeLimitInMS *= MILLISECONDS_IN_ONE_SECOND;
hr = GetElementBoolProperty(pAspNetCoreElement,
CS_ASPNETCORE_FORWARD_WINDOWS_AUTH_TOKEN,
&m_fForwardWindowsAuthToken);
if (FAILED(hr))
{
goto Finished;
}
hr = GetElementBoolProperty(pAspNetCoreElement,
CS_ASPNETCORE_DISABLE_START_UP_ERROR_PAGE,
&m_fDisableStartUpErrorPage);
if (FAILED(hr))
{
goto Finished;
}
hr = GetElementRawTimeSpanProperty(
pAspNetCoreElement,
CS_ASPNETCORE_WINHTTP_REQUEST_TIMEOUT,
&ullRawTimeSpan
);
if (FAILED(hr))
{
goto Finished;
}
m_dwRequestTimeoutInMS = (DWORD)TIMESPAN_IN_MILLISECONDS(ullRawTimeSpan);
hr = GetElementBoolProperty(pAspNetCoreElement,
CS_ASPNETCORE_STDOUT_LOG_ENABLED,
&m_fStdoutLogEnabled);
if (FAILED(hr))
{
goto Finished;
}
hr = GetElementStringProperty(pAspNetCoreElement,
CS_ASPNETCORE_STDOUT_LOG_FILE,
&m_struStdoutLogFile);
if (FAILED(hr))
{
goto Finished;
}
hr = GetElementChildByName(pAspNetCoreElement,
CS_ASPNETCORE_ENVIRONMENT_VARIABLES,
&pEnvVarList);
if (FAILED(hr))
{
goto Finished;
}
hr = pEnvVarList->get_Collection(&pEnvVarCollection);
if (FAILED(hr))
{
goto Finished;
}
for (hr = FindFirstElement(pEnvVarCollection, &index, &pEnvVar);
SUCCEEDED(hr);
hr = FindNextElement(pEnvVarCollection, &index, &pEnvVar))
{
if (hr == S_FALSE)
{
hr = S_OK;
break;
}
if (FAILED(hr = GetElementStringProperty(pEnvVar,
CS_ASPNETCORE_ENVIRONMENT_VARIABLE_NAME,
&strEnvName)) ||
FAILED(hr = GetElementStringProperty(pEnvVar,
CS_ASPNETCORE_ENVIRONMENT_VARIABLE_VALUE,
&strEnvValue)) ||
FAILED(hr = strEnvName.Append(L"=")) ||
FAILED(hr = STRU::ExpandEnvironmentVariables(strEnvValue.QueryStr(), &strExpandedEnvValue)))
{
goto Finished;
}
pEntry = new ENVIRONMENT_VAR_ENTRY();
if (pEntry == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
if (FAILED(hr = pEntry->Initialize(strEnvName.QueryStr(), strExpandedEnvValue.QueryStr())) ||
FAILED(hr = m_pEnvironmentVariables->InsertRecord(pEntry)))
{
goto Finished;
}
strEnvName.Reset();
strEnvValue.Reset();
strExpandedEnvValue.Reset();
pEnvVar->Release();
pEnvVar = NULL;
pEntry->Dereference();
pEntry = NULL;
}
Finished:
if (pAspNetCoreElement != NULL)
{
pAspNetCoreElement->Release();
pAspNetCoreElement = NULL;
}
if (pEnvVarList != NULL)
{
pEnvVarList->Release();
pEnvVarList = NULL;
}
if (pEnvVar != NULL)
{
pEnvVar->Release();
pEnvVar = NULL;
}
if (pEnvVarCollection != NULL)
{
pEnvVarCollection->Release();
pEnvVarCollection = NULL;
}
if (pEntry != NULL)
{
pEntry->Dereference();
pEntry = NULL;
}
return hr;
}

View File

@ -0,0 +1,481 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "precomp.hxx"
FILE_WATCHER::FILE_WATCHER() :
m_hCompletionPort(NULL),
m_hChangeNotificationThread(NULL)
{
}
FILE_WATCHER::~FILE_WATCHER()
{
if (m_hChangeNotificationThread != NULL)
{
CloseHandle(m_hChangeNotificationThread);
m_hChangeNotificationThread = NULL;
}
}
HRESULT
FILE_WATCHER::Create(
VOID
)
{
HRESULT hr = S_OK;
m_hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
NULL,
0,
0);
if (m_hCompletionPort == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
m_hChangeNotificationThread = CreateThread(NULL,
0,
ChangeNotificationThread,
this,
0,
NULL);
if (m_hChangeNotificationThread == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
CloseHandle(m_hCompletionPort);
m_hCompletionPort = NULL;
goto Finished;
}
Finished:
return hr;
}
DWORD
WINAPI
FILE_WATCHER::ChangeNotificationThread(
LPVOID pvArg
)
/*++
Routine Description:
IO completion thread
Arguments:
None
Return Value:
Win32 error
--*/
{
FILE_WATCHER * pFileMonitor;
BOOL fSuccess = FALSE;
DWORD cbCompletion = 0;
OVERLAPPED * pOverlapped = NULL;
DWORD dwErrorStatus;
ULONG_PTR completionKey;
pFileMonitor = (FILE_WATCHER*)pvArg;
DBG_ASSERT(pFileMonitor != NULL);
while (TRUE)
{
fSuccess = GetQueuedCompletionStatus(
pFileMonitor->m_hCompletionPort,
&cbCompletion,
&completionKey,
&pOverlapped,
INFINITE);
DBG_ASSERT(fSuccess);
DebugPrint(1, "FILE_WATCHER::ChangeNotificationThread");
dwErrorStatus = fSuccess ? ERROR_SUCCESS : GetLastError();
if (completionKey == FILE_WATCHER_SHUTDOWN_KEY)
{
continue;
}
DBG_ASSERT(pOverlapped != NULL);
if (pOverlapped != NULL)
{
FileWatcherCompletionRoutine(
dwErrorStatus,
cbCompletion,
pOverlapped);
}
pOverlapped = NULL;
cbCompletion = 0;
}
}
VOID
WINAPI
FILE_WATCHER::FileWatcherCompletionRoutine(
DWORD dwCompletionStatus,
DWORD cbCompletion,
OVERLAPPED * pOverlapped
)
/*++
Routine Description:
Called when ReadDirectoryChangesW() completes
Arguments:
dwCompletionStatus - Error of completion
cbCompletion - Bytes of completion
pOverlapped - State of completion
Return Value:
None
--*/
{
FILE_WATCHER_ENTRY * pMonitorEntry;
pMonitorEntry = CONTAINING_RECORD(pOverlapped, FILE_WATCHER_ENTRY, _overlapped);
pMonitorEntry->DereferenceFileWatcherEntry();
DBG_ASSERT(pMonitorEntry != NULL);
pMonitorEntry->HandleChangeCompletion(dwCompletionStatus, cbCompletion);
if (pMonitorEntry->QueryIsValid())
{
//
// Continue monitoring
//
pMonitorEntry->Monitor();
}
else
{
//
// Marked by application distructor
// Deference the entry to delete it
//
pMonitorEntry->DereferenceFileWatcherEntry();
}
}
FILE_WATCHER_ENTRY::FILE_WATCHER_ENTRY(FILE_WATCHER * pFileMonitor) :
_pFileMonitor(pFileMonitor),
_hDirectory(INVALID_HANDLE_VALUE),
_hImpersonationToken(NULL),
_pApplication(NULL),
_lStopMonitorCalled(0),
_cRefs(1),
_fIsValid(TRUE)
{
_dwSignature = FILE_WATCHER_ENTRY_SIGNATURE;
InitializeSRWLock(&_srwLock);
}
FILE_WATCHER_ENTRY::~FILE_WATCHER_ENTRY()
{
_dwSignature = FILE_WATCHER_ENTRY_SIGNATURE_FREE;
if (_hDirectory != INVALID_HANDLE_VALUE)
{
CloseHandle(_hDirectory);
_hDirectory = INVALID_HANDLE_VALUE;
}
if (_hImpersonationToken != NULL)
{
CloseHandle(_hImpersonationToken);
_hImpersonationToken = NULL;
}
}
#pragma warning(disable:4100)
HRESULT
FILE_WATCHER_ENTRY::HandleChangeCompletion(
_In_ DWORD dwCompletionStatus,
_In_ DWORD cbCompletion
)
/*++
Routine Description:
Handle change notification (see if any of associated config files
need to be flushed)
Arguments:
dwCompletionStatus - Completion status
cbCompletion - Bytes of completion
Return Value:
HRESULT
--*/
{
HRESULT hr = S_OK;
FILE_NOTIFY_INFORMATION * pNotificationInfo;
BOOL fFileChanged = FALSE;
STRU strEventMsg;
AcquireSRWLockExclusive(&_srwLock);
if (!_fIsValid)
{
goto Finished;
}
// When directory handle is closed then HandleChangeCompletion
// happens with cbCompletion = 0 and dwCompletionStatus = 0
// From documentation it is not clear if that combination
// of return values is specific to closing handles or whether
// it could also mean an error condition. Hence we will maintain
// explicit flag that will help us determine if entry
// is being shutdown (StopMonitor() called)
//
if (_lStopMonitorCalled)
{
goto Finished;
}
//
// There could be a FCN overflow
// Let assume the file got changed instead of checking files
// Othersie we have to cache the file info
//
if (cbCompletion == 0)
{
fFileChanged = TRUE;
}
else
{
pNotificationInfo = (FILE_NOTIFY_INFORMATION*)_buffDirectoryChanges.QueryPtr();
DBG_ASSERT(pNotificationInfo != NULL);
while (pNotificationInfo != NULL)
{
//
// check whether the monitored file got changed
//
if (_wcsnicmp(pNotificationInfo->FileName,
_strFileName.QueryStr(),
pNotificationInfo->FileNameLength/sizeof(WCHAR)) == 0)
{
fFileChanged = TRUE;
break;
}
//
// Advance to next notification
//
if (pNotificationInfo->NextEntryOffset == 0)
{
pNotificationInfo = NULL;
}
else
{
pNotificationInfo = (FILE_NOTIFY_INFORMATION*)
((PBYTE)pNotificationInfo +
pNotificationInfo->NextEntryOffset);
}
}
}
if (fFileChanged)
{
LPCWSTR apsz[1];
if (SUCCEEDED(strEventMsg.SafeSnwprintf(
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE_MSG,
_strFileName.QueryStr())))
{
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_INFORMATION_TYPE,
0,
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE,
NULL,
1,
0,
apsz,
NULL);
}
}
//
// so far we only monitoring app_offline
//
_pApplication->UpdateAppOfflineFileHandle();
}
Finished:
ReleaseSRWLockExclusive(&_srwLock);
return hr;
}
#pragma warning( error : 4100 )
HRESULT
FILE_WATCHER_ENTRY::Monitor(VOID)
{
HRESULT hr = S_OK;
DWORD cbRead;
AcquireSRWLockExclusive(&_srwLock);
ReferenceFileWatcherEntry();
ZeroMemory(&_overlapped, sizeof(_overlapped));
if(!ReadDirectoryChangesW(_hDirectory,
_buffDirectoryChanges.QueryPtr(),
_buffDirectoryChanges.QuerySize(),
FALSE, // Watching sub dirs. Set to False now as only monitoring app_offline
FILE_NOTIFY_VALID_MASK & ~FILE_NOTIFY_CHANGE_LAST_ACCESS,
&cbRead,
&_overlapped,
NULL))
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
ReleaseSRWLockExclusive(&_srwLock);
return hr;
}
VOID
FILE_WATCHER_ENTRY::StopMonitor(VOID)
{
//
// Flag that monitoring is being stopped so that
// we know that HandleChangeCompletion() call
// can be ignored
//
InterlockedExchange(&_lStopMonitorCalled, 1);
AcquireSRWLockExclusive(&_srwLock);
if (_hDirectory != INVALID_HANDLE_VALUE)
{
CloseHandle(_hDirectory);
_hDirectory = INVALID_HANDLE_VALUE;
}
ReleaseSRWLockExclusive(&_srwLock);
}
HRESULT
FILE_WATCHER_ENTRY::Create(
_In_ PCWSTR pszDirectoryToMonitor,
_In_ PCWSTR pszFileNameToMonitor,
_In_ APPLICATION* pApplication,
_In_ HANDLE hImpersonationToken
)
{
HRESULT hr = S_OK;
BOOL fRet = FALSE;
if (pszDirectoryToMonitor == NULL ||
pszFileNameToMonitor == NULL ||
pApplication == NULL)
{
DBG_ASSERT(FALSE);
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
goto Finished;
}
//
//remember the application
//
_pApplication = pApplication;
if (FAILED(hr = _strFileName.Copy(pszFileNameToMonitor)))
{
goto Finished;
}
if (FAILED(hr = _strDirectoryName.Copy(pszDirectoryToMonitor)))
{
goto Finished;
}
//
// Resize change buffer to something "reasonable"
//
if (!_buffDirectoryChanges.Resize(FILE_WATCHER_ENTRY_BUFFER_SIZE))
{
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
goto Finished;
}
if (hImpersonationToken != NULL)
{
fRet = DuplicateHandle(GetCurrentProcess(),
hImpersonationToken,
GetCurrentProcess(),
&_hImpersonationToken,
0,
FALSE,
DUPLICATE_SAME_ACCESS);
if (!fRet)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
}
else
{
if (_hImpersonationToken != NULL)
{
CloseHandle(_hImpersonationToken);
_hImpersonationToken = NULL;
}
}
_hDirectory = CreateFileW(
_strDirectoryName.QueryStr(),
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL);
if (_hDirectory == INVALID_HANDLE_VALUE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
if (CreateIoCompletionPort(
_hDirectory,
_pFileMonitor->QueryCompletionPort(),
NULL,
0) == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
//
// Start monitoring
//
hr = Monitor();
Finished:
return hr;
}

View File

@ -0,0 +1,52 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "precomp.hxx"
FORWARDER_CONNECTION::FORWARDER_CONNECTION(
VOID
) : m_cRefs (1),
m_hConnection (NULL)
{
}
HRESULT
FORWARDER_CONNECTION::Initialize(
DWORD dwPort
)
{
HRESULT hr = S_OK;
hr = m_ConnectionKey.Initialize( dwPort );
if ( FAILED( hr ) )
{
goto Finished;
}
m_hConnection = WinHttpConnect(FORWARDING_HANDLER::sm_hSession,
L"127.0.0.1",
(USHORT) dwPort,
0);
if (m_hConnection == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
//
// Since WinHttp will not emit WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING
// when closing WebSocket handle on Win8. Register callback at Connect level as a workaround
//
if (WinHttpSetStatusCallback(m_hConnection,
FORWARDING_HANDLER::OnWinHttpCompletion,
WINHTTP_CALLBACK_FLAG_HANDLES,
NULL) == WINHTTP_INVALID_STATUS_CALLBACK)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
Finished:
return hr;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,278 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "precomp.hxx"
#include <IPHlpApi.h>
#ifdef DEBUG
DECLARE_DEBUG_PRINTS_OBJECT();
DECLARE_DEBUG_VARIABLE();
DECLARE_PLATFORM_TYPE();
#endif // DEBUG
HTTP_MODULE_ID g_pModuleId = NULL;
IHttpServer * g_pHttpServer = NULL;
BOOL g_fAsyncDisconnectAvailable = FALSE;
BOOL g_fWinHttpNonBlockingCallbackAvailable = FALSE;
PCWSTR g_pszModuleName = NULL;
HINSTANCE g_hModule;
HINSTANCE g_hWinHttpModule;
BOOL g_fWebSocketSupported = FALSE;
DWORD g_dwTlsIndex = TLS_OUT_OF_INDEXES;
BOOL g_fEnableReferenceCountTracing = FALSE;
DWORD g_dwAspNetCoreDebugFlags = 0;
BOOL g_fNsiApiNotSupported = FALSE;
DWORD g_dwActiveServerProcesses = 0;
DWORD g_OptionalWinHttpFlags = 0; //specify additional WinHTTP options when using WinHttpOpenRequest API.
DWORD g_dwDebugFlags = 0;
PCSTR g_szDebugLabel = "ASPNET_CORE_MODULE";
#ifdef DEBUG
STRA g_strLogs[ASPNETCORE_DEBUG_STRU_ARRAY_SIZE];
DWORD g_dwLogCounter = 0;
#endif // DEBUG
BOOL WINAPI DllMain(
HMODULE hModule,
DWORD dwReason,
LPVOID
)
{
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
g_hModule = hModule;
DisableThreadLibraryCalls(hModule);
break;
default:
break;
}
return TRUE;
}
VOID
LoadGlobalConfiguration(
VOID
)
{
HKEY hKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\IIS Extensions\\IIS AspNetCore Module\\Parameters",
0,
KEY_READ,
&hKey) == NO_ERROR)
{
DWORD dwType;
DWORD dwData;
DWORD cbData;
cbData = sizeof(dwData);
if ((RegQueryValueEx(hKey,
L"OptionalWinHttpFlags",
NULL,
&dwType,
(LPBYTE)&dwData,
&cbData) == NO_ERROR) &&
(dwType == REG_DWORD))
{
g_OptionalWinHttpFlags = dwData;
}
cbData = sizeof(dwData);
if ((RegQueryValueEx(hKey,
L"EnableReferenceCountTracing",
NULL,
&dwType,
(LPBYTE)&dwData,
&cbData) == NO_ERROR) &&
(dwType == REG_DWORD) && (dwData == 1 || dwData == 0))
{
g_fEnableReferenceCountTracing = !!dwData;
}
cbData = sizeof(dwData);
if ((RegQueryValueEx(hKey,
L"DebugFlags",
NULL,
&dwType,
(LPBYTE)&dwData,
&cbData) == NO_ERROR) &&
(dwType == REG_DWORD))
{
g_dwAspNetCoreDebugFlags = dwData;
}
RegCloseKey(hKey);
}
DWORD dwSize = 0;
DWORD dwResult = GetExtendedTcpTable(NULL,
&dwSize,
FALSE,
AF_INET,
TCP_TABLE_OWNER_PID_LISTENER,
0);
if (dwResult != NO_ERROR && dwResult != ERROR_INSUFFICIENT_BUFFER)
{
g_fNsiApiNotSupported = TRUE;
}
}
HRESULT
__stdcall
RegisterModule(
DWORD dwServerVersion,
IHttpModuleRegistrationInfo * pModuleInfo,
IHttpServer * pHttpServer
)
/*++
Routine description:
Function called by IIS immediately after loading the module, used to let
IIS know what notifications the module is interested in
Arguments:
dwServerVersion - IIS version the module is being loaded on
pModuleInfo - info regarding this module
pHttpServer - callback functions which can be used by the module at
any point
Return value:
HRESULT
--*/
{
HRESULT hr = S_OK;
CProxyModuleFactory * pFactory = NULL;
#ifdef DEBUG
CREATE_DEBUG_PRINT_OBJECT("Asp.Net Core Module");
g_dwDebugFlags = DEBUG_FLAGS_ANY;
#endif // DEBUG
LoadGlobalConfiguration();
//
// 7.0 is 0,7
//
if (dwServerVersion > MAKELONG(0, 7))
{
g_fAsyncDisconnectAvailable = TRUE;
}
//
// 8.0 is 0,8
//
if (dwServerVersion >= MAKELONG(0, 8))
{
// IISOOB:36641 Enable back WINHTTP_OPTION_ASSURED_NON_BLOCKING_CALLBACKS for Win8.
// g_fWinHttpNonBlockingCallbackAvailable = TRUE;
g_fWebSocketSupported = TRUE;
}
hr = WINHTTP_HELPER::StaticInitialize();
if (FAILED(hr))
{
if (hr == HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND))
{
g_fWebSocketSupported = FALSE;
}
else
{
goto Finished;
}
}
g_pModuleId = pModuleInfo->GetId();
g_pszModuleName = pModuleInfo->GetName();
g_pHttpServer = pHttpServer;
#ifdef DEBUG
for (int i = 0; i < ASPNETCORE_DEBUG_STRU_ARRAY_SIZE; i++)
{
g_strLogs[i].Resize(ASPNETCORE_DEBUG_STRU_BUFFER_SIZE + 1);
}
#endif // DEBUG
//
// WinHTTP does not create enough threads, ask it to create more.
// Starting in Windows 7, this setting is ignored because WinHTTP
// uses a thread pool.
//
SYSTEM_INFO si;
GetSystemInfo(&si);
DWORD dwThreadCount = (si.dwNumberOfProcessors * 3 + 1) / 2;
WinHttpSetOption(NULL,
WINHTTP_OPTION_WORKER_THREAD_COUNT,
&dwThreadCount,
sizeof(dwThreadCount));
//
// Create the factory before any static initialization.
// The CProxyModuleFactory::Terminate method will clean any
// static object initialized.
//
pFactory = new CProxyModuleFactory;
if (pFactory == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
hr = pModuleInfo->SetRequestNotifications(
pFactory,
RQ_EXECUTE_REQUEST_HANDLER,
0);
if (FAILED(hr))
{
goto Finished;
}
pFactory = NULL;
g_pResponseHeaderHash = new RESPONSE_HEADER_HASH;
if (g_pResponseHeaderHash == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
hr = g_pResponseHeaderHash->Initialize();
if (FAILED(hr))
{
goto Finished;
}
hr = ALLOC_CACHE_HANDLER::StaticInitialize();
if (FAILED(hr))
{
goto Finished;
}
hr = FORWARDING_HANDLER::StaticInitialize(g_fEnableReferenceCountTracing);
if (FAILED(hr))
{
goto Finished;
}
hr = WEBSOCKET_HANDLER::StaticInitialize(g_fEnableReferenceCountTracing);
if (FAILED(hr))
{
goto Finished;
}
Finished:
if (pFactory != NULL)
{
pFactory->Terminate();
pFactory = NULL;
}
return hr;
}

View File

@ -0,0 +1,442 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "precomp.hxx"
// static
HRESULT
PATH::SplitUrl(
PCWSTR pszDestinationUrl,
BOOL *pfSecure,
STRU *pstrDestination,
STRU *pstrUrl
)
/*++
Routine Description:
Split the URL specified for forwarding into its specific components
The format of the URL looks like
http[s]://destination[:port]/path
when port is omitted, the default port for that specific protocol is used
when host is omitted, it gets the same value as the destination
Arguments:
pszDestinationUrl - the url to be split up
pfSecure - SSL to be used in forwarding?
pstrDestination - destination
pDestinationPort - port
pstrUrl - URL
Return Value:
HRESULT
--*/
{
HRESULT hr;
//
// First determine if the target is secure
//
if (_wcsnicmp(pszDestinationUrl, L"http://", 7) == 0)
{
*pfSecure = FALSE;
pszDestinationUrl += 7;
}
else if (_wcsnicmp(pszDestinationUrl, L"https://", 8) == 0)
{
*pfSecure = TRUE;
pszDestinationUrl += 8;
}
else
{
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
}
if (*pszDestinationUrl == L'\0')
{
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
}
//
// Find the 3rd slash corresponding to the url
//
LPCWSTR pszSlash = wcschr(pszDestinationUrl, L'/');
if (pszSlash == NULL)
{
if (FAILED(hr = pstrUrl->Copy(L"/", 1)) ||
FAILED(hr = pstrDestination->Copy(pszDestinationUrl)))
{
return hr;
}
}
else
{
if (FAILED(hr = pstrUrl->Copy(pszSlash)) ||
FAILED(hr = pstrDestination->Copy(pszDestinationUrl,
(DWORD)(pszSlash - pszDestinationUrl))))
{
return hr;
}
}
return S_OK;
}
// Change a hexadecimal digit to its numerical equivalent
#define TOHEX( ch ) \
((ch) > L'9' ? \
(ch) >= L'a' ? \
(ch) - L'a' + 10 : \
(ch) - L'A' + 10 \
: (ch) - L'0')
// static
HRESULT
PATH::UnEscapeUrl(
PCWSTR pszUrl,
DWORD cchUrl,
bool fCopyQuery,
STRA * pstrResult
)
{
HRESULT hr;
CHAR pch[2];
pch[1] = '\0';
DWORD cchStart = 0;
DWORD index = 0;
while (index < cchUrl &&
(fCopyQuery || pszUrl[index] != L'?'))
{
switch (pszUrl[index])
{
case L'%':
if (iswxdigit(pszUrl[index+1]) && iswxdigit(pszUrl[index+2]))
{
if (index > cchStart &&
FAILED(hr = pstrResult->AppendW(pszUrl + cchStart,
index - cchStart)))
{
return hr;
}
cchStart = index+3;
pch[0] = static_cast<CHAR>(TOHEX(pszUrl[index+1]) * 16 +
TOHEX(pszUrl[index+2]));
if (FAILED(hr = pstrResult->Append(pch, 1)))
{
return hr;
}
index += 3;
break;
}
__fallthrough;
default:
index++;
}
}
if (index > cchStart)
{
return pstrResult->AppendW(pszUrl + cchStart,
index - cchStart);
}
return S_OK;
}
// static
HRESULT
PATH::UnEscapeUrl(
PCWSTR pszUrl,
DWORD cchUrl,
STRU * pstrResult
)
{
HRESULT hr;
WCHAR pch[2];
pch[1] = L'\0';
DWORD cchStart = 0;
DWORD index = 0;
bool fInQuery = FALSE;
while (index < cchUrl)
{
switch (pszUrl[index])
{
case L'%':
if (iswxdigit(pszUrl[index+1]) && iswxdigit(pszUrl[index+2]))
{
if (index > cchStart &&
FAILED(hr = pstrResult->Append(pszUrl + cchStart,
index - cchStart)))
{
return hr;
}
cchStart = index+3;
pch[0] = static_cast<WCHAR>(TOHEX(pszUrl[index+1]) * 16 +
TOHEX(pszUrl[index+2]));
if (FAILED(hr = pstrResult->Append(pch, 1)))
{
return hr;
}
index += 3;
if (pch[0] == L'?')
{
fInQuery = TRUE;
}
break;
}
index++;
break;
case L'/':
if (fInQuery)
{
if (index > cchStart &&
FAILED(hr = pstrResult->Append(pszUrl + cchStart,
index - cchStart)))
{
return hr;
}
cchStart = index+1;
if (FAILED(hr = pstrResult->Append(L"\\", 1)))
{
return hr;
}
index += 1;
break;
}
__fallthrough;
default:
index++;
}
}
if (index > cchStart)
{
return pstrResult->Append(pszUrl + cchStart,
index - cchStart);
}
return S_OK;
}
HRESULT
PATH::EscapeAbsPath(
IHttpRequest * pRequest,
STRU * strEscapedUrl
)
{
HRESULT hr = S_OK;
STRU strAbsPath;
LPCWSTR pszAbsPath = NULL;
LPCWSTR pszFindStr = NULL;
hr = strAbsPath.Copy( pRequest->GetRawHttpRequest()->CookedUrl.pAbsPath,
pRequest->GetRawHttpRequest()->CookedUrl.AbsPathLength / sizeof(WCHAR) );
if(FAILED(hr))
{
goto Finished;
}
pszAbsPath = strAbsPath.QueryStr();
pszFindStr = wcschr(pszAbsPath, L'?');
while(pszFindStr != NULL)
{
strEscapedUrl->Append( pszAbsPath, pszFindStr - pszAbsPath);
strEscapedUrl->Append(L"%3F");
pszAbsPath = pszFindStr + 1;
pszFindStr = wcschr(pszAbsPath, L'?');
}
strEscapedUrl->Append(pszAbsPath);
strEscapedUrl->Append(pRequest->GetRawHttpRequest()->CookedUrl.pQueryString,
pRequest->GetRawHttpRequest()->CookedUrl.QueryStringLength / sizeof(WCHAR));
Finished:
return hr;
}
// static
bool
PATH::IsValidAttributeNameChar(
WCHAR ch
)
{
//
// Values based on ASP.NET rendering for cookie names. RFC 2965 is not clear
// what the non-special characters are.
//
return ch == L'\t' || (ch > 31 && ch < 127);
}
// static
bool
PATH::FindInMultiString(
PCWSTR pszMultiString,
PCWSTR pszStringToFind
)
{
while (*pszMultiString != L'\0')
{
if (wcscmp(pszMultiString, pszStringToFind) == 0)
{
return TRUE;
}
pszMultiString += wcslen(pszMultiString) + 1;
}
return FALSE;
}
// static
bool
PATH::IsValidQueryStringName(
PCWSTR pszName
)
{
while (*pszName != L'\0')
{
WCHAR c = *pszName;
if (c != L'-' && c != L'_' && c != L'+' &&
c != L'.' && c != L'*' && c != L'$' && c != L'%' && c != L',' &&
!iswalnum(c))
{
return FALSE;
}
pszName++;
}
return TRUE;
}
// static
bool
PATH::IsValidHeaderName(
PCWSTR pszName
)
{
while (*pszName != L'\0')
{
WCHAR c = *pszName;
if (c != L'-' && c != L'_' && c != L'+' &&
c != L'.' && c != L'*' && c != L'$' && c != L'%'
&& !iswalnum(c))
{
return FALSE;
}
pszName++;
}
return TRUE;
}
HRESULT
PATH::IsPathUnc(
__in LPCWSTR pszPath,
__out BOOL * pfIsUnc
)
{
HRESULT hr = S_OK;
STRU strTempPath;
if ( pszPath == NULL || pfIsUnc == NULL )
{
hr = E_INVALIDARG;
goto Finished;
}
hr = MakePathCanonicalizationProof( (LPWSTR) pszPath, &strTempPath );
if ( FAILED(hr) )
{
goto Finished;
}
//
// MakePathCanonicalizationProof will map \\?\UNC, \\.\UNC and \\ to \\?\UNC
//
(*pfIsUnc) = ( _wcsnicmp( strTempPath.QueryStr(), L"\\\\?\\UNC\\", 8 /* sizeof \\?\UNC\ */) == 0 );
Finished:
return hr;
}
HRESULT
PATH::ConvertPathToFullPath(
_In_ LPCWSTR pszPath,
_In_ LPCWSTR pszRootPath,
_Out_ STRU* pStruFullPath
)
{
HRESULT hr = S_OK;
STRU strFileFullPath;
LPWSTR pszFullPath = NULL;
// if relative path, prefix with root path and then convert to absolute path.
if( pszPath[0] == L'.' )
{
hr = strFileFullPath.Copy(pszRootPath);
if(FAILED(hr))
{
goto Finished;
}
if(!strFileFullPath.EndsWith(L"\\"))
{
hr = strFileFullPath.Append(L"\\");
if(FAILED(hr))
{
goto Finished;
}
}
}
hr = strFileFullPath.Append( pszPath );
if(FAILED(hr))
{
goto Finished;
}
pszFullPath = new WCHAR[ strFileFullPath.QueryCCH() + 1];
if( pszFullPath == NULL )
{
hr = E_OUTOFMEMORY;
goto Finished;
}
if(_wfullpath( pszFullPath,
strFileFullPath.QueryStr(),
strFileFullPath.QueryCCH() + 1 ) == NULL )
{
hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
goto Finished;
}
// convert to canonical path
hr = MakePathCanonicalizationProof( pszFullPath, pStruFullPath );
if(FAILED(hr))
{
goto Finished;
}
Finished:
if( pszFullPath != NULL )
{
delete[] pszFullPath;
pszFullPath = NULL;
}
return hr;
}

View File

@ -0,0 +1,147 @@
#pragma once
/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
precomp.hxx
Abstract:
Precompiled header.
--*/
//
// System related headers
//
#define _WIN32_WINNT 0x0600
#define _WINSOCKAPI_
#include <windows.h>
#include <atlbase.h>
#include <pdh.h>
#include <Shlobj.h>
#include <ntassert.h>
#include <httpserv.h>
#include <iiswebsocket.h>
#include <httptrace.h>
#include <winhttp.h>
//
// Option available starting Windows 8.
// 111 is the value in SDK on May 15, 2012.
//
#ifndef WINHTTP_OPTION_ASSURED_NON_BLOCKING_CALLBACKS
#define WINHTTP_OPTION_ASSURED_NON_BLOCKING_CALLBACKS 111
#endif
#define ASPNETCORE_EVENT_PROVIDER L"IIS AspNetCore Module"
#define ASPNETCORE_IISEXPRESS_EVENT_PROVIDER L"IIS Express AspNetCore Module"
#define TIMESPAN_IN_MILLISECONDS(x) ((x)/((LONGLONG)(10000)))
#define TIMESPAN_IN_SECONDS(x) ((TIMESPAN_IN_MILLISECONDS(x))/((LONGLONG)(1000)))
#define TIMESPAN_IN_MINUTES(x) ((TIMESPAN_IN_SECONDS(x))/((LONGLONG)(60)))
#ifdef max
#undef max
template<typename T> inline T max(T a, T b)
{
return a > b ? a : b;
}
#endif
#ifdef min
#undef min
template<typename T> inline T min(T a, T b)
{
return a < b ? a : b;
}
#endif
inline bool IsSpace(char ch)
{
switch(ch)
{
case 32: // ' '
case 9: // '\t'
case 10: // '\n'
case 13: // '\r'
case 11: // '\v'
case 12: // '\f'
return true;
default:
return false;
}
}
#include <hashfn.h>
#include <hashtable.h>
#include <stringa.h>
#include <stringu.h>
#include <treehash.h>
#include <dbgutil.h>
#include "ahutil.h"
#include "multisz.hxx"
#include "multisza.hxx"
#include "sttimer.h"
#include <listentry.h>
#include <base64.hxx>
#include <datetime.h>
#include <reftrace.h>
#include <acache.h>
#include <time.h>
#include "filewatcher.h"
#include "environmentvariablehash.h"
#include "aspnetcore_msg.h"
#include "aspnetcoreconfig.h"
#include "serverprocess.h"
#include "processmanager.h"
#include "application.h"
#include "applicationmanager.h"
#include "resource.h"
#include "path.h"
#include "debugutil.h"
#include "protocolconfig.h"
#include "responseheaderhash.h"
#include "forwarderconnection.h"
#include "winhttphelper.h"
#include "websockethandler.h"
#include "forwardinghandler.h"
#include "proxymodule.h"
FORCEINLINE
DWORD
WIN32_FROM_HRESULT(
HRESULT hr
)
{
if ((FAILED(hr)) &&
(HRESULT_FACILITY(hr) == FACILITY_WIN32))
{
return HRESULT_CODE(hr);
}
return hr;
}
FORCEINLINE
HRESULT
HRESULT_FROM_GETLASTERROR()
{
return ( GetLastError() != NO_ERROR )
? HRESULT_FROM_WIN32( GetLastError() )
: E_FAIL;
}
extern BOOL g_fAsyncDisconnectAvailable;
extern PVOID g_pModuleId;
extern BOOL g_fWebSocketSupported;
extern BOOL g_fEnableReferenceCountTracing;
extern DWORD g_dwActiveServerProcesses;

View File

@ -0,0 +1,294 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "precomp.hxx"
volatile BOOL PROCESS_MANAGER::sm_fWSAStartupDone = FALSE;
HRESULT
PROCESS_MANAGER::Initialize(
VOID
)
{
HRESULT hr = S_OK;
WSADATA wsaData;
int result;
BOOL fLocked = FALSE;
if( !sm_fWSAStartupDone )
{
AcquireSRWLockExclusive( &m_srwLock );
fLocked = TRUE;
if( !sm_fWSAStartupDone )
{
if( (result = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0 )
{
hr = HRESULT_FROM_WIN32( result );
goto Finished;
}
sm_fWSAStartupDone = TRUE;
}
ReleaseSRWLockExclusive( &m_srwLock );
fLocked = FALSE;
}
m_dwRapidFailTickStart = GetTickCount();
if( m_hNULHandle == NULL )
{
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
m_hNULHandle = CreateFileW( L"NUL",
FILE_WRITE_DATA,
FILE_SHARE_READ,
&saAttr,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL );
if( m_hNULHandle == INVALID_HANDLE_VALUE )
{
hr = HRESULT_FROM_GETLASTERROR();
goto Finished;
}
}
Finished:
if(fLocked)
{
ReleaseSRWLockExclusive( &m_srwLock );
}
return hr;
}
PROCESS_MANAGER::~PROCESS_MANAGER()
{
AcquireSRWLockExclusive(&m_srwLock);
if( m_ppServerProcessList != NULL )
{
for( DWORD i = 0; i < m_dwProcessesPerApplication; ++i )
{
if( m_ppServerProcessList[i] != NULL )
{
m_ppServerProcessList[i]->DereferenceServerProcess();
m_ppServerProcessList[i] = NULL;
}
}
delete[] m_ppServerProcessList;
m_ppServerProcessList = NULL;
}
if( m_hNULHandle != NULL )
{
CloseHandle( m_hNULHandle );
m_hNULHandle = NULL;
}
if( sm_fWSAStartupDone )
{
WSACleanup();
sm_fWSAStartupDone = FALSE;
}
ReleaseSRWLockExclusive(&m_srwLock);
}
HRESULT
PROCESS_MANAGER::GetProcess(
_In_ IHttpContext *context,
_In_ ASPNETCORE_CONFIG *pConfig,
_Out_ SERVER_PROCESS **ppServerProcess
)
{
HRESULT hr = S_OK;
BOOL fSharedLock = FALSE;
BOOL fExclusiveLock = FALSE;
PCWSTR apsz[1];
STACK_STRU( strEventMsg, 256 );
DWORD dwProcessIndex = 0;
SERVER_PROCESS **ppSelectedServerProcess = NULL;
if (!m_fServerProcessListReady)
{
AcquireSRWLockExclusive( &m_srwLock );
fExclusiveLock = TRUE;
if (!m_fServerProcessListReady)
{
m_dwProcessesPerApplication = pConfig->QueryProcessesPerApplication();
m_ppServerProcessList = new SERVER_PROCESS*[m_dwProcessesPerApplication];
if(m_ppServerProcessList == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
for(DWORD i=0;i<m_dwProcessesPerApplication;++i)
{
m_ppServerProcessList[i] = NULL;
}
}
m_fServerProcessListReady = TRUE;
ReleaseSRWLockExclusive( &m_srwLock );
fExclusiveLock = FALSE;
}
AcquireSRWLockShared( &m_srwLock );
fSharedLock = TRUE;
//
// round robin through to the next available process.
//
dwProcessIndex = (DWORD) InterlockedIncrement64( (LONGLONG*) &m_dwRouteToProcessIndex );
dwProcessIndex = dwProcessIndex % m_dwProcessesPerApplication;
ppSelectedServerProcess = &m_ppServerProcessList[dwProcessIndex];
if( *ppSelectedServerProcess != NULL &&
m_ppServerProcessList[dwProcessIndex]->IsReady() )
{
m_ppServerProcessList[dwProcessIndex]->ReferenceServerProcess();
*ppServerProcess = m_ppServerProcessList[dwProcessIndex];
goto Finished;
}
ReleaseSRWLockShared( &m_srwLock );
fSharedLock = FALSE;
// should make the lock per process so that we can start processes simultaneously ?
if(m_ppServerProcessList[dwProcessIndex] == NULL || !m_ppServerProcessList[dwProcessIndex]->IsReady())
{
AcquireSRWLockExclusive( &m_srwLock );
fExclusiveLock = TRUE;
if( m_ppServerProcessList[dwProcessIndex] != NULL )
{
if( !m_ppServerProcessList[dwProcessIndex]->IsReady() )
{
//
// terminate existing process that is not ready
// before creating new one.
//
ShutdownProcessNoLock( m_ppServerProcessList[dwProcessIndex] );
}
else
{
// server is already up and ready to serve requests.
m_ppServerProcessList[dwProcessIndex]->ReferenceServerProcess();
*ppServerProcess = m_ppServerProcessList[dwProcessIndex];
goto Finished;
}
}
if( RapidFailsPerMinuteExceeded(pConfig->QueryRapidFailsPerMinute()) )
{
//
// rapid fails per minute exceeded, do not create new process.
//
if( SUCCEEDED( strEventMsg.SafeSnwprintf(
ASPNETCORE_EVENT_RAPID_FAIL_COUNT_EXCEEDED_MSG,
pConfig->QueryRapidFailsPerMinute() ) ) )
{
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_INFORMATION_TYPE,
0,
ASPNETCORE_EVENT_RAPID_FAIL_COUNT_EXCEEDED,
NULL,
1,
0,
apsz,
NULL);
}
}
hr = HRESULT_FROM_WIN32(ERROR_SERVER_DISABLED);
goto Finished;
}
if( m_ppServerProcessList[dwProcessIndex] == NULL )
{
m_ppServerProcessList[dwProcessIndex] = new SERVER_PROCESS();
if( m_ppServerProcessList[dwProcessIndex] == NULL )
{
hr = E_OUTOFMEMORY;
goto Finished;
}
hr = m_ppServerProcessList[dwProcessIndex]->Initialize(
this,
pConfig->QueryProcessPath(),
pConfig->QueryArguments(),
pConfig->QueryStartupTimeLimitInMS(),
pConfig->QueryShutdownTimeLimitInMS(),
pConfig->QueryWindowsAuthEnabled(),
pConfig->QueryBasicAuthEnabled(),
pConfig->QueryAnonymousAuthEnabled(),
pConfig->QueryEnvironmentVariables(),
pConfig->QueryStdoutLogEnabled(),
pConfig->QueryStdoutLogFile()
);
if( FAILED( hr ) )
{
goto Finished;
}
hr = m_ppServerProcessList[dwProcessIndex]->StartProcess(context);
if( FAILED( hr ) )
{
goto Finished;
}
}
if( !m_ppServerProcessList[dwProcessIndex]->IsReady() )
{
hr = HRESULT_FROM_WIN32( ERROR_CREATE_FAILED );
goto Finished;
}
m_ppServerProcessList[dwProcessIndex]->ReferenceServerProcess();
*ppServerProcess = m_ppServerProcessList[dwProcessIndex];
}
Finished:
if( FAILED(hr) )
{
if(m_ppServerProcessList[dwProcessIndex] != NULL )
{
m_ppServerProcessList[dwProcessIndex]->DereferenceServerProcess();
m_ppServerProcessList[dwProcessIndex] = NULL;
}
}
if( fSharedLock )
{
ReleaseSRWLockShared( &m_srwLock );
fSharedLock = FALSE;
}
if( fExclusiveLock )
{
ReleaseSRWLockExclusive( &m_srwLock );
fExclusiveLock = FALSE;
}
return hr;
}

View File

@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "precomp.hxx"
HRESULT
PROTOCOL_CONFIG::Initialize()
{
HRESULT hr;
STRU strTemp;
m_fKeepAlive = TRUE;
m_msTimeout = 120000;
m_fPreserveHostHeader = TRUE;
m_fReverseRewriteHeaders = FALSE;
if (FAILED(hr = m_strXForwardedForName.CopyW(L"X-Forwarded-For")))
{
goto Finished;
}
if (FAILED(hr = m_strSslHeaderName.CopyW(L"X-Forwarded-Proto")))
{
goto Finished;
}
if (FAILED(hr = m_strClientCertName.CopyW(L"MS-ASPNETCORE-CLIENTCERT")))
{
goto Finished;
}
m_fIncludePortInXForwardedFor = TRUE;
m_dwMinResponseBuffer = 0; // no response buffering
m_dwResponseBufferLimit = 4096*1024;
m_dwMaxResponseHeaderSize = 65536;
Finished:
return hr;
}
VOID
PROTOCOL_CONFIG::OverrideConfig(
ASPNETCORE_CONFIG *pAspNetCoreConfig
)
{
m_msTimeout = pAspNetCoreConfig->QueryRequestTimeoutInMS();
}

View File

@ -0,0 +1,114 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "precomp.hxx"
__override
HRESULT
CProxyModuleFactory::GetHttpModule(
CHttpModule ** ppModule,
IModuleAllocator * pAllocator
)
{
CProxyModule *pModule = new (pAllocator) CProxyModule();
if (pModule == NULL)
{
return E_OUTOFMEMORY;
}
*ppModule = pModule;
return S_OK;
}
__override
VOID
CProxyModuleFactory::Terminate(
VOID
)
/*++
Routine description:
Function called by IIS for global (non-request-specific) notifications
Arguments:
None.
Return value:
None
--*/
{
FORWARDING_HANDLER::StaticTerminate();
WEBSOCKET_HANDLER::StaticTerminate();
if (g_pResponseHeaderHash != NULL)
{
g_pResponseHeaderHash->Clear();
delete g_pResponseHeaderHash;
g_pResponseHeaderHash = NULL;
}
ALLOC_CACHE_HANDLER::StaticTerminate();
delete this;
}
CProxyModule::CProxyModule(
) : m_pHandler(NULL)
{
}
CProxyModule::~CProxyModule()
{
if (m_pHandler != NULL)
{
//
// This will be called when the main notification is cleaned up
// i.e., the request is done with IIS pipeline
//
m_pHandler->DereferenceForwardingHandler();
m_pHandler = NULL;
}
}
__override
REQUEST_NOTIFICATION_STATUS
CProxyModule::OnExecuteRequestHandler(
IHttpContext * pHttpContext,
IHttpEventProvider *
)
{
m_pHandler = new FORWARDING_HANDLER(pHttpContext);
if (m_pHandler == NULL)
{
pHttpContext->GetResponse()->SetStatus(500, "Internal Server Error", 0, E_OUTOFMEMORY);
return RQ_NOTIFICATION_FINISH_REQUEST;
}
return m_pHandler->OnExecuteRequestHandler();
}
__override
REQUEST_NOTIFICATION_STATUS
CProxyModule::OnAsyncCompletion(
IHttpContext *,
DWORD dwNotification,
BOOL fPostNotification,
IHttpEventProvider *,
IHttpCompletionInfo * pCompletionInfo
)
{
UNREFERENCED_PARAMETER(dwNotification);
UNREFERENCED_PARAMETER(fPostNotification);
DBG_ASSERT(dwNotification == RQ_EXECUTE_REQUEST_HANDLER);
DBG_ASSERT(fPostNotification == FALSE);
return m_pHandler->OnAsyncCompletion(
pCompletionInfo->GetCompletionBytes(),
pCompletionInfo->GetCompletionStatus());
}

View File

@ -0,0 +1,100 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "precomp.hxx"
RESPONSE_HEADER_HASH * g_pResponseHeaderHash = NULL;
HEADER_RECORD RESPONSE_HEADER_HASH::sm_rgHeaders[] =
{
{ "Cache-Control", HttpHeaderCacheControl },
{ "Connection", HttpHeaderConnection },
{ "Date", HttpHeaderDate },
{ "Keep-Alive", HttpHeaderKeepAlive },
{ "Pragma", HttpHeaderPragma },
{ "Trailer", HttpHeaderTrailer },
{ "Transfer-Encoding", HttpHeaderTransferEncoding },
{ "Upgrade", HttpHeaderUpgrade },
{ "Via", HttpHeaderVia },
{ "Warning", HttpHeaderWarning },
{ "Allow", HttpHeaderAllow },
{ "Content-Length", HttpHeaderContentLength },
{ "Content-Type", HttpHeaderContentType },
{ "Content-Encoding", HttpHeaderContentEncoding },
{ "Content-Language", HttpHeaderContentLanguage },
{ "Content-Location", HttpHeaderContentLocation },
{ "Content-MD5", HttpHeaderContentMd5 },
{ "Content-Range", HttpHeaderContentRange },
{ "Expires", HttpHeaderExpires },
{ "Last-Modified", HttpHeaderLastModified },
{ "Accept-Ranges", HttpHeaderAcceptRanges },
{ "Age", HttpHeaderAge },
{ "ETag", HttpHeaderEtag },
{ "Location", HttpHeaderLocation },
{ "Proxy-Authenticate", HttpHeaderProxyAuthenticate },
{ "Retry-After", HttpHeaderRetryAfter },
{ "Server", HttpHeaderServer },
// Set it to something which cannot be a header name, in effect
// making Server an unknown header. w:w is used to avoid collision with Keep-Alive.
{ "w:w\r\n", HttpHeaderServer },
// Set it to something which cannot be a header name, in effect
// making Set-Cookie an unknown header
{ "y:y\r\n", HttpHeaderSetCookie },
{ "Vary", HttpHeaderVary },
// Set it to something which cannot be a header name, in effect
// making WWW-Authenticate an unknown header
{ "z:z\r\n", HttpHeaderWwwAuthenticate }
};
HRESULT
RESPONSE_HEADER_HASH::Initialize(
VOID
)
/*++
Routine Description:
Initialize global header hash table
Arguments:
None
Return Value:
HRESULT
--*/
{
HRESULT hr;
//
// 31 response headers.
// Make sure to update the number of buckets it new headers
// are added. Test it to avoid collisions.
//
C_ASSERT(_countof(sm_rgHeaders) == 31);
//
// 79 buckets will have less collisions for the 31 response headers.
// Known collisions are "Age" colliding with "Expire" and "Location"
// colliding with both "Expire" and "Age".
//
hr = HASH_TABLE::Initialize(79);
if (FAILED(hr))
{
return hr;
}
for ( DWORD Index = 0; Index < _countof(sm_rgHeaders); ++Index )
{
if (FAILED(hr = InsertRecord(&sm_rgHeaders[Index])))
{
return hr;
}
}
return S_OK;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,176 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "precomp.hxx"
PFN_WINHTTP_WEBSOCKET_COMPLETE_UPGRADE
WINHTTP_HELPER::sm_pfnWinHttpWebSocketCompleteUpgrade;
PFN_WINHTTP_WEBSOCKET_SEND
WINHTTP_HELPER::sm_pfnWinHttpWebSocketSend;
PFN_WINHTTP_WEBSOCKET_RECEIVE
WINHTTP_HELPER::sm_pfnWinHttpWebSocketReceive;
PFN_WINHTTP_WEBSOCKET_SHUTDOWN
WINHTTP_HELPER::sm_pfnWinHttpWebSocketShutdown;
PFN_WINHTTP_WEBSOCKET_QUERY_CLOSE_STATUS
WINHTTP_HELPER::sm_pfnWinHttpWebSocketQueryCloseStatus;
//static
HRESULT
WINHTTP_HELPER::StaticInitialize(
VOID
)
{
HRESULT hr = S_OK;
if (!g_fWebSocketSupported)
{
return S_OK;
}
//
// Initialize the function pointers for WinHttp Websocket API's.
//
HMODULE hWinHttp = GetModuleHandleA("winhttp.dll");
if (hWinHttp == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
sm_pfnWinHttpWebSocketCompleteUpgrade = (PFN_WINHTTP_WEBSOCKET_COMPLETE_UPGRADE)
GetProcAddress(hWinHttp, "WinHttpWebSocketCompleteUpgrade");
if (sm_pfnWinHttpWebSocketCompleteUpgrade == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
sm_pfnWinHttpWebSocketQueryCloseStatus = (PFN_WINHTTP_WEBSOCKET_QUERY_CLOSE_STATUS)
GetProcAddress(hWinHttp, "WinHttpWebSocketQueryCloseStatus");
if (sm_pfnWinHttpWebSocketQueryCloseStatus == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
sm_pfnWinHttpWebSocketReceive = (PFN_WINHTTP_WEBSOCKET_RECEIVE)
GetProcAddress(hWinHttp, "WinHttpWebSocketReceive");
if (sm_pfnWinHttpWebSocketReceive == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
sm_pfnWinHttpWebSocketSend = (PFN_WINHTTP_WEBSOCKET_SEND)
GetProcAddress(hWinHttp, "WinHttpWebSocketSend");
if (sm_pfnWinHttpWebSocketSend == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
sm_pfnWinHttpWebSocketShutdown = (PFN_WINHTTP_WEBSOCKET_SHUTDOWN)
GetProcAddress(hWinHttp, "WinHttpWebSocketShutdown");
if (sm_pfnWinHttpWebSocketShutdown == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished;
}
Finished:
return hr;
}
//static
VOID
WINHTTP_HELPER::GetFlagsFromBufferType(
__in WINHTTP_WEB_SOCKET_BUFFER_TYPE BufferType,
__out BOOL * pfUtf8Encoded,
__out BOOL * pfFinalFragment,
__out BOOL * pfClose
)
{
switch (BufferType)
{
case WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE:
*pfUtf8Encoded = FALSE;
*pfFinalFragment = TRUE;
*pfClose = FALSE;
break;
case WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE:
*pfUtf8Encoded = FALSE;
*pfFinalFragment = FALSE;
*pfClose = FALSE;
break;
case WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE:
*pfUtf8Encoded = TRUE;
*pfFinalFragment = TRUE;
*pfClose = FALSE;
break;
case WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE:
*pfUtf8Encoded = TRUE;
*pfFinalFragment = FALSE;
*pfClose = FALSE;
break;
case WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE:
*pfUtf8Encoded = FALSE;
*pfFinalFragment = FALSE;
*pfClose = TRUE;
break;
}
}
//static
VOID
WINHTTP_HELPER::GetBufferTypeFromFlags(
__in BOOL fUtf8Encoded,
__in BOOL fFinalFragment,
__in BOOL fClose,
__out WINHTTP_WEB_SOCKET_BUFFER_TYPE* pBufferType
)
{
if (fClose)
{
*pBufferType = WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE;
}
else
if (fUtf8Encoded)
{
if (fFinalFragment)
{
*pBufferType = WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE;
}
else
{
*pBufferType = WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE;
}
}
else
{
if (fFinalFragment)
{
*pBufferType = WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE;
}
else
{
*pBufferType = WINHTTP_WEB_SOCKET_BINARY_FRAGMENT_BUFFER_TYPE;
}
}
return;
}