diff --git a/README.md b/README.md index 73fa4b5e..130404da 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,6 @@ This can be a head-scratcher since IIS Express 8 gives you both 32-bit and 64-bi **Howtos** ======= - [the basics](http://tomasz.janczuk.org/2011/08/hosting-nodejs-applications-in-iis-on.html) -- [the basics (Pусский перевод)](http://softdroid.net/hosting-nodejs-applications-ru) - [**NEW: websockets**] (http://tomasz.janczuk.org/2012/11/how-to-use-websockets-with-nodejs-apps.html) - [using with express framework](http://tomasz.janczuk.org/2011/08/hosting-express-nodejs-applications-in.html) - [using with URL rewrite module](http://tomasz.janczuk.org/2011/08/using-url-rewriting-with-nodejs.html) @@ -61,7 +60,7 @@ This can be a head-scratcher since IIS Express 8 gives you both 32-bit and 64-bi - [using with MVC](http://weblogs.asp.net/jgalloway/archive/2011/10/26/using-node-js-in-an-asp-net-mvc-application-with-iisnode.aspx) - [portuguese: node.js no windows: instalando o iisnode](http://vivina.com.br/nodejs-windows-parte-2) - [integrated debugging](http://tomasz.janczuk.org/2011/11/debug-nodejs-applications-on-windows.html) -- [**NEW: integrated debugging with node-inspector v0.7.3**](http://www.ranjithr.com/?p=98) +- [Debugging via VSCode](https://blog.immatt.com/2018/10/09/iisnode-modern-debugging-via-vscode/) - [pub/sub server using faye](http://weblogs.asp.net/cibrax/archive/2011/12/12/transform-your-iis-into-a-real-time-pub-sub-engine-with-faye-node.aspx) - [appharbor uses iisnode](http://blog.appharbor.com/2012/01/19/announcing-node-js-support) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..e138ec5d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/index.html b/index.html new file mode 100644 index 00000000..577b4512 --- /dev/null +++ b/index.html @@ -0,0 +1,164 @@ + + + + iisnode-debug + + + +

HTTP response diagnostics for iisnode

+ +

This request

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Processing time [ms]N/A
Named pipe connection retry countN/A
HRESULTN/A
Server DNS nameN/A
w3wp.exe PIDN/A
node.exe PIDN/A
+ +

Memory

+ + Memory consumption chart + +

Counters

+ + + + + + + + + + + + + + + + + + +
Active node.exe processes serving this applicationN/A
Active HTTP requests in this applicationN/A
Active HTTP requests in this node.exe processN/A
Total node.js requests processed by w3wp.exeN/A
+ +

Environment

+ + + + + + + + + + + + + + +
Version of iisnodeN/A
Server full DNS nameN/A
Full node.exe pathN/A
+ +

Actions

+ + Bugs, feedback, questions
+ iisnode project home page
+ + Debug node.js applications hosted in IIS using iisnode +
+ + Use Event Tracing for Windows (ETW) to get more diagnostics information +
+ + Windows Azure node.js developer center + + + + + diff --git a/src/config/iisnode_dev_x64.xml b/src/config/iisnode_dev_x64.xml index cab240cf..9d6a435c 100644 --- a/src/config/iisnode_dev_x64.xml +++ b/src/config/iisnode_dev_x64.xml @@ -32,7 +32,7 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/ - + @@ -60,5 +60,6 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/ + diff --git a/src/config/iisnode_dev_x86_on_x64.xml b/src/config/iisnode_dev_x86_on_x64.xml index aaa0bbc9..ab17fba8 100644 --- a/src/config/iisnode_dev_x86_on_x64.xml +++ b/src/config/iisnode_dev_x86_on_x64.xml @@ -32,7 +32,7 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/ - + @@ -60,5 +60,6 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/ + diff --git a/src/config/iisnode_dev_x86_on_x86.xml b/src/config/iisnode_dev_x86_on_x86.xml index 9606a25e..efab7f7d 100644 --- a/src/config/iisnode_dev_x86_on_x86.xml +++ b/src/config/iisnode_dev_x86_on_x86.xml @@ -32,7 +32,7 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/ - + @@ -60,5 +60,6 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/ + diff --git a/src/config/iisnode_express_schema.xml b/src/config/iisnode_express_schema.xml index 2ad9eddc..a6c95a85 100644 --- a/src/config/iisnode_express_schema.xml +++ b/src/config/iisnode_express_schema.xml @@ -32,7 +32,7 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/ - + @@ -60,5 +60,6 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/ + diff --git a/src/config/iisnode_express_schema_x64.xml b/src/config/iisnode_express_schema_x64.xml index ddf6368a..fb39746b 100644 --- a/src/config/iisnode_express_schema_x64.xml +++ b/src/config/iisnode_express_schema_x64.xml @@ -32,7 +32,7 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/ - + @@ -60,5 +60,6 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/ + diff --git a/src/config/iisnode_schema.xml b/src/config/iisnode_schema.xml index 4088c009..62e8004f 100644 --- a/src/config/iisnode_schema.xml +++ b/src/config/iisnode_schema.xml @@ -32,7 +32,7 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/ - + @@ -60,5 +60,6 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/ + diff --git a/src/config/iisnode_schema_x64.xml b/src/config/iisnode_schema_x64.xml index 8d454eae..f1561969 100644 --- a/src/config/iisnode_schema_x64.xml +++ b/src/config/iisnode_schema_x64.xml @@ -32,7 +32,7 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/ - + @@ -60,5 +60,6 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/ + diff --git a/src/iisnode/casyncmanager.cpp b/src/iisnode/casyncmanager.cpp index e0235e8b..50786340 100644 --- a/src/iisnode/casyncmanager.cpp +++ b/src/iisnode/casyncmanager.cpp @@ -4,14 +4,15 @@ extern RtlNtStatusToDosError pRtlNtStatusToDosError; void ASYNC_CONTEXT::RunSynchronousContinuations() { - while (this->continueSynchronously) + BOOL fCompletionPosted = FALSE; + while (!fCompletionPosted && this->continueSynchronously) { // The continueSynchronously is used to unwind the call stack // to avoid stack overflow in case of a synchronous IO completions this->continueSynchronously = FALSE; DWORD bytesCompleteted = this->bytesCompleteted; this->bytesCompleteted = 0; - this->completionProcessor(S_OK, bytesCompleteted, (LPOVERLAPPED)this); + this->completionProcessor(S_OK, bytesCompleteted, (LPOVERLAPPED)this, &fCompletionPosted); } } @@ -171,19 +172,31 @@ unsigned int WINAPI CAsyncManager::Worker(void* arg) { OVERLAPPED_ENTRY* entry = entries; for (int i = 0; i < entriesRemoved; i++) - { + { + BOOL fCompletionPosted = FALSE; + if (0L == entry->lpCompletionKey && NULL != (ctx = (ASYNC_CONTEXT*)entry->lpOverlapped) && NULL != ctx->completionProcessor) // regular IO completion - invoke custom processor { + error = (entry->lpOverlapped->Internal == STATUS_SUCCESS) ? ERROR_SUCCESS : pRtlNtStatusToDosError(entry->lpOverlapped->Internal); ctx = (ASYNC_CONTEXT*)entry->lpOverlapped; + ctx->completionProcessor( (0 == entry->dwNumberOfBytesTransferred && ERROR_SUCCESS == error) ? ERROR_NO_DATA : error, entry->dwNumberOfBytesTransferred, - (LPOVERLAPPED)ctx); - ctx->RunSynchronousContinuations(); + (LPOVERLAPPED)ctx, + &fCompletionPosted); + + if(!fCompletionPosted) + { + ctx->RunSynchronousContinuations(); + } + + CNodeHttpStoredContext* storedCtx = (CNodeHttpStoredContext*)ctx->data; + storedCtx->DereferenceNodeHttpStoredContext(); } else if (-1L == entry->lpCompletionKey) // shutdown initiated from Terminate { @@ -194,8 +207,14 @@ unsigned int WINAPI CAsyncManager::Worker(void* arg) if (-2L == entry->lpCompletionKey) // completion of an alertable wait state timer initialized from OnTimer { ctx = (ASYNC_CONTEXT*)entry->lpOverlapped; - ctx->completionProcessor(S_OK, 0, (LPOVERLAPPED)ctx); - ctx->RunSynchronousContinuations(); + ctx->completionProcessor(S_OK, 0, (LPOVERLAPPED)ctx, &fCompletionPosted); + if(!fCompletionPosted) + { + ctx->RunSynchronousContinuations(); + } + + CNodeHttpStoredContext* storedCtx = (CNodeHttpStoredContext*)ctx->data; + storedCtx->DereferenceNodeHttpStoredContext(); } else if (-3L == entry->lpCompletionKey) // continuation initiated form PostContinuation { diff --git a/src/iisnode/casyncmanager.h b/src/iisnode/casyncmanager.h index d3b5b044..be4a3ce5 100644 --- a/src/iisnode/casyncmanager.h +++ b/src/iisnode/casyncmanager.h @@ -1,9 +1,18 @@ #ifndef __CASYNCMANAGER_H__ #define __CASYNCMANAGER_H__ +typedef +VOID +(WINAPI *LPOVERLAPPED_COMPLETION_ROUTINE_IISNODE)( + _In_ DWORD dwErrorCode, + _In_ DWORD dwNumberOfBytesTransfered, + _Inout_ LPOVERLAPPED lpOverlapped, + _Inout_ BOOL * fCompletionPosted + ); + typedef struct { OVERLAPPED overlapped; // this member must be first in the struct - LPOVERLAPPED_COMPLETION_ROUTINE completionProcessor; + LPOVERLAPPED_COMPLETION_ROUTINE_IISNODE completionProcessor; BOOL continueSynchronously; void* data; HANDLE timer; diff --git a/src/iisnode/cmoduleconfiguration.cpp b/src/iisnode/cmoduleconfiguration.cpp index 46c0f538..3562ddd8 100644 --- a/src/iisnode/cmoduleconfiguration.cpp +++ b/src/iisnode/cmoduleconfiguration.cpp @@ -87,15 +87,21 @@ CModuleConfiguration::~CModuleConfiguration() this->debuggerPathSegment = NULL; } + if( NULL != this->debugPortRange ) + { + delete [] this->debugPortRange; + this->debugPortRange = NULL; + } + if (NULL != this->node_env) { - delete this->node_env; + delete [] this->node_env; this->node_env = NULL; } if (NULL != this->watchedFiles) { - delete this->watchedFiles; + delete [] this->watchedFiles; this->watchedFiles = NULL; } @@ -187,7 +193,7 @@ HRESULT CModuleConfiguration::CreateNodeEnvironment(IHttpContext* ctx, DWORD deb // allocate memory for new environment variables - tmpSize = 32767 - environmentSize; + tmpSize = 65536; // hard coded for now, change this to auto allocate based on the values. ErrorIf(NULL == (tmpIndex = tmpStart = new char[tmpSize]), ERROR_NOT_ENOUGH_MEMORY); RtlZeroMemory(tmpIndex, tmpSize); @@ -814,6 +820,10 @@ HRESULT CModuleConfiguration::ApplyConfigOverrideKeyValue(IHttpContext* context, { CheckError(GetDWORD(valueStart, &config->maxLogFiles)); } + else if (0 == strcmpi(keyStart, "nodeProcessStickySessions")) + { + CheckError(GetBOOL(valueStart, &config->nodeProcessStickySessions)); + } else if (0 == strcmpi(keyStart, "loggingEnabled")) { CheckError(GetBOOL(valueStart, &config->loggingEnabled)); @@ -1228,6 +1238,7 @@ HRESULT CModuleConfiguration::GetConfig(IHttpContext* context, CModuleConfigurat CheckError(GetDWORD(section, L"maxTotalLogFileSizeInKB", &c->maxTotalLogFileSizeInKB)); CheckError(GetDWORD(section, L"maxLogFileSizeInKB", &c->maxLogFileSizeInKB)); CheckError(GetDWORD(section, L"maxLogFiles", &c->maxLogFiles)); + CheckError(GetBOOL(section, L"nodeProcessStickySessions", &c->nodeProcessStickySessions, FALSE)); CheckError(GetBOOL(section, L"loggingEnabled", &c->loggingEnabled, TRUE)); CheckError(GetBOOL(section, L"devErrorsEnabled", &c->devErrorsEnabled, TRUE)); CheckError(GetBOOL(section, L"flushResponse", &c->flushResponse, FALSE)); @@ -1236,7 +1247,7 @@ HRESULT CModuleConfiguration::GetConfig(IHttpContext* context, CModuleConfigurat CheckError(GetString(section, L"debuggerExtensionDll", &c->debuggerExtensionDll)); CheckError(GetBOOL(section, L"debugHeaderEnabled", &c->debugHeaderEnabled, FALSE)); CheckError(GetBOOL(section, L"recycleSignalEnabled", &c->recycleSignalEnabled, FALSE)); - CheckError(GetString(section, L"debuggerVirtualDir", &c->debuggerVirtualDir)); + CheckError(GetString(section, L"debuggerVirtualDir", &c->debuggerVirtualDir)); c->debuggerVirtualDirLength = wcslen(c->debuggerVirtualDir); CheckError(GetString(section, L"node_env", &c->node_env)); CheckError(GetString(section, L"debuggerPortRange", &c->debugPortRange)); @@ -1433,6 +1444,11 @@ BOOL CModuleConfiguration::GetDebuggingEnabled(IHttpContext* ctx) GETCONFIG(debuggingEnabled) } +BOOL CModuleConfiguration::GetProcessStickySessions(IHttpContext* ctx) +{ + GETCONFIG(nodeProcessStickySessions) +} + PWSTR CModuleConfiguration::GetDebuggerExtensionDll(IHttpContext* ctx) { GETCONFIG(debuggerExtensionDll) diff --git a/src/iisnode/cmoduleconfiguration.h b/src/iisnode/cmoduleconfiguration.h index c951fca3..8151cd3b 100644 --- a/src/iisnode/cmoduleconfiguration.h +++ b/src/iisnode/cmoduleconfiguration.h @@ -52,6 +52,7 @@ class CModuleConfiguration : public IHttpStoredContext static BOOL invalid; SRWLOCK srwlock; LPWSTR configOverrides; + BOOL nodeProcessStickySessions; static IHttpServer* server; static HTTP_MODULE_ID moduleId; @@ -129,6 +130,7 @@ class CModuleConfiguration : public IHttpStoredContext static BOOL GetEnableXFF(IHttpContext* ctx); static HRESULT GetPromoteServerVars(IHttpContext* ctx, char*** vars, int* count); static LPWSTR GetConfigOverrides(IHttpContext* ctx); + static BOOL GetProcessStickySessions(IHttpContext* ctx); static HRESULT CreateNodeEnvironment(IHttpContext* ctx, DWORD debugPort, PCH namedPipe, PCH signalPipeName, PCH* env); diff --git a/src/iisnode/cnodeeventprovider.cpp b/src/iisnode/cnodeeventprovider.cpp index 0d5b56f3..97df5d6c 100644 --- a/src/iisnode/cnodeeventprovider.cpp +++ b/src/iisnode/cnodeeventprovider.cpp @@ -107,10 +107,14 @@ HRESULT CNodeEventProvider::Log(IHttpContext *context, PCWSTR message, UCHAR lev } } + /* + commented because there might be a race condition after calling PostCompletion and then using the IHttpContext object. + Uncomment this only after making sure there is no race condition. (IHttpContext cannot be used after calling PostCompletion) if( IsEnabled( context->GetTraceContext(), level ) ) { CheckError( RaiseEvent( context->GetTraceContext(), message, level, activityId ) ); } + */ return S_OK; Error: diff --git a/src/iisnode/cnodehttpmodule.cpp b/src/iisnode/cnodehttpmodule.cpp index 41f8a2ef..a4876b68 100644 --- a/src/iisnode/cnodehttpmodule.cpp +++ b/src/iisnode/cnodehttpmodule.cpp @@ -219,7 +219,9 @@ REQUEST_NOTIFICATION_STATUS CNodeHttpModule::OnAsyncCompletion( bytesCompleted = async->bytesCompleteted; async->bytesCompleteted = 0; } - async->completionProcessor(pCompletionInfo->GetCompletionStatus(), bytesCompleted, ctx->GetOverlapped()); + BOOL fCompletionPosted = FALSE; + async->completionProcessor(pCompletionInfo->GetCompletionStatus(), bytesCompleted, ctx->GetOverlapped(), &fCompletionPosted); + async->RunSynchronousContinuations(); } @@ -237,6 +239,17 @@ REQUEST_NOTIFICATION_STATUS CNodeHttpModule::OnAsyncCompletion( // result = RQ_NOTIFICATION_CONTINUE; } + else + { + if (0 == value) // decreases ref count set to 1 in the ctor of CNodeHttpStoredContext + { + result = ctx->GetRequestNotificationStatus(); + } + else + { + result = RQ_NOTIFICATION_PENDING; + } + } switch (result) { @@ -266,6 +279,8 @@ REQUEST_NOTIFICATION_STATUS CNodeHttpModule::OnAsyncCompletion( break; }; + ctx->DereferenceNodeHttpStoredContext(); + return result; } diff --git a/src/iisnode/cnodehttpstoredcontext.cpp b/src/iisnode/cnodehttpstoredcontext.cpp index 80fc9fef..bb0e1eed 100644 --- a/src/iisnode/cnodehttpstoredcontext.cpp +++ b/src/iisnode/cnodehttpstoredcontext.cpp @@ -6,7 +6,7 @@ CNodeHttpStoredContext::CNodeHttpStoredContext(CNodeApplication* nodeApplication requestNotificationStatus(RQ_NOTIFICATION_PENDING), connectionRetryCount(0), pendingAsyncOperationCount(1), targetUrl(NULL), targetUrlLength(0), childContext(NULL), isConnectionFromPool(FALSE), expectResponseBody(TRUE), closeConnection(FALSE), isUpgrade(FALSE), upgradeContext(NULL), opaqueFlagSet(FALSE), requestPumpStarted(FALSE), - responseChunkBufferSize(0) + responseChunkBufferSize(0), m_cRefs(1) { IHttpTraceContext* tctx; LPCGUID pguid; @@ -75,7 +75,7 @@ CNodeApplication* CNodeHttpStoredContext::GetNodeApplication() return this->nodeApplication; } -void CNodeHttpStoredContext::SetNextProcessor(LPOVERLAPPED_COMPLETION_ROUTINE processor) +void CNodeHttpStoredContext::SetNextProcessor(LPOVERLAPPED_COMPLETION_ROUTINE_IISNODE processor) { this->asyncContext.completionProcessor = processor; this->SetContinueSynchronously(FALSE); @@ -95,7 +95,7 @@ LPOVERLAPPED CNodeHttpStoredContext::InitializeOverlapped() void CNodeHttpStoredContext::CleanupStoredContext() { - delete this; + DereferenceNodeHttpStoredContext(); } CNodeProcess* CNodeHttpStoredContext::GetNodeProcess() diff --git a/src/iisnode/cnodehttpstoredcontext.h b/src/iisnode/cnodehttpstoredcontext.h index ae116f0a..f38875d7 100644 --- a/src/iisnode/cnodehttpstoredcontext.h +++ b/src/iisnode/cnodehttpstoredcontext.h @@ -40,12 +40,35 @@ class CNodeHttpStoredContext : public IHttpStoredContext HTTP_DATA_CHUNK responseChunk; DWORD responseChunkBufferSize; CNodeEventProvider* eventProvider; + ~CNodeHttpStoredContext(); + + mutable LONG m_cRefs; public: // Context is owned by the caller CNodeHttpStoredContext(CNodeApplication* nodeApplication, CNodeEventProvider* eventProvider, IHttpContext* context); - ~CNodeHttpStoredContext(); + + VOID + ReferenceNodeHttpStoredContext( + VOID + ) + { + InterlockedIncrement(&m_cRefs); + } + + VOID + DereferenceNodeHttpStoredContext( + VOID + ) + { + _ASSERT(m_cRefs != 0); + + if (InterlockedDecrement(&m_cRefs) == 0) + { + delete this; + } + } IHttpContext* GetHttpContext(); CNodeApplication* GetNodeApplication(); @@ -78,7 +101,7 @@ class CNodeHttpStoredContext : public IHttpStoredContext IHttpContext* GetChildContext(); DWORD GetBytesCompleted(); - void SetNextProcessor(LPOVERLAPPED_COMPLETION_ROUTINE processor); + void SetNextProcessor(LPOVERLAPPED_COMPLETION_ROUTINE_IISNODE processor); void SetNodeProcess(CNodeProcess* process); void SetPipe(HANDLE pipe); void SetConnectionRetryCount(DWORD count); diff --git a/src/iisnode/cnodeprocess.cpp b/src/iisnode/cnodeprocess.cpp index 1ab6683d..f9e0ed06 100644 --- a/src/iisnode/cnodeprocess.cpp +++ b/src/iisnode/cnodeprocess.cpp @@ -20,6 +20,18 @@ CNodeProcess::~CNodeProcess() this->process = NULL; } + if (NULL != this->startupInfo.hStdOutput && INVALID_HANDLE_VALUE != this->startupInfo.hStdOutput) + { + CloseHandle(this->startupInfo.hStdOutput); + this->startupInfo.hStdOutput = INVALID_HANDLE_VALUE; + } + + if (NULL != this->startupInfo.hStdError && INVALID_HANDLE_VALUE != this->startupInfo.hStdError) + { + CloseHandle(this->startupInfo.hStdError); + this->startupInfo.hStdError = INVALID_HANDLE_VALUE; + } + if (NULL != this->processWatcher) { // The following check prevents a dead-lock between process watcher thread calling OnProcessExited diff --git a/src/iisnode/cnodeprocessmanager.cpp b/src/iisnode/cnodeprocessmanager.cpp index cb6df5d6..4d02f263 100644 --- a/src/iisnode/cnodeprocessmanager.cpp +++ b/src/iisnode/cnodeprocessmanager.cpp @@ -2,7 +2,7 @@ CNodeProcessManager::CNodeProcessManager(CNodeApplication* application, IHttpContext* context) : application(application), processes(NULL), currentProcess(0), isClosing(FALSE), - refCount(1), gracefulShutdownProcessCount(0) + refCount(1), gracefulShutdownProcessCount(0), stickySessions(FALSE) { if (this->GetApplication()->IsDebugMode()) { @@ -13,6 +13,8 @@ CNodeProcessManager::CNodeProcessManager(CNodeApplication* application, IHttpCon this->processCount = CModuleConfiguration::GetNodeProcessCountPerApplication(context); } + this->stickySessions = CModuleConfiguration::GetProcessStickySessions(context); + // cache event provider since the application can be disposed prior to CNodeProcessManager this->eventProvider = this->GetApplication()->GetApplicationManager()->GetEventProvider(); @@ -107,10 +109,57 @@ HRESULT CNodeProcessManager::AddProcess(int ordinal, IHttpContext* context) return hr; } +int CNodeProcessManager::ExtractStickySessionsProcess( PCSTR pszCookie ) +{ + const CHAR* pszKey = "iisnode.session.cookie"; + const CHAR* pszDivider = "="; + const CHAR* pszNext = ";"; + CHAR acProcess[10]; // characters needed for MAXDWORD64 + memset(acProcess, 0, sizeof(acProcess)); + + const CHAR* pStart = strstr(pszCookie, pszKey); + const CHAR* pEnd = NULL; + + if( pStart != NULL ) + { + pStart = strstr(pStart, pszDivider); + if( pStart != NULL ) + { + // skip the '='; + pStart++; + if( pStart != NULL ) + { + pEnd = strstr(pStart, pszNext); + if(!pEnd) + { + pEnd = pStart; + while (*pEnd) /* Works because end-of-string and FALSE are identical. */ + { + if((pEnd - pStart) >= sizeof(acProcess)) + { + break; + } + pEnd++; + } + } + + if((pEnd - pStart) < sizeof(acProcess)) + { + memcpy(acProcess, pStart, pEnd - pStart); // copy result + return atoi(acProcess); + } + } + } + } + + return -1; +} + HRESULT CNodeProcessManager::Dispatch(CNodeHttpStoredContext* request) { HRESULT hr; unsigned int tmpProcess, processToUse; + int processInCookie = -1; CheckNull(request); @@ -122,23 +171,56 @@ HRESULT CNodeProcessManager::Dispatch(CNodeHttpStoredContext* request) if (!this->isClosing) { + // + // if sticky sessions is enabled, use the process specified in the cookie else // employ a round robin routing logic to get a "ticket" to use a process with a specific ordinal number - - if (1 == this->processCount) + // + + if(this->stickySessions) // sticky sessions { - processToUse = 0; + PCSTR pszCookieHeader = NULL; + pszCookieHeader = request->GetHttpContext()->GetRequest()->GetHeader(HttpHeaderCookie); + if(pszCookieHeader != NULL) // There might be a sticky session + { + processInCookie = ExtractStickySessionsProcess(pszCookieHeader); + } } - else + + if( processInCookie < 0) { - do + // + // employ a round robin routing logic to get a "ticket" to use a process with a specific ordinal number + // + + if (1 == this->processCount) + { + processToUse = 0; + } + else { - tmpProcess = this->currentProcess; - processToUse = (tmpProcess + 1) % this->processCount; - } while (tmpProcess != InterlockedCompareExchange(&this->currentProcess, processToUse, tmpProcess)); + do + { + tmpProcess = this->currentProcess; + processToUse = (tmpProcess + 1) % this->processCount; + } while (tmpProcess != InterlockedCompareExchange(&this->currentProcess, processToUse, tmpProcess)); + } + } + else + { + // ensure the cookie did not carry a value outside of the possible processes + processToUse = (unsigned int)processInCookie % this->processCount; } - // try dispatch to that process + if(this->stickySessions && (processInCookie < 0)) + { + // Set cookie for sticky session with selected process + CHAR buffer [255]; + int cCount = sprintf (buffer, "iisnode.session.cookie=%d", processToUse); + ErrorIf( cCount < 0, E_OUTOFMEMORY ); + CheckError(request->GetHttpContext()->GetResponse()->SetHeader(HttpHeaderSetCookie, buffer, cCount, FALSE)); + } + // try dispatch to that process if (NULL != this->processes[processToUse]) { CheckError(this->processes[processToUse]->AcceptRequest(request)); @@ -172,7 +254,9 @@ HRESULT CNodeProcessManager::Dispatch(CNodeHttpStoredContext* request) if (request) { this->GetEventProvider()->Log(request->GetHttpContext(), L"iisnode failed to accept a request beacuse the application is recycling", WINEVENT_LEVEL_ERROR, request->GetActivityId()); - CProtocolBridge::SendEmptyResponse(request, 503, CNodeConstants::IISNODE_ERROR_FAILED_ACCEPT_REQUEST_APP_RECYCLE, _T("Service Unavailable"), IISNODE_ERROR_APPLICATION_IS_RECYCLING); + + BOOL fCompletionPosted = FALSE; + CProtocolBridge::SendEmptyResponse(request, 503, CNodeConstants::IISNODE_ERROR_FAILED_ACCEPT_REQUEST_APP_RECYCLE, _T("Service Unavailable"), IISNODE_ERROR_APPLICATION_IS_RECYCLING, &fCompletionPosted); } this->DecRef(); // incremented at the beginning of this method @@ -185,7 +269,8 @@ HRESULT CNodeProcessManager::Dispatch(CNodeHttpStoredContext* request) if (!CProtocolBridge::SendIisnodeError(request, hr)) { - CProtocolBridge::SendEmptyResponse(request, 503, CNodeConstants::IISNODE_ERROR_INIT_PROCESS_REQUEST, _T("Service Unavailable"), hr); + BOOL fCompletionPosted = FALSE; + CProtocolBridge::SendEmptyResponse(request, 503, CNodeConstants::IISNODE_ERROR_INIT_PROCESS_REQUEST, _T("Service Unavailable"), hr, &fCompletionPosted); } this->DecRef(); // incremented at the beginning of this method diff --git a/src/iisnode/cnodeprocessmanager.h b/src/iisnode/cnodeprocessmanager.h index 0c41737e..ac0d741f 100644 --- a/src/iisnode/cnodeprocessmanager.h +++ b/src/iisnode/cnodeprocessmanager.h @@ -22,6 +22,7 @@ class CNodeProcessManager CNodeApplication* application; CNodeProcess** processes; DWORD processCount; + BOOL stickySessions; unsigned int currentProcess; SRWLOCK srwlock; DWORD gracefulShutdownTimeout; @@ -40,6 +41,7 @@ class CNodeProcessManager CNodeProcessManager(CNodeApplication* application, IHttpContext* context); ~CNodeProcessManager(); + int ExtractStickySessionsProcess( PCSTR pszCookie ); HRESULT EmptyWorkingSet(); CNodeApplication* GetApplication(); HRESULT Initialize(IHttpContext* context); diff --git a/src/iisnode/cprotocolbridge.cpp b/src/iisnode/cprotocolbridge.cpp index 726758ff..996af08f 100644 --- a/src/iisnode/cprotocolbridge.cpp +++ b/src/iisnode/cprotocolbridge.cpp @@ -7,6 +7,9 @@ HRESULT CProtocolBridge::PostponeProcessing(CNodeHttpStoredContext* context, DWO delay.QuadPart = dueTime; delay.QuadPart *= -10000; // convert from ms to 100ns units + // will be dereferenced in AsynManager::Worker + context->ReferenceNodeHttpStoredContext(); + return async->SetTimer(context->GetAsyncContext(), &delay); } @@ -193,10 +196,12 @@ BOOL CProtocolBridge::SendIisnodeError(CNodeHttpStoredContext* ctx, HRESULT hr) ctx->SetPipe(INVALID_HANDLE_VALUE); } + BOOL fCompletionPosted = FALSE; CProtocolBridge::FinalizeResponseCore( ctx, RQ_NOTIFICATION_FINISH_REQUEST, hr, + &fCompletionPosted, ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider(), L"iisnode posts completion from SendIisnodeError", WINEVENT_LEVEL_VERBOSE); @@ -405,7 +410,7 @@ BOOL CProtocolBridge::SendDevError(CNodeHttpStoredContext* context, return false; } -HRESULT CProtocolBridge::SendEmptyResponse(CNodeHttpStoredContext* context, USHORT status, USHORT subStatus, PCTSTR reason, HRESULT hresult, BOOL disableCache) +HRESULT CProtocolBridge::SendEmptyResponse(CNodeHttpStoredContext* context, USHORT status, USHORT subStatus, PCTSTR reason, HRESULT hresult, BOOL *pfCompletionPosted, BOOL disableCache) { context->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(context->GetHttpContext(), L"iisnode request processing failed for reasons unrecognized by iisnode", WINEVENT_LEVEL_VERBOSE, context->GetActivityId()); @@ -427,6 +432,7 @@ HRESULT CProtocolBridge::SendEmptyResponse(CNodeHttpStoredContext* context, USHO context, RQ_NOTIFICATION_FINISH_REQUEST, hresult, + pfCompletionPosted, context->GetNodeApplication()->GetApplicationManager()->GetEventProvider(), L"iisnode posts completion from SendEmtpyResponse", WINEVENT_LEVEL_VERBOSE); @@ -474,6 +480,8 @@ HRESULT CProtocolBridge::InitiateRequest(CNodeHttpStoredContext* context) BOOL mainDebuggerPage = FALSE; IHttpContext* child = NULL; BOOL completionExpected; + BOOL fCompletionPosted = FALSE; + BOOL fReference = FALSE; // determine what the target path of the request is @@ -532,17 +540,22 @@ HRESULT CProtocolBridge::InitiateRequest(CNodeHttpStoredContext* context) CheckError(child->GetRequest()->SetUrl(context->GetTargetUrl(), context->GetTargetUrlLength(), FALSE)); context->SetChildContext(child); context->SetNextProcessor(CProtocolBridge::ChildContextCompleted); - + + context->ReferenceNodeHttpStoredContext(); + fReference = TRUE; + CheckError(context->GetHttpContext()->ExecuteRequest(TRUE, child, 0, NULL, &completionExpected)); if (!completionExpected) { - CProtocolBridge::ChildContextCompleted(S_OK, 0, context->GetOverlapped()); + CProtocolBridge::ChildContextCompleted(S_OK, 0, context->GetOverlapped(), &fCompletionPosted); + context->DereferenceNodeHttpStoredContext(); + fReference = FALSE; } } else { context->SetNextProcessor(CProtocolBridge::CreateNamedPipeConnection); - CProtocolBridge::CreateNamedPipeConnection(S_OK, 0, context->InitializeOverlapped()); + CProtocolBridge::CreateNamedPipeConnection(S_OK, 0, context->InitializeOverlapped(), &fCompletionPosted); } return S_OK; @@ -555,10 +568,15 @@ HRESULT CProtocolBridge::InitiateRequest(CNodeHttpStoredContext* context) context->SetChildContext(NULL); } + if(fReference) + { + context->DereferenceNodeHttpStoredContext(); + } + return hr; } -void WINAPI CProtocolBridge::ChildContextCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped) +void WINAPI CProtocolBridge::ChildContextCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted) { CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped); @@ -584,6 +602,7 @@ void WINAPI CProtocolBridge::ChildContextCompleted(DWORD error, DWORD bytesTrans ctx, RQ_NOTIFICATION_CONTINUE, error, + pfCompletionPosted, ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider(), L"iisnode posts completion from ChildContextCompleted", WINEVENT_LEVEL_VERBOSE); @@ -591,7 +610,7 @@ void WINAPI CProtocolBridge::ChildContextCompleted(DWORD error, DWORD bytesTrans return; } -void WINAPI CProtocolBridge::CreateNamedPipeConnection(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped) +void WINAPI CProtocolBridge::CreateNamedPipeConnection(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted) { HRESULT hr; CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped); @@ -607,6 +626,12 @@ void WINAPI CProtocolBridge::CreateNamedPipeConnection(DWORD error, DWORD bytesT if (INVALID_HANDLE_VALUE == pipe) { + // Clean up old pipe handle, otherwise there will be a handle leak + HANDLE oldPipe = ctx->GetPipe(); + if (oldPipe != NULL && oldPipe != INVALID_HANDLE_VALUE) { + CloseHandle(oldPipe); + } + ErrorIf(INVALID_HANDLE_VALUE == (pipe = CreateFile( ctx->GetNodeProcess()->GetNamedPipeName(), GENERIC_READ | GENERIC_WRITE, @@ -635,7 +660,7 @@ void WINAPI CProtocolBridge::CreateNamedPipeConnection(DWORD error, DWORD bytesT ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(), L"iisnode created named pipe connection to the node.exe process", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId()); - CProtocolBridge::SendHttpRequestHeaders(ctx); + CProtocolBridge::SendHttpRequestHeaders(ctx, pfCompletionPosted); return; @@ -649,7 +674,7 @@ void WINAPI CProtocolBridge::CreateNamedPipeConnection(DWORD error, DWORD bytesT { ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(), L"iisnode was unable to establish named pipe connection to the node.exe process because the named pipe server is too busy", WINEVENT_LEVEL_ERROR, ctx->GetActivityId()); - CProtocolBridge::SendEmptyResponse(ctx, 503, CNodeConstants::IISNODE_ERROR_PIPE_CONNECTION_TOO_BUSY, _T("Service Unavailable"), hr); + CProtocolBridge::SendEmptyResponse(ctx, 503, CNodeConstants::IISNODE_ERROR_PIPE_CONNECTION_TOO_BUSY, _T("Service Unavailable"), hr, pfCompletionPosted); } else { @@ -659,7 +684,8 @@ void WINAPI CProtocolBridge::CreateNamedPipeConnection(DWORD error, DWORD bytesT 500, CNodeConstants::IISNODE_ERROR_PIPE_CONNECTION, _T("Internal Server Error"), - hr ); + hr, + pfCompletionPosted); } } else if (ctx->GetNodeProcess()->HasProcessExited()) @@ -673,7 +699,8 @@ void WINAPI CProtocolBridge::CreateNamedPipeConnection(DWORD error, DWORD bytesT 500, CNodeConstants::IISNODE_ERROR_PIPE_CONNECTION_BEFORE_PROCESS_TERMINATED, _T("Internal Server Error"), - hr ); + hr, + pfCompletionPosted); } else { @@ -693,13 +720,14 @@ void WINAPI CProtocolBridge::CreateNamedPipeConnection(DWORD error, DWORD bytesT 500, CNodeConstants::IISNODE_ERROR_CONFIGURE_PIPE_CONNECTION, _T("Internal Server Error"), - hr ); + hr, + pfCompletionPosted); } return; } -void CProtocolBridge::SendHttpRequestHeaders(CNodeHttpStoredContext* context) +void CProtocolBridge::SendHttpRequestHeaders(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted) { HRESULT hr; DWORD length; @@ -754,11 +782,15 @@ void CProtocolBridge::SendHttpRequestHeaders(CNodeHttpStoredContext* context) CheckError(CHttpProtocol::SerializeRequestHeaders(context, context->GetBufferRef(), context->GetBufferSizeRef(), &length)); context->SetNextProcessor(CProtocolBridge::SendHttpRequestHeadersCompleted); + + context->ReferenceNodeHttpStoredContext(); if (WriteFile(context->GetPipe(), context->GetBuffer(), length, NULL, context->InitializeOverlapped())) { // completed synchronously + context->DereferenceNodeHttpStoredContext(); + etw->Log(context->GetHttpContext(), L"iisnode initiated sending http request headers to the node.exe process and completed synchronously", WINEVENT_LEVEL_VERBOSE, &activityId); @@ -768,16 +800,18 @@ void CProtocolBridge::SendHttpRequestHeaders(CNodeHttpStoredContext* context) // - see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365683(v=vs.85).aspx // and http://msdn.microsoft.com/en-us/library/windows/desktop/aa365538(v=vs.85).aspx - CProtocolBridge::SendHttpRequestHeadersCompleted(S_OK, 0, context->GetOverlapped()); + CProtocolBridge::SendHttpRequestHeadersCompleted(S_OK, 0, context->GetOverlapped(), pfCompletionPosted); } else { hr = GetLastError(); if (ERROR_IO_PENDING == hr) { + // async WriteFile - will be dereferenced in AsyncManager::Worker } else { + context->DereferenceNodeHttpStoredContext(); // error if (context->GetIsConnectionFromPool()) @@ -786,7 +820,7 @@ void CProtocolBridge::SendHttpRequestHeaders(CNodeHttpStoredContext* context) // try to create a brand new connection instead context->SetConnectionRetryCount(1); - CProtocolBridge::CreateNamedPipeConnection(S_OK, 0, context->GetOverlapped()); + CProtocolBridge::CreateNamedPipeConnection(S_OK, 0, context->GetOverlapped(), pfCompletionPosted); } else { @@ -798,7 +832,8 @@ void CProtocolBridge::SendHttpRequestHeaders(CNodeHttpStoredContext* context) 500, CNodeConstants::IISNODE_ERROR_FAILED_INIT_SEND_HTTP_HEADERS, _T("Internal Server Error"), - hr ); + hr , + pfCompletionPosted); } } } @@ -815,12 +850,13 @@ void CProtocolBridge::SendHttpRequestHeaders(CNodeHttpStoredContext* context) 500, CNodeConstants::IISNODE_ERROR_FAILED_SERIALIZE_HTTP_HEADERS, _T("Internal Server Error"), - hr ); + hr, + pfCompletionPosted); return; } -void WINAPI CProtocolBridge::SendHttpRequestHeadersCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped) +void WINAPI CProtocolBridge::SendHttpRequestHeadersCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted) { HRESULT hr; CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped); @@ -828,7 +864,7 @@ void WINAPI CProtocolBridge::SendHttpRequestHeadersCompleted(DWORD error, DWORD CheckError(error); ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(), L"iisnode finished sending http request headers to the node.exe process", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId()); - CProtocolBridge::ReadRequestBody(ctx); + CProtocolBridge::ReadRequestBody(ctx, pfCompletionPosted); return; Error: @@ -839,7 +875,7 @@ void WINAPI CProtocolBridge::SendHttpRequestHeadersCompleted(DWORD error, DWORD // try to create a brand new connection instead ctx->SetConnectionRetryCount(1); - CProtocolBridge::CreateNamedPipeConnection(S_OK, 0, ctx->GetOverlapped()); + CProtocolBridge::CreateNamedPipeConnection(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted); } else { @@ -849,22 +885,28 @@ void WINAPI CProtocolBridge::SendHttpRequestHeadersCompleted(DWORD error, DWORD 500, CNodeConstants::IISNODE_ERROR_FAILED_SEND_HTTP_HEADERS, _T("Internal Server Error"), - hr ); + hr, + pfCompletionPosted); } return; } -void CProtocolBridge::ReadRequestBody(CNodeHttpStoredContext* context) +void CProtocolBridge::ReadRequestBody(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted) { HRESULT hr; DWORD bytesReceived = 0; BOOL completionPending = FALSE; BOOL continueSynchronouslyNow = TRUE; + BOOL fReferenced = FALSE; if (0 < context->GetHttpContext()->GetRequest()->GetRemainingEntityBytes() || context->GetIsUpgrade()) { context->SetNextProcessor(CProtocolBridge::ReadRequestBodyCompleted); + + // if async, will be dereferenced in OnAsyncCompletion + context->ReferenceNodeHttpStoredContext(); + fReferenced = TRUE; if (context->GetIsChunked()) { @@ -877,6 +919,8 @@ void CProtocolBridge::ReadRequestBody(CNodeHttpStoredContext* context) if (!completionPending) { + context->DereferenceNodeHttpStoredContext(); + context->SetContinueSynchronously(TRUE); continueSynchronouslyNow = FALSE; context->SetBytesCompleted(bytesReceived); @@ -891,7 +935,7 @@ void CProtocolBridge::ReadRequestBody(CNodeHttpStoredContext* context) context->SetBytesCompleted(bytesReceived); if (continueSynchronouslyNow) { - CProtocolBridge::ReadRequestBodyCompleted(S_OK, 0, context->GetOverlapped()); + CProtocolBridge::ReadRequestBodyCompleted(S_OK, 0, context->GetOverlapped(), pfCompletionPosted); } } else @@ -910,17 +954,16 @@ void CProtocolBridge::ReadRequestBody(CNodeHttpStoredContext* context) if (context->GetIsUpgrade()) { - CProtocolBridge::FinalizeUpgradeResponse(context, S_OK); + CProtocolBridge::FinalizeUpgradeResponse(context, S_OK, pfCompletionPosted); } else if (context->GetIsChunked() && !context->GetIsLastChunk()) { // send the terminating zero-length chunk - - CProtocolBridge::ReadRequestBodyCompleted(S_OK, 0, context->GetOverlapped()); + CProtocolBridge::ReadRequestBodyCompleted(S_OK, 0, context->GetOverlapped(), pfCompletionPosted); } else { - CProtocolBridge::StartReadResponse(context); + CProtocolBridge::StartReadResponse(context, pfCompletionPosted); } } else @@ -930,7 +973,7 @@ void CProtocolBridge::ReadRequestBody(CNodeHttpStoredContext* context) if (context->GetIsUpgrade()) { - CProtocolBridge::FinalizeUpgradeResponse(context, HRESULT_FROM_WIN32(hr)); + CProtocolBridge::FinalizeUpgradeResponse(context, HRESULT_FROM_WIN32(hr), pfCompletionPosted); } else { @@ -938,14 +981,20 @@ void CProtocolBridge::ReadRequestBody(CNodeHttpStoredContext* context) 500, CNodeConstants::IISNODE_ERROR_FAILED_READ_REQ_BODY, _T("Internal Server Error"), - HRESULT_FROM_WIN32(hr) ); + HRESULT_FROM_WIN32(hr), + pfCompletionPosted); } } + if (fReferenced) + { + context->DereferenceNodeHttpStoredContext(); + } + return; } -void WINAPI CProtocolBridge::ReadRequestBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped) +void WINAPI CProtocolBridge::ReadRequestBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted) { CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped); @@ -953,7 +1002,7 @@ void WINAPI CProtocolBridge::ReadRequestBodyCompleted(DWORD error, DWORD bytesTr { ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(), L"iisnode read a chunk of http request body", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId()); - CProtocolBridge::SendRequestBody(ctx, bytesTransfered); + CProtocolBridge::SendRequestBody(ctx, bytesTransfered, pfCompletionPosted); } else if (ERROR_HANDLE_EOF == error || 0 == bytesTransfered) { @@ -962,17 +1011,17 @@ void WINAPI CProtocolBridge::ReadRequestBodyCompleted(DWORD error, DWORD bytesTr if (ctx->GetIsUpgrade()) { - CProtocolBridge::FinalizeUpgradeResponse(ctx, S_OK); + CProtocolBridge::FinalizeUpgradeResponse(ctx, S_OK, pfCompletionPosted); } else if (ctx->GetIsChunked() && !ctx->GetIsLastChunk()) { // send the zero-length last chunk to indicate the end of a chunked entity body - CProtocolBridge::SendRequestBody(ctx, 0); + CProtocolBridge::SendRequestBody(ctx, 0, pfCompletionPosted); } else { - CProtocolBridge::StartReadResponse(ctx); + CProtocolBridge::StartReadResponse(ctx, pfCompletionPosted); } } else @@ -982,7 +1031,7 @@ void WINAPI CProtocolBridge::ReadRequestBodyCompleted(DWORD error, DWORD bytesTr if (ctx->GetIsUpgrade()) { - CProtocolBridge::FinalizeUpgradeResponse(ctx, error); + CProtocolBridge::FinalizeUpgradeResponse(ctx, error, pfCompletionPosted); } else { @@ -990,12 +1039,13 @@ void WINAPI CProtocolBridge::ReadRequestBodyCompleted(DWORD error, DWORD bytesTr 500, CNodeConstants::IISNODE_ERROR_FAILED_READ_REQ_BODY_COMPLETED, _T("Internal Server Error"), - error ); + error, + pfCompletionPosted); } } } -void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chunkLength) +void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chunkLength, BOOL *pfCompletionPosted) { // capture ETW provider since after a successful call to WriteFile the context may be asynchronously deleted @@ -1058,10 +1108,15 @@ void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chu context->SetNextProcessor(CProtocolBridge::SendRequestBodyCompleted); + // will derefence in AsyncManager::Worker + context->ReferenceNodeHttpStoredContext(); + if (WriteFile(context->GetPipe(), (void*)buffer, length, NULL, context->InitializeOverlapped())) { // completed synchronously + context->DereferenceNodeHttpStoredContext(); + etw->Log(context->GetHttpContext(), L"iisnode initiated sending http request body chunk to the node.exe process and completed synchronously", WINEVENT_LEVEL_VERBOSE, &activityId); @@ -1071,7 +1126,7 @@ void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chu // - see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365683(v=vs.85).aspx // and http://msdn.microsoft.com/en-us/library/windows/desktop/aa365538(v=vs.85).aspx - CProtocolBridge::SendRequestBodyCompleted(S_OK, 0, context->GetOverlapped()); + CProtocolBridge::SendRequestBodyCompleted(S_OK, 0, context->GetOverlapped(), pfCompletionPosted); } else { @@ -1087,7 +1142,7 @@ void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chu } else if (ERROR_NO_DATA == hr) { - // Node.exe has closed the named pipe. This means it does not expect any more request data, but it does not mean there is no response. + context->DereferenceNodeHttpStoredContext(); // Node.exe has closed the named pipe. This means it does not expect any more request data, but it does not mean there is no response. // This may happen even for POST requests if the node.js application does not register event handlers for the 'data' or 'end' request events. // Ignore the write error and attempt to read the response instead (which might have been written by node.exe before the named pipe connection // was closed). This may also happen for WebSocket traffic. @@ -1098,16 +1153,17 @@ void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chu if (context->GetIsUpgrade()) { - CProtocolBridge::FinalizeUpgradeResponse(context, S_OK); + CProtocolBridge::FinalizeUpgradeResponse(context, S_OK, pfCompletionPosted); } else { - CProtocolBridge::StartReadResponse(context); + CProtocolBridge::StartReadResponse(context, pfCompletionPosted); } } else { // error + context->DereferenceNodeHttpStoredContext(); etw->Log(context->GetHttpContext(), L"iisnode failed to initiate sending http request body chunk to the node.exe process", WINEVENT_LEVEL_ERROR, @@ -1115,7 +1171,7 @@ void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chu if (context->GetIsUpgrade()) { - CProtocolBridge::FinalizeUpgradeResponse(context, hr); + CProtocolBridge::FinalizeUpgradeResponse(context, hr, pfCompletionPosted); } else { @@ -1123,7 +1179,8 @@ void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chu 500, CNodeConstants::IISNODE_ERROR_FAILED_INIT_SEND_REQ_BODY, _T("Internal Server Error"), - hr ); + hr, + pfCompletionPosted ); } } } @@ -1131,7 +1188,7 @@ void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chu return; } -void WINAPI CProtocolBridge::SendRequestBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped) +void WINAPI CProtocolBridge::SendRequestBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted) { CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped); @@ -1139,7 +1196,7 @@ void WINAPI CProtocolBridge::SendRequestBodyCompleted(DWORD error, DWORD bytesTr { ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(), L"iisnode finished sending http request body chunk to the node.exe process", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId()); - CProtocolBridge::ReadRequestBody(ctx); + CProtocolBridge::ReadRequestBody(ctx, pfCompletionPosted); } else { @@ -1148,7 +1205,7 @@ void WINAPI CProtocolBridge::SendRequestBodyCompleted(DWORD error, DWORD bytesTr if (ctx->GetIsUpgrade()) { - CProtocolBridge::FinalizeUpgradeResponse(ctx, error); + CProtocolBridge::FinalizeUpgradeResponse(ctx, error, pfCompletionPosted); } else { @@ -1156,19 +1213,20 @@ void WINAPI CProtocolBridge::SendRequestBodyCompleted(DWORD error, DWORD bytesTr 500, CNodeConstants::IISNODE_ERROR_FAILED_SEND_REQ_BODY, _T("Internal Server Error"), - error ); + error, + pfCompletionPosted); } } } -void CProtocolBridge::StartReadResponse(CNodeHttpStoredContext* context) +void CProtocolBridge::StartReadResponse(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted) { context->SetDataSize(0); context->SetParsingOffset(0); context->SetNextProcessor(CProtocolBridge::ProcessResponseStatusLine); context->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(context->GetHttpContext(), L"iisnode starting to read http response", WINEVENT_LEVEL_VERBOSE, context->GetActivityId()); - CProtocolBridge::ContinueReadResponse(context); + CProtocolBridge::ContinueReadResponse(context, pfCompletionPosted); } HRESULT CProtocolBridge::EnsureBuffer(CNodeHttpStoredContext* context) @@ -1222,7 +1280,7 @@ HRESULT CProtocolBridge::EnsureBuffer(CNodeHttpStoredContext* context) } -void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context) +void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted) { HRESULT hr; DWORD bytesRead = 0; @@ -1235,6 +1293,9 @@ void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context) CheckError(CProtocolBridge::EnsureBuffer(context)); + // will dereference in AsyncManager::Worker + context->ReferenceNodeHttpStoredContext(); + if (ReadFile( context->GetPipe(), (char*)context->GetBuffer() + context->GetDataSize(), @@ -1243,6 +1304,7 @@ void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context) context->InitializeOverlapped())) { // read completed synchronously + context->DereferenceNodeHttpStoredContext(); etw->Log(context->GetHttpContext(), L"iisnode initiated reading http response chunk and completed synchronously", WINEVENT_LEVEL_VERBOSE, @@ -1253,7 +1315,7 @@ void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context) // - see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365683(v=vs.85).aspx // and http://msdn.microsoft.com/en-us/library/windows/desktop/aa365538(v=vs.85).aspx - context->GetAsyncContext()->completionProcessor(S_OK, bytesRead, context->GetOverlapped()); + context->GetAsyncContext()->completionProcessor(S_OK, bytesRead, context->GetOverlapped(), pfCompletionPosted); } else if (ERROR_IO_PENDING == (hr = GetLastError())) { @@ -1265,13 +1327,15 @@ void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context) } else if (ERROR_BROKEN_PIPE == hr && context->GetCloseConnection()) { + context->DereferenceNodeHttpStoredContext(); // Termination of a connection indicates the end of the response body if Connection: close response header was present - CProtocolBridge::FinalizeResponse(context); + CProtocolBridge::FinalizeResponse(context, pfCompletionPosted); } else { // error + context->DereferenceNodeHttpStoredContext(); etw->Log(context->GetHttpContext(), L"iisnode failed to initialize reading of http response chunk", WINEVENT_LEVEL_ERROR, @@ -1281,7 +1345,8 @@ void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context) 500, CNodeConstants::IISNODE_ERROR_FAILED_INIT_READ_RESPONSE, _T("Internal Server Error"), - hr ); + hr, + pfCompletionPosted); } return; @@ -1294,12 +1359,13 @@ void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context) 500, CNodeConstants::IISNODE_ERROR_FAILED_ALLOC_MEM_READ_RESPONSE, _T("Internal Server Error"), - hr ); + hr, + pfCompletionPosted); return; } -void WINAPI CProtocolBridge::ProcessResponseStatusLine(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped) +void WINAPI CProtocolBridge::ProcessResponseStatusLine(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted) { HRESULT hr; CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped); @@ -1315,14 +1381,14 @@ void WINAPI CProtocolBridge::ProcessResponseStatusLine(DWORD error, DWORD bytesT L"iisnode finished processing http response status line", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId()); ctx->SetNextProcessor(CProtocolBridge::ProcessResponseHeaders); - CProtocolBridge::ProcessResponseHeaders(S_OK, 0, ctx->GetOverlapped()); + CProtocolBridge::ProcessResponseHeaders(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted); return; Error: if (ERROR_MORE_DATA == hr) { - CProtocolBridge::ContinueReadResponse(ctx); + CProtocolBridge::ContinueReadResponse(ctx, pfCompletionPosted); } else { @@ -1332,7 +1398,8 @@ void WINAPI CProtocolBridge::ProcessResponseStatusLine(DWORD error, DWORD bytesT 500, CNodeConstants::IISNODE_ERROR_FAILED_PROCESS_HTTP_STATUS_LINE, _T("Internal Server Error"), - hr ); + hr, + pfCompletionPosted); } return; @@ -1450,7 +1517,7 @@ HRESULT CProtocolBridge::AddDebugHeader(CNodeHttpStoredContext* context) return hr; } -void WINAPI CProtocolBridge::ProcessResponseHeaders(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped) +void WINAPI CProtocolBridge::ProcessResponseHeaders(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted) { HRESULT hr; CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped); @@ -1474,7 +1541,7 @@ void WINAPI CProtocolBridge::ProcessResponseHeaders(DWORD error, DWORD bytesTran ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(), L"iisnode determined the HTTP response does not have entity body", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId()); - CProtocolBridge::FinalizeResponse(ctx); + CProtocolBridge::FinalizeResponse(ctx, pfCompletionPosted); } else { @@ -1523,7 +1590,7 @@ void WINAPI CProtocolBridge::ProcessResponseHeaders(DWORD error, DWORD bytesTran ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(), L"iisnode finished processing http response headers", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId()); - ctx->GetAsyncContext()->completionProcessor(S_OK, 0, ctx->GetOverlapped()); + ctx->GetAsyncContext()->completionProcessor(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted); } return; @@ -1531,7 +1598,7 @@ void WINAPI CProtocolBridge::ProcessResponseHeaders(DWORD error, DWORD bytesTran if (ERROR_MORE_DATA == hr) { - CProtocolBridge::ContinueReadResponse(ctx); + CProtocolBridge::ContinueReadResponse(ctx, pfCompletionPosted); } else { @@ -1541,13 +1608,14 @@ void WINAPI CProtocolBridge::ProcessResponseHeaders(DWORD error, DWORD bytesTran 500, CNodeConstants::IISNODE_ERROR_FAILED_PROCESS_HTTP_HEADERS, _T("Internal Server Error"), - hr ); + hr, + pfCompletionPosted); } return; } -void WINAPI CProtocolBridge::ProcessChunkHeader(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped) +void WINAPI CProtocolBridge::ProcessChunkHeader(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted) { HRESULT hr; CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped); @@ -1564,7 +1632,8 @@ void WINAPI CProtocolBridge::ProcessChunkHeader(DWORD error, DWORD bytesTransfer L"iisnode finished processing http response body chunk header", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId()); ctx->SetNextProcessor(CProtocolBridge::ProcessResponseBody); - CProtocolBridge::ProcessResponseBody(S_OK, 0, ctx->GetOverlapped()); + + CProtocolBridge::ProcessResponseBody(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted); return; @@ -1572,7 +1641,7 @@ void WINAPI CProtocolBridge::ProcessChunkHeader(DWORD error, DWORD bytesTransfer if (ERROR_MORE_DATA == hr) { - CProtocolBridge::ContinueReadResponse(ctx); + CProtocolBridge::ContinueReadResponse(ctx, pfCompletionPosted); } else { @@ -1582,13 +1651,14 @@ void WINAPI CProtocolBridge::ProcessChunkHeader(DWORD error, DWORD bytesTransfer 500, CNodeConstants::IISNODE_ERROR_FAILED_PROCESS_RESPONSE_BODY_CHUNK_HEADER, _T("Internal Server Error"), - hr ); + hr, + pfCompletionPosted); } return; } -void CProtocolBridge::EnsureRequestPumpStarted(CNodeHttpStoredContext* context) +void CProtocolBridge::EnsureRequestPumpStarted(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted) { if (context->GetOpaqueFlagSet() && !context->GetRequestPumpStarted()) { @@ -1596,19 +1666,20 @@ void CProtocolBridge::EnsureRequestPumpStarted(CNodeHttpStoredContext* context) // only after the 101 Switching Protocols response had been sent. context->SetRequestPumpStarted(); - CProtocolBridge::ReadRequestBody(context->GetUpgradeContext()); + CProtocolBridge::ReadRequestBody(context->GetUpgradeContext(), pfCompletionPosted); ASYNC_CONTEXT* async = context->GetUpgradeContext()->GetAsyncContext(); async->RunSynchronousContinuations(); } } -void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped) +void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted) { HRESULT hr; CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped); HTTP_DATA_CHUNK* chunk; DWORD bytesSent; BOOL completionExpected; + BOOL fReferenced = FALSE; ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(), L"iisnode starting to process http response body", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId()); @@ -1646,6 +1717,10 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe ctx->SetNextProcessor(CProtocolBridge::SendResponseBodyCompleted); ctx->SetBytesCompleted(bytesToSend); + // will be dereferenced in OnAsyncCompletion + ctx->ReferenceNodeHttpStoredContext(); + fReferenced = TRUE; + CheckError(ctx->GetHttpContext()->GetResponse()->WriteEntityChunks( chunk, 1, @@ -1659,6 +1734,7 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe if (!completionExpected) { + ctx->DereferenceNodeHttpStoredContext(); ctx->SetContinueSynchronously(TRUE); } } @@ -1667,7 +1743,7 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe // process next chunk of the chunked encoding ctx->SetNextProcessor(CProtocolBridge::ProcessChunkHeader); - CProtocolBridge::ProcessChunkHeader(S_OK, 0, ctx->GetOverlapped()); + CProtocolBridge::ProcessChunkHeader(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted); } else { @@ -1680,7 +1756,7 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe { // read more body data - CProtocolBridge::ContinueReadResponse(ctx); + CProtocolBridge::ContinueReadResponse(ctx, pfCompletionPosted); } else { @@ -1688,11 +1764,11 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe if (ctx->GetIsUpgrade()) { - CProtocolBridge::FinalizeUpgradeResponse(ctx, S_OK); + CProtocolBridge::FinalizeUpgradeResponse(ctx, S_OK, pfCompletionPosted); } else { - CProtocolBridge::FinalizeResponse(ctx); + CProtocolBridge::FinalizeResponse(ctx, pfCompletionPosted); } } @@ -1703,13 +1779,13 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe { // Termination of a connection indicates the end of the upgraded request - CProtocolBridge::FinalizeUpgradeResponse(ctx, S_OK); + CProtocolBridge::FinalizeUpgradeResponse(ctx, S_OK, pfCompletionPosted); } else if (ERROR_BROKEN_PIPE == hr && ctx->GetCloseConnection()) { // Termination of a connection indicates the end of the response body if Connection: close response header was present - CProtocolBridge::FinalizeResponse(ctx); + CProtocolBridge::FinalizeResponse(ctx, pfCompletionPosted); } else { @@ -1718,7 +1794,7 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe if (ctx->GetIsUpgrade()) { - CProtocolBridge::FinalizeUpgradeResponse(ctx, hr); + CProtocolBridge::FinalizeUpgradeResponse(ctx, hr, pfCompletionPosted); } else { @@ -1726,19 +1802,26 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe 500, CNodeConstants::IISNODE_ERROR_FAILED_SEND_RESPONSE_BODY_CHUNK, _T("Internal Server Error"), - hr ); + hr, + pfCompletionPosted); } } + if (fReferenced) + { + ctx->DereferenceNodeHttpStoredContext(); + } + return; } -void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped) +void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted) { HRESULT hr; CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped); DWORD bytesSent; BOOL completionExpected = FALSE; + BOOL fReference = FALSE; CheckError(error); @@ -1754,7 +1837,7 @@ void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesT if (ctx->GetIsLastChunk() && ctx->GetChunkLength() == ctx->GetChunkTransmitted()) { - CProtocolBridge::FinalizeResponse(ctx); + CProtocolBridge::FinalizeResponse(ctx, pfCompletionPosted); } else { @@ -1765,12 +1848,18 @@ void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesT ctx->SetNextProcessor(CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush); ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(), L"iisnode initiated flushing http response body chunk", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId()); + ctx->ReferenceNodeHttpStoredContext(); + fReference = TRUE; ctx->GetHttpContext()->GetResponse()->Flush(TRUE, TRUE, &bytesSent, &completionExpected); } if (!completionExpected) { - CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(S_OK, 0, ctx->GetOverlapped()); + CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted); + if(fReference) + { + ctx->DereferenceNodeHttpStoredContext(); + } } } @@ -1782,7 +1871,7 @@ void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesT if (ctx->GetIsUpgrade()) { - CProtocolBridge::FinalizeUpgradeResponse(ctx, hr); + CProtocolBridge::FinalizeUpgradeResponse(ctx, hr, pfCompletionPosted); } else { @@ -1790,31 +1879,34 @@ void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesT 500, CNodeConstants::IISNODE_ERROR_FAILED_FLUSH_RESPONSE_BODY, _T("Internal Server Error"), - hr ); + hr, + pfCompletionPosted); } return; } -void WINAPI CProtocolBridge::ProcessUpgradeResponse(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped) +void WINAPI CProtocolBridge::ProcessUpgradeResponse(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted) { - BOOL completionExpected; + BOOL completionExpected = FALSE; DWORD bytesSent; CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped); ctx->SetNextProcessor(CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush); ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(), L"iisnode initiated flushing http upgrade response headers", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId()); + ctx->ReferenceNodeHttpStoredContext(); ctx->GetHttpContext()->GetResponse()->Flush(TRUE, TRUE, &bytesSent, &completionExpected); if (!completionExpected) { - CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(S_OK, 0, ctx->GetOverlapped()); + CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted); + ctx->DereferenceNodeHttpStoredContext(); } } -void WINAPI CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped) +void WINAPI CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted) { HRESULT hr; CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped); @@ -1822,11 +1914,11 @@ void WINAPI CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(DWORD CheckError(error); // Start reading the request bytes if the request was an accepted HTTP Upgrade - CProtocolBridge::EnsureRequestPumpStarted(ctx); + CProtocolBridge::EnsureRequestPumpStarted(ctx, pfCompletionPosted); // Continue on to reading the response body ctx->SetNextProcessor(CProtocolBridge::ProcessResponseBody); - CProtocolBridge::ProcessResponseBody(S_OK, 0, ctx->GetOverlapped()); + CProtocolBridge::ProcessResponseBody(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted); return; Error: @@ -1837,12 +1929,13 @@ void WINAPI CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(DWORD 500, CNodeConstants::IISNODE_ERROR_FAILED_FLUSH_RESPONSE_BODY_PARTIAL_FLUSH, _T("Internal Server Error"), - hr ); + hr, + pfCompletionPosted); return; } -void CProtocolBridge::FinalizeUpgradeResponse(CNodeHttpStoredContext* context, HRESULT hresult) +void CProtocolBridge::FinalizeUpgradeResponse(CNodeHttpStoredContext* context, HRESULT hresult, BOOL *pfCompletionPosted) { context->SetNextProcessor(NULL); context->SetHresult(hresult); @@ -1858,7 +1951,12 @@ void CProtocolBridge::FinalizeUpgradeResponse(CNodeHttpStoredContext* context, H CloseHandle(context->GetPipe()); context->SetPipe(INVALID_HANDLE_VALUE); context->GetHttpContext()->GetResponse()->SetNeedDisconnect(); + + // will dereference in OnAsyncCompletion + context->ReferenceNodeHttpStoredContext(); + context->GetHttpContext()->PostCompletion(0); + *pfCompletionPosted = TRUE; } else { @@ -1869,7 +1967,7 @@ void CProtocolBridge::FinalizeUpgradeResponse(CNodeHttpStoredContext* context, H } } -void CProtocolBridge::FinalizeResponse(CNodeHttpStoredContext* context) +void CProtocolBridge::FinalizeResponse(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted) { context->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(context->GetHttpContext(), @@ -1889,12 +1987,13 @@ void CProtocolBridge::FinalizeResponse(CNodeHttpStoredContext* context) context, RQ_NOTIFICATION_CONTINUE, S_OK, + pfCompletionPosted, context->GetNodeApplication()->GetApplicationManager()->GetEventProvider(), L"iisnode posts completion from FinalizeResponse", WINEVENT_LEVEL_VERBOSE); } -HRESULT CProtocolBridge::FinalizeResponseCore(CNodeHttpStoredContext* context, REQUEST_NOTIFICATION_STATUS status, HRESULT error, CNodeEventProvider* log, PCWSTR etw, UCHAR level) +HRESULT CProtocolBridge::FinalizeResponseCore(CNodeHttpStoredContext* context, REQUEST_NOTIFICATION_STATUS status, HRESULT error, BOOL *pfCompletionPosted, CNodeEventProvider* log, PCWSTR etw, UCHAR level) { context->SetRequestNotificationStatus(status); context->SetNextProcessor(NULL); @@ -1910,8 +2009,12 @@ HRESULT CProtocolBridge::FinalizeResponseCore(CNodeHttpStoredContext* context, R if (0 == context->DecreasePendingAsyncOperationCount()) // decreases ref count increased in the ctor of CNodeApplication::Dispatch { + // will be dereferenced in OnAsyncCompletion + context->ReferenceNodeHttpStoredContext(); + log->Log(etw, level, context->GetActivityId()); context->GetHttpContext()->PostCompletion(0); + *pfCompletionPosted = TRUE; } return S_OK; @@ -1937,10 +2040,14 @@ HRESULT CProtocolBridge::SendDebugRedirect(CNodeHttpStoredContext* context, CNod response->SetStatus(301, "Moved Permanently"); CheckError(context->GetHttpContext()->GetResponse()->Redirect(path, FALSE, TRUE)); + + BOOL fCompletionPosted = FALSE; + CProtocolBridge::FinalizeResponseCore( context, RQ_NOTIFICATION_FINISH_REQUEST, S_OK, + &fCompletionPosted, log, L"iisnode redirected debugging request", WINEVENT_LEVEL_VERBOSE); diff --git a/src/iisnode/cprotocolbridge.h b/src/iisnode/cprotocolbridge.h index 5ce4ab80..054f5e59 100644 --- a/src/iisnode/cprotocolbridge.h +++ b/src/iisnode/cprotocolbridge.h @@ -11,48 +11,48 @@ class CProtocolBridge // utility static HRESULT PostponeProcessing(CNodeHttpStoredContext* context, DWORD dueTime); static HRESULT EnsureBuffer(CNodeHttpStoredContext* context); - static HRESULT FinalizeResponseCore(CNodeHttpStoredContext * context, REQUEST_NOTIFICATION_STATUS status, HRESULT error, CNodeEventProvider* log, PCWSTR etw, UCHAR level); + static HRESULT FinalizeResponseCore(CNodeHttpStoredContext * context, REQUEST_NOTIFICATION_STATUS status, HRESULT error, BOOL *pfCompletionPosted, CNodeEventProvider* log, PCWSTR etw, UCHAR level); static BOOL IsLocalCall(IHttpContext* ctx); static BOOL SendDevError(CNodeHttpStoredContext* context, USHORT status, USHORT subStatus, PCTSTR reason, HRESULT hresult, BOOL disableCache = FALSE); static HRESULT AddDebugHeader(CNodeHttpStoredContext* context); // processing stages - static void WINAPI ChildContextCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped); - static void WINAPI CreateNamedPipeConnection(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped); + static void WINAPI ChildContextCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted); + static void WINAPI CreateNamedPipeConnection(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted); - static void SendHttpRequestHeaders(CNodeHttpStoredContext* context); - static void WINAPI SendHttpRequestHeadersCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped); + static void SendHttpRequestHeaders(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted); + static void WINAPI SendHttpRequestHeadersCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted); - static void ReadRequestBody(CNodeHttpStoredContext* context); - static void WINAPI ReadRequestBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped); + static void ReadRequestBody(CNodeHttpStoredContext* context, BOOL * fCompletionPosted); + static void WINAPI ReadRequestBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted); - static void SendRequestBody(CNodeHttpStoredContext* context, DWORD chunkLength); - static void WINAPI SendRequestBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped); + static void SendRequestBody(CNodeHttpStoredContext* context, DWORD chunkLength, BOOL *pfCompletionPosted); + static void WINAPI SendRequestBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted); - static void StartReadResponse(CNodeHttpStoredContext* context); - static void ContinueReadResponse(CNodeHttpStoredContext* context); - static void WINAPI ProcessResponseStatusLine(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped); - static void WINAPI ProcessResponseHeaders(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped); + static void StartReadResponse(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted); + static void ContinueReadResponse(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted); + static void WINAPI ProcessResponseStatusLine(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted); + static void WINAPI ProcessResponseHeaders(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted); - static void WINAPI ProcessChunkHeader(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped); - static void WINAPI ProcessResponseBody(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped); + static void WINAPI ProcessChunkHeader(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted); + static void WINAPI ProcessResponseBody(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted); - static void WINAPI ProcessUpgradeResponse(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped); - static void WINAPI ContinueProcessResponseBodyAfterPartialFlush(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped); + static void WINAPI ProcessUpgradeResponse(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted); + static void WINAPI ContinueProcessResponseBodyAfterPartialFlush(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted); - static void EnsureRequestPumpStarted(CNodeHttpStoredContext* context); + static void EnsureRequestPumpStarted(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted); - static void FinalizeResponse(CNodeHttpStoredContext* context); - static void FinalizeUpgradeResponse(CNodeHttpStoredContext* context, HRESULT hresult); + static void FinalizeResponse(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted); + static void FinalizeUpgradeResponse(CNodeHttpStoredContext* context, HRESULT hresult, BOOL *pfCompletionPosted); public: - static void WINAPI SendResponseBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped); + static void WINAPI SendResponseBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted); static HRESULT InitiateRequest(CNodeHttpStoredContext* context); static BOOL SendIisnodeError(IHttpContext* httpCtx, HRESULT hr); static BOOL SendIisnodeError(CNodeHttpStoredContext* ctx, HRESULT hr); - static HRESULT SendEmptyResponse(CNodeHttpStoredContext* context, USHORT status, USHORT subStatus, PCTSTR reason, HRESULT hresult, BOOL disableCache = FALSE); + static HRESULT SendEmptyResponse(CNodeHttpStoredContext* context, USHORT status, USHORT subStatus, PCTSTR reason, HRESULT hresult, BOOL *pfCompletionPosted, BOOL disableCache = FALSE); static HRESULT SendSyncResponse(IHttpContext* httpCtx, USHORT status, PCTSTR reason, HRESULT hresult, BOOL disableCache, PCSTR htmlBody); static void SendEmptyResponse(IHttpContext* httpCtx, USHORT status, USHORT subStatus, PCTSTR reason, HRESULT hresult, BOOL disableCache = FALSE); static HRESULT SendDebugRedirect(CNodeHttpStoredContext* context, CNodeEventProvider* log); diff --git a/src/version.txt b/src/version.txt index d88290b2..18de5ee1 100644 --- a/src/version.txt +++ b/src/version.txt @@ -1 +1 @@ -0.2.21 +0.2.27