From 83879dfefab6b0bdd9f8dbe280a99a40c861c23e Mon Sep 17 00:00:00 2001 From: Ranjith Ramachandra Date: Thu, 21 Apr 2016 12:47:57 -0700 Subject: [PATCH 01/19] iisnode-debug.html --- iisnode-debug.html | 163 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 iisnode-debug.html diff --git a/iisnode-debug.html b/iisnode-debug.html new file mode 100644 index 00000000..88fe9cf3 --- /dev/null +++ b/iisnode-debug.html @@ -0,0 +1,163 @@ + + + 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 + + + + + \ No newline at end of file From 5a1dba67822abc03c6ed0ae5d5746988eb3170d9 Mon Sep 17 00:00:00 2001 From: Ranjith Ramachandra Date: Thu, 21 Apr 2016 12:51:50 -0700 Subject: [PATCH 02/19] Update iisnode-debug.html --- iisnode-debug.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iisnode-debug.html b/iisnode-debug.html index 88fe9cf3..577b4512 100644 --- a/iisnode-debug.html +++ b/iisnode-debug.html @@ -1,3 +1,4 @@ + iisnode-debug @@ -160,4 +161,4 @@

Actions

- \ No newline at end of file + From 4d633eee5441fccdab7294db5e7e517f8fbe70a0 Mon Sep 17 00:00:00 2001 From: Ranjith Ramachandra Date: Thu, 21 Apr 2016 12:52:30 -0700 Subject: [PATCH 03/19] Rename iisnode-debug.html to index.html --- iisnode-debug.html => index.html | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename iisnode-debug.html => index.html (100%) diff --git a/iisnode-debug.html b/index.html similarity index 100% rename from iisnode-debug.html rename to index.html From b3058d6697484cc806146fdd998348943ca600e4 Mon Sep 17 00:00:00 2001 From: Ranjith Mukkai Ramachandra Date: Thu, 9 Jun 2016 13:44:01 -0700 Subject: [PATCH 04/19] Fix env variables allocation issue --- src/iisnode/cmoduleconfiguration.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iisnode/cmoduleconfiguration.cpp b/src/iisnode/cmoduleconfiguration.cpp index 46c0f538..b2979cba 100644 --- a/src/iisnode/cmoduleconfiguration.cpp +++ b/src/iisnode/cmoduleconfiguration.cpp @@ -187,7 +187,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); From 8f9d43a61dd48fe0322eedf020aae457bfba3ea4 Mon Sep 17 00:00:00 2001 From: Ranjith Mukkai Ramachandra Date: Sun, 19 Feb 2017 11:18:16 -0800 Subject: [PATCH 05/19] fix w3wp crashes --- src/config/iisnode_dev_x64.xml | 3 +- src/config/iisnode_dev_x86_on_x64.xml | 3 +- src/config/iisnode_dev_x86_on_x86.xml | 3 +- src/config/iisnode_express_schema.xml | 3 +- src/config/iisnode_express_schema_x64.xml | 3 +- src/config/iisnode_schema.xml | 3 +- src/config/iisnode_schema_x64.xml | 3 +- src/iisnode/casyncmanager.cpp | 33 ++- src/iisnode/casyncmanager.h | 11 +- src/iisnode/cmoduleconfiguration.cpp | 22 +- src/iisnode/cmoduleconfiguration.h | 2 + src/iisnode/cnodeeventprovider.cpp | 4 + src/iisnode/cnodehttpmodule.cpp | 6 +- src/iisnode/cnodehttpstoredcontext.cpp | 6 +- src/iisnode/cnodehttpstoredcontext.h | 27 ++- src/iisnode/cnodeprocess.cpp | 12 + src/iisnode/cnodeprocessmanager.cpp | 101 ++++++++- src/iisnode/cnodeprocessmanager.h | 2 + src/iisnode/cprotocolbridge.cpp | 261 ++++++++++++++-------- src/iisnode/cprotocolbridge.h | 44 ++-- src/version.txt | 2 +- 21 files changed, 405 insertions(+), 149 deletions(-) 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 b2979cba..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; } @@ -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..0ca88d7f 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(); } @@ -266,6 +268,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..ea29394c 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,49 @@ 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. */ + { + pEnd++; + } + } + 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 +163,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 +246,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 +261,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..83bf6a23 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,7 @@ HRESULT CProtocolBridge::InitiateRequest(CNodeHttpStoredContext* context) BOOL mainDebuggerPage = FALSE; IHttpContext* child = NULL; BOOL completionExpected; + BOOL fCompletionPosted = FALSE; // determine what the target path of the request is @@ -536,13 +543,13 @@ HRESULT CProtocolBridge::InitiateRequest(CNodeHttpStoredContext* context) 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); } } else { context->SetNextProcessor(CProtocolBridge::CreateNamedPipeConnection); - CProtocolBridge::CreateNamedPipeConnection(S_OK, 0, context->InitializeOverlapped()); + CProtocolBridge::CreateNamedPipeConnection(S_OK, 0, context->InitializeOverlapped(), &fCompletionPosted); } return S_OK; @@ -558,7 +565,7 @@ HRESULT CProtocolBridge::InitiateRequest(CNodeHttpStoredContext* context) 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 +591,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 +599,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); @@ -635,7 +643,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 +657,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 +667,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 +682,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 +703,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 +765,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 +783,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 +803,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 +815,8 @@ void CProtocolBridge::SendHttpRequestHeaders(CNodeHttpStoredContext* context) 500, CNodeConstants::IISNODE_ERROR_FAILED_INIT_SEND_HTTP_HEADERS, _T("Internal Server Error"), - hr ); + hr , + pfCompletionPosted); } } } @@ -815,12 +833,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 +847,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 +858,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 +868,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 +902,8 @@ void CProtocolBridge::ReadRequestBody(CNodeHttpStoredContext* context) if (!completionPending) { + context->DereferenceNodeHttpStoredContext(); + context->SetContinueSynchronously(TRUE); continueSynchronouslyNow = FALSE; context->SetBytesCompleted(bytesReceived); @@ -891,7 +918,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 +937,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 +956,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 +964,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 +985,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 +994,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 +1014,7 @@ void WINAPI CProtocolBridge::ReadRequestBodyCompleted(DWORD error, DWORD bytesTr if (ctx->GetIsUpgrade()) { - CProtocolBridge::FinalizeUpgradeResponse(ctx, error); + CProtocolBridge::FinalizeUpgradeResponse(ctx, error, pfCompletionPosted); } else { @@ -990,12 +1022,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 +1091,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 +1109,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 +1125,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 +1136,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 +1154,7 @@ void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chu if (context->GetIsUpgrade()) { - CProtocolBridge::FinalizeUpgradeResponse(context, hr); + CProtocolBridge::FinalizeUpgradeResponse(context, hr, pfCompletionPosted); } else { @@ -1123,7 +1162,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 +1171,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 +1179,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 +1188,7 @@ void WINAPI CProtocolBridge::SendRequestBodyCompleted(DWORD error, DWORD bytesTr if (ctx->GetIsUpgrade()) { - CProtocolBridge::FinalizeUpgradeResponse(ctx, error); + CProtocolBridge::FinalizeUpgradeResponse(ctx, error, pfCompletionPosted); } else { @@ -1156,19 +1196,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 +1263,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 +1276,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 +1287,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 +1298,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 +1310,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 +1328,8 @@ void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context) 500, CNodeConstants::IISNODE_ERROR_FAILED_INIT_READ_RESPONSE, _T("Internal Server Error"), - hr ); + hr, + pfCompletionPosted); } return; @@ -1294,12 +1342,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 +1364,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 +1381,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 +1500,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 +1524,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 +1573,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 +1581,7 @@ void WINAPI CProtocolBridge::ProcessResponseHeaders(DWORD error, DWORD bytesTran if (ERROR_MORE_DATA == hr) { - CProtocolBridge::ContinueReadResponse(ctx); + CProtocolBridge::ContinueReadResponse(ctx, pfCompletionPosted); } else { @@ -1541,13 +1591,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 +1615,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 +1624,7 @@ void WINAPI CProtocolBridge::ProcessChunkHeader(DWORD error, DWORD bytesTransfer if (ERROR_MORE_DATA == hr) { - CProtocolBridge::ContinueReadResponse(ctx); + CProtocolBridge::ContinueReadResponse(ctx, pfCompletionPosted); } else { @@ -1582,13 +1634,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 +1649,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 +1700,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 +1717,7 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe if (!completionExpected) { + ctx->DereferenceNodeHttpStoredContext(); ctx->SetContinueSynchronously(TRUE); } } @@ -1667,7 +1726,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 +1739,7 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe { // read more body data - CProtocolBridge::ContinueReadResponse(ctx); + CProtocolBridge::ContinueReadResponse(ctx, pfCompletionPosted); } else { @@ -1688,11 +1747,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 +1762,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 +1777,7 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe if (ctx->GetIsUpgrade()) { - CProtocolBridge::FinalizeUpgradeResponse(ctx, hr); + CProtocolBridge::FinalizeUpgradeResponse(ctx, hr, pfCompletionPosted); } else { @@ -1726,14 +1785,20 @@ 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); @@ -1754,7 +1819,7 @@ void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesT if (ctx->GetIsLastChunk() && ctx->GetChunkLength() == ctx->GetChunkTransmitted()) { - CProtocolBridge::FinalizeResponse(ctx); + CProtocolBridge::FinalizeResponse(ctx, pfCompletionPosted); } else { @@ -1770,7 +1835,7 @@ void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesT if (!completionExpected) { - CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(S_OK, 0, ctx->GetOverlapped()); + CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted); } } @@ -1782,7 +1847,7 @@ void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesT if (ctx->GetIsUpgrade()) { - CProtocolBridge::FinalizeUpgradeResponse(ctx, hr); + CProtocolBridge::FinalizeUpgradeResponse(ctx, hr, pfCompletionPosted); } else { @@ -1790,13 +1855,14 @@ 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; DWORD bytesSent; @@ -1809,12 +1875,12 @@ void WINAPI CProtocolBridge::ProcessUpgradeResponse(DWORD error, DWORD bytesTran if (!completionExpected) { - CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(S_OK, 0, ctx->GetOverlapped()); + CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted); } } -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 +1888,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 +1903,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 +1925,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 +1941,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 +1961,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 +1983,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 +2014,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..e1bd0c6a 100644 --- a/src/version.txt +++ b/src/version.txt @@ -1 +1 @@ -0.2.21 +0.2.23 From 9b05ea4c6023c3138a52d67aa590f35bb6010e25 Mon Sep 17 00:00:00 2001 From: Ranjith Mukkai Ramachandra Date: Sun, 19 Feb 2017 12:19:15 -0800 Subject: [PATCH 06/19] node sticky sessions fix --- src/iisnode/cnodeprocessmanager.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/iisnode/cnodeprocessmanager.cpp b/src/iisnode/cnodeprocessmanager.cpp index ea29394c..4d02f263 100644 --- a/src/iisnode/cnodeprocessmanager.cpp +++ b/src/iisnode/cnodeprocessmanager.cpp @@ -135,11 +135,19 @@ int CNodeProcessManager::ExtractStickySessionsProcess( PCSTR pszCookie ) pEnd = pStart; while (*pEnd) /* Works because end-of-string and FALSE are identical. */ { + if((pEnd - pStart) >= sizeof(acProcess)) + { + break; + } pEnd++; } } - memcpy(acProcess, pStart, pEnd - pStart); // copy result - return atoi(acProcess); + + if((pEnd - pStart) < sizeof(acProcess)) + { + memcpy(acProcess, pStart, pEnd - pStart); // copy result + return atoi(acProcess); + } } } } From 1183694d376e874df60e9c7a20a264c31d80935f Mon Sep 17 00:00:00 2001 From: Ranjith Mukkai Ramachandra Date: Sun, 19 Feb 2017 12:20:06 -0800 Subject: [PATCH 07/19] Increase version to 0.2.24 --- src/version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.txt b/src/version.txt index e1bd0c6a..905c198e 100644 --- a/src/version.txt +++ b/src/version.txt @@ -1 +1 @@ -0.2.23 +0.2.24 From 2c5c85f3390fdc80120f1674bbbf26b46180164c Mon Sep 17 00:00:00 2001 From: Ranjith Mukkai Ramachandra Date: Sun, 19 Feb 2017 20:03:12 -0800 Subject: [PATCH 08/19] Fix w3wp crash when flushResponse=true --- src/iisnode/cprotocolbridge.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/iisnode/cprotocolbridge.cpp b/src/iisnode/cprotocolbridge.cpp index 83bf6a23..905199f9 100644 --- a/src/iisnode/cprotocolbridge.cpp +++ b/src/iisnode/cprotocolbridge.cpp @@ -1804,6 +1804,7 @@ void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesT CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped); DWORD bytesSent; BOOL completionExpected = FALSE; + BOOL fReference = FALSE; CheckError(error); @@ -1830,12 +1831,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(), pfCompletionPosted); + if(fReference) + { + ctx->DereferenceNodeHttpStoredContext(); + } } } @@ -1864,18 +1871,20 @@ void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesT 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(), pfCompletionPosted); + ctx->DereferenceNodeHttpStoredContext(); } } From da4e5e1f3843dec979f8c9d0a72851cf5f3487c0 Mon Sep 17 00:00:00 2001 From: Ranjith Mukkai Ramachandra Date: Sun, 19 Feb 2017 20:03:44 -0800 Subject: [PATCH 09/19] Increase version to 0.2.25 --- src/version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.txt b/src/version.txt index 905c198e..84a6a30d 100644 --- a/src/version.txt +++ b/src/version.txt @@ -1 +1 @@ -0.2.24 +0.2.25 From 1bf74c5f66aed1d2290f8333cb1bf5b8624e71b6 Mon Sep 17 00:00:00 2001 From: Ranjith Mukkai Ramachandra Date: Tue, 21 Feb 2017 14:53:30 -0800 Subject: [PATCH 10/19] Fix context RefCount when debugger is enabled --- src/iisnode/cprotocolbridge.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/iisnode/cprotocolbridge.cpp b/src/iisnode/cprotocolbridge.cpp index 905199f9..980d600e 100644 --- a/src/iisnode/cprotocolbridge.cpp +++ b/src/iisnode/cprotocolbridge.cpp @@ -539,11 +539,14 @@ HRESULT CProtocolBridge::InitiateRequest(CNodeHttpStoredContext* context) CheckError(child->GetRequest()->SetUrl(context->GetTargetUrl(), context->GetTargetUrlLength(), FALSE)); context->SetChildContext(child); context->SetNextProcessor(CProtocolBridge::ChildContextCompleted); - + + context->ReferenceNodeHttpStoredContext(); + CheckError(context->GetHttpContext()->ExecuteRequest(TRUE, child, 0, NULL, &completionExpected)); if (!completionExpected) { CProtocolBridge::ChildContextCompleted(S_OK, 0, context->GetOverlapped(), &fCompletionPosted); + context->DereferenceNodeHttpStoredContext(); } } else From c53c71e4b8ea7287785e2d4775120d14315627a0 Mon Sep 17 00:00:00 2001 From: Ranjith Mukkai Ramachandra Date: Tue, 21 Feb 2017 14:53:55 -0800 Subject: [PATCH 11/19] Fix context RefCount when debugger is enabled v0.2.26 --- src/version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.txt b/src/version.txt index 84a6a30d..7ada3a2d 100644 --- a/src/version.txt +++ b/src/version.txt @@ -1 +1 @@ -0.2.25 +0.2.26 From 6478fdea206547c4e1b0b0bb97186368d5a05f56 Mon Sep 17 00:00:00 2001 From: Ranjith Mukkai Ramachandra Date: Tue, 21 Feb 2017 14:56:02 -0800 Subject: [PATCH 12/19] Fix context RefCount when debugger is enabled v0.2.26 --- src/iisnode/cprotocolbridge.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/iisnode/cprotocolbridge.cpp b/src/iisnode/cprotocolbridge.cpp index 980d600e..ec7b2a75 100644 --- a/src/iisnode/cprotocolbridge.cpp +++ b/src/iisnode/cprotocolbridge.cpp @@ -481,6 +481,7 @@ HRESULT CProtocolBridge::InitiateRequest(CNodeHttpStoredContext* context) IHttpContext* child = NULL; BOOL completionExpected; BOOL fCompletionPosted = FALSE; + BOOL fReference = FALSE; // determine what the target path of the request is @@ -541,12 +542,14 @@ HRESULT CProtocolBridge::InitiateRequest(CNodeHttpStoredContext* context) 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(), &fCompletionPosted); context->DereferenceNodeHttpStoredContext(); + fReference = FALSE; } } else @@ -565,6 +568,11 @@ HRESULT CProtocolBridge::InitiateRequest(CNodeHttpStoredContext* context) context->SetChildContext(NULL); } + if(fReference) + { + context->DereferenceNodeHttpStoredContext(); + } + return hr; } From 9f49752cb13a7cd5b3d17eab6c6f1c9269ecc1d0 Mon Sep 17 00:00:00 2001 From: Michael Parque Date: Thu, 6 Apr 2017 08:41:27 -0400 Subject: [PATCH 13/19] Set fTrySkipCustomErrors flag to TRUE IHTTPResponse::SetStatus needs to be called with this flag set in order for iisnode to bypass IIS custom errors when errorMode="Auto" --- src/iisnode/chttpprotocol.cpp | 3 ++- src/iisnode/cnodehttpmodule.cpp | 2 +- src/iisnode/cprotocolbridge.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/iisnode/chttpprotocol.cpp b/src/iisnode/chttpprotocol.cpp index 4e70f92c..264da439 100644 --- a/src/iisnode/chttpprotocol.cpp +++ b/src/iisnode/chttpprotocol.cpp @@ -364,7 +364,8 @@ HRESULT CHttpProtocol::ParseResponseStatusLine(CNodeHttpStoredContext* context) data[newOffset] = 0; // zero-terminate the reason phrase to reuse it without copying IHttpResponse* response = context->GetHttpContext()->GetResponse(); - response->SetStatus(statusCode, data + offset, subStatusCode); + + response->SetStatus(statusCode, data + offset, subStatusCode, S_OK, NULL, TRUE); // adjust buffers diff --git a/src/iisnode/cnodehttpmodule.cpp b/src/iisnode/cnodehttpmodule.cpp index 41f8a2ef..109836a7 100644 --- a/src/iisnode/cnodehttpmodule.cpp +++ b/src/iisnode/cnodehttpmodule.cpp @@ -173,7 +173,7 @@ REQUEST_NOTIFICATION_STATUS CNodeHttpModule::OnExecuteRequestHandler( if (FAILED(hr)) { // Set the HTTP status. - pHttpResponse->SetStatus(500,"Server Error",0,hr); + pHttpResponse->SetStatus(500,"Server Error",0,hr,NULL,TRUE); } // End additional processing. diff --git a/src/iisnode/cprotocolbridge.cpp b/src/iisnode/cprotocolbridge.cpp index 726758ff..182b7daa 100644 --- a/src/iisnode/cprotocolbridge.cpp +++ b/src/iisnode/cprotocolbridge.cpp @@ -459,7 +459,7 @@ void CProtocolBridge::SendEmptyResponse(IHttpContext* httpCtx, USHORT status, US if (!httpCtx->GetResponseHeadersSent()) { httpCtx->GetResponse()->Clear(); - httpCtx->GetResponse()->SetStatus(status, reason, subStatus, hresult); + httpCtx->GetResponse()->SetStatus(status, reason, subStatus, hresult, NULL, TRUE); if (disableCache) { httpCtx->GetResponse()->SetHeader(HttpHeaderCacheControl, "no-cache", 8, TRUE); From b0a9bec6d5388741768cb4eaf7bdb3154e40a89f Mon Sep 17 00:00:00 2001 From: Michael Parque Date: Sat, 8 Apr 2017 18:20:07 -0400 Subject: [PATCH 14/19] Add opt-in skipIISCustomErrors switch to iisnode configuration. Users can opt into the fTrySkipCustomErrors flag behavior with skipIISCustomErrors="true". If this option is not specified, then the default behavior will match the previous behavior. --- src/config/iisnode_dev_x64.xml | 1 + src/config/iisnode_dev_x86_on_x64.xml | 1 + src/config/iisnode_dev_x86_on_x86.xml | 1 + src/config/iisnode_express_schema.xml | 1 + src/config/iisnode_express_schema_x64.xml | 1 + src/config/iisnode_schema.xml | 1 + src/config/iisnode_schema_x64.xml | 1 + src/iisnode/chttpprotocol.cpp | 11 +++++++++-- src/iisnode/cmoduleconfiguration.cpp | 10 ++++++++++ src/iisnode/cmoduleconfiguration.h | 2 ++ src/iisnode/cnodehttpmodule.cpp | 2 +- src/iisnode/cprotocolbridge.cpp | 10 +++++++++- 12 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/config/iisnode_dev_x64.xml b/src/config/iisnode_dev_x64.xml index cab240cf..a8625d4e 100644 --- a/src/config/iisnode_dev_x64.xml +++ b/src/config/iisnode_dev_x64.xml @@ -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..44ea9126 100644 --- a/src/config/iisnode_dev_x86_on_x64.xml +++ b/src/config/iisnode_dev_x86_on_x64.xml @@ -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..2e6f5e82 100644 --- a/src/config/iisnode_dev_x86_on_x86.xml +++ b/src/config/iisnode_dev_x86_on_x86.xml @@ -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..d26f65dc 100644 --- a/src/config/iisnode_express_schema.xml +++ b/src/config/iisnode_express_schema.xml @@ -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..b53f0570 100644 --- a/src/config/iisnode_express_schema_x64.xml +++ b/src/config/iisnode_express_schema_x64.xml @@ -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..836e0388 100644 --- a/src/config/iisnode_schema.xml +++ b/src/config/iisnode_schema.xml @@ -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..d4251f30 100644 --- a/src/config/iisnode_schema_x64.xml +++ b/src/config/iisnode_schema_x64.xml @@ -60,5 +60,6 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/ + " diff --git a/src/iisnode/chttpprotocol.cpp b/src/iisnode/chttpprotocol.cpp index 264da439..200dc701 100644 --- a/src/iisnode/chttpprotocol.cpp +++ b/src/iisnode/chttpprotocol.cpp @@ -364,8 +364,15 @@ HRESULT CHttpProtocol::ParseResponseStatusLine(CNodeHttpStoredContext* context) data[newOffset] = 0; // zero-terminate the reason phrase to reuse it without copying IHttpResponse* response = context->GetHttpContext()->GetResponse(); - - response->SetStatus(statusCode, data + offset, subStatusCode, S_OK, NULL, TRUE); + + if (CModuleConfiguration::GetSkipIISCustomErrors(context->GetHttpContext())) + { + response->SetStatus(statusCode, data + offset, subStatusCode, S_OK, NULL, TRUE); + } + else + { + response->SetStatus(statusCode, data + offset, subStatusCode); + } // adjust buffers diff --git a/src/iisnode/cmoduleconfiguration.cpp b/src/iisnode/cmoduleconfiguration.cpp index 46c0f538..c1315363 100644 --- a/src/iisnode/cmoduleconfiguration.cpp +++ b/src/iisnode/cmoduleconfiguration.cpp @@ -888,6 +888,10 @@ HRESULT CModuleConfiguration::ApplyConfigOverrideKeyValue(IHttpContext* context, { CheckError(GetDWORD(valueStart, &config->idlePageOutTimePeriod)); } + else if(0 == stricmp(keyStart, "skipIISCustomErrors")) + { + CheckError(GetBOOL(valueStart, &config->skipIISCustomErrors)); + } return S_OK; Error: @@ -1247,6 +1251,7 @@ HRESULT CModuleConfiguration::GetConfig(IHttpContext* context, CModuleConfigurat CheckError(GetString(section, L"nodeProcessCommandLine", &c->nodeProcessCommandLine)); CheckError(GetString(section, L"interceptor", &c->interceptor)); CheckError(GetDWORD(section, L"idlePageOutTimePeriod", &c->idlePageOutTimePeriod)); + CheckError(GetBOOL(section, L"skipIISCustomErrors", &c->skipIISCustomErrors, FALSE)); // debuggerPathSegment @@ -1488,6 +1493,11 @@ LPWSTR CModuleConfiguration::GetConfigOverrides(IHttpContext* ctx) GETCONFIG(configOverrides) } +BOOL CModuleConfiguration::GetSkipIISCustomErrors(IHttpContext* ctx) +{ + GETCONFIG(skipIISCustomErrors) +} + HRESULT CModuleConfiguration::GenerateDebuggerConfig(IHttpContext* context, CModuleConfiguration *config) { HRESULT hr = S_OK; diff --git a/src/iisnode/cmoduleconfiguration.h b/src/iisnode/cmoduleconfiguration.h index c951fca3..bf4615c9 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 skipIISCustomErrors; 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 GetSkipIISCustomErrors(IHttpContext* ctx); static HRESULT CreateNodeEnvironment(IHttpContext* ctx, DWORD debugPort, PCH namedPipe, PCH signalPipeName, PCH* env); diff --git a/src/iisnode/cnodehttpmodule.cpp b/src/iisnode/cnodehttpmodule.cpp index 109836a7..41f8a2ef 100644 --- a/src/iisnode/cnodehttpmodule.cpp +++ b/src/iisnode/cnodehttpmodule.cpp @@ -173,7 +173,7 @@ REQUEST_NOTIFICATION_STATUS CNodeHttpModule::OnExecuteRequestHandler( if (FAILED(hr)) { // Set the HTTP status. - pHttpResponse->SetStatus(500,"Server Error",0,hr,NULL,TRUE); + pHttpResponse->SetStatus(500,"Server Error",0,hr); } // End additional processing. diff --git a/src/iisnode/cprotocolbridge.cpp b/src/iisnode/cprotocolbridge.cpp index 182b7daa..8297f8ba 100644 --- a/src/iisnode/cprotocolbridge.cpp +++ b/src/iisnode/cprotocolbridge.cpp @@ -459,7 +459,15 @@ void CProtocolBridge::SendEmptyResponse(IHttpContext* httpCtx, USHORT status, US if (!httpCtx->GetResponseHeadersSent()) { httpCtx->GetResponse()->Clear(); - httpCtx->GetResponse()->SetStatus(status, reason, subStatus, hresult, NULL, TRUE); + if (CModuleConfiguration::GetSkipIISCustomErrors(httpCtx)) + { + httpCtx->GetResponse()->SetStatus(status, reason, subStatus, hresult, NULL, TRUE); + } + else + { + httpCtx->GetResponse()->SetStatus(status, reason, subStatus, hresult); + } + if (disableCache) { httpCtx->GetResponse()->SetHeader(HttpHeaderCacheControl, "no-cache", 8, TRUE); From 7ad8addaee7fd15045bf7e8cb4ee3256aa5a37a7 Mon Sep 17 00:00:00 2001 From: Michael Parque Date: Sun, 9 Apr 2017 01:09:50 -0400 Subject: [PATCH 15/19] Remove skipIISCustomErrors behavior from SendEmptyResponse Internal iisnode errors should probably not set fTrySkipCustomErrors since these are just empty status responses. Let IIS capture and replace these responses with more detailed messages depending on the custom error mode. --- src/iisnode/chttpprotocol.cpp | 5 +++++ src/iisnode/cprotocolbridge.cpp | 12 ++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/iisnode/chttpprotocol.cpp b/src/iisnode/chttpprotocol.cpp index 200dc701..22e67c18 100644 --- a/src/iisnode/chttpprotocol.cpp +++ b/src/iisnode/chttpprotocol.cpp @@ -367,6 +367,11 @@ HRESULT CHttpProtocol::ParseResponseStatusLine(CNodeHttpStoredContext* context) if (CModuleConfiguration::GetSkipIISCustomErrors(context->GetHttpContext())) { + // set fTrySkipCustomErrors so that error responses sent back from the node app through iisnode + // are passed through to the client instead of being intercepted by IIS when httpErrors existingResponse="Auto" + // this allows a mixed solution where custom error pages can be provided via IIS for errors that occur outside + // of iisnode's purview, while also allowing usually-more-helpful error responses from the node application + // to be passed through to the client rather than being intercepted by IIS. response->SetStatus(statusCode, data + offset, subStatusCode, S_OK, NULL, TRUE); } else diff --git a/src/iisnode/cprotocolbridge.cpp b/src/iisnode/cprotocolbridge.cpp index 8297f8ba..ba3c381a 100644 --- a/src/iisnode/cprotocolbridge.cpp +++ b/src/iisnode/cprotocolbridge.cpp @@ -459,14 +459,10 @@ void CProtocolBridge::SendEmptyResponse(IHttpContext* httpCtx, USHORT status, US if (!httpCtx->GetResponseHeadersSent()) { httpCtx->GetResponse()->Clear(); - if (CModuleConfiguration::GetSkipIISCustomErrors(httpCtx)) - { - httpCtx->GetResponse()->SetStatus(status, reason, subStatus, hresult, NULL, TRUE); - } - else - { - httpCtx->GetResponse()->SetStatus(status, reason, subStatus, hresult); - } + + // Internal iisnode errors should probably not set fTrySkipCustomErrors since these are just empty status responses. + // Let IIS capture and replace these responses with more detailed messages depending on the custom error mode. + httpCtx->GetResponse()->SetStatus(status, reason, subStatus, hresult); if (disableCache) { From 561b1f2ce92909c431af18fc3bd57f6feefad446 Mon Sep 17 00:00:00 2001 From: Michael Parque Date: Sun, 9 Apr 2017 01:10:30 -0400 Subject: [PATCH 16/19] Add documentation of skipIISCustomErrors to samples --- src/samples/configuration/iisnode.yml | 5 ++++- src/samples/configuration/readme.htm | 9 ++++++++- src/samples/configuration/web.config | 3 +++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/samples/configuration/iisnode.yml b/src/samples/configuration/iisnode.yml index 415b0bf9..76e1be42 100644 --- a/src/samples/configuration/iisnode.yml +++ b/src/samples/configuration/iisnode.yml @@ -141,4 +141,7 @@ enableXFF: false # HTTP request headers; for a list of IIS server variables available see # http://msdn.microsoft.com/en-us/library/ms524602(v=vs.90).aspx; for example "AUTH_USER,AUTH_TYPE" -promoteServerVars: \ No newline at end of file +promoteServerVars: + +# skipIISCustomErrors - controls whether iisnode will try to skip IIS custom error handling when existingResponse="Auto" +skipIISCustomErrors: false \ No newline at end of file diff --git a/src/samples/configuration/readme.htm b/src/samples/configuration/readme.htm index 7dacba7b..1408ec6e 100644 --- a/src/samples/configuration/readme.htm +++ b/src/samples/configuration/readme.htm @@ -145,6 +145,8 @@

maxRequestBufferSize: 8192 # increasing from the default # maxConcurrentRequestsPerProcess: 512 - commented out setting + * skipIISCustomErrors - controls whether iisnode will try to skip IIS >httpErrors< custom error handling when existingResponse="Auto" + --> <iisnode @@ -175,6 +177,7 @@

enableXFF="false" promoteServerVars="" configOverrides="node.conf" + skipIISCustomErrors="false" /> <!-- @@ -335,6 +338,10 @@

# HTTP request headers; for a list of IIS server variables available see # http://msdn.microsoft.com/en-us/library/ms524602(v=vs.90).aspx; for example "AUTH_USER,AUTH_TYPE" -promoteServerVars: +promoteServerVars: + +# skipIISCustomErrors - controls whether iisnode will try to skip IIS >httpErrors< custom error handling when existingResponse="Auto" +skipIISCustomErrors: false + diff --git a/src/samples/configuration/web.config b/src/samples/configuration/web.config index fed9441e..51fb003a 100644 --- a/src/samples/configuration/web.config +++ b/src/samples/configuration/web.config @@ -109,6 +109,8 @@ nodeProcessCountPerApplication: 2 maxRequestBufferSize: 8192 # increasing from the default # maxConcurrentRequestsPerProcess: 512 - commented out setting + + * skipIISCustomErrors - controls whether iisnode will try to skip IIS custom error handling when existingResponse="Auto" --> @@ -140,6 +142,7 @@ enableXFF="false" promoteServerVars="" configOverrides="iisnode.yml" + skipIISCustomErrors="false" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 648721479c40c48b9e75ef1c586d4ce5188989f8 Mon Sep 17 00:00:00 2001 From: Michael Parque Date: Sun, 9 Apr 2017 01:29:31 -0400 Subject: [PATCH 18/19] Fix indentation discrepancies. --- src/iisnode/cmoduleconfiguration.cpp | 52 ++++++++++++++-------------- src/iisnode/cmoduleconfiguration.h | 4 +-- src/iisnode/cprotocolbridge.cpp | 2 +- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/iisnode/cmoduleconfiguration.cpp b/src/iisnode/cmoduleconfiguration.cpp index c1315363..fd37e56b 100644 --- a/src/iisnode/cmoduleconfiguration.cpp +++ b/src/iisnode/cmoduleconfiguration.cpp @@ -888,10 +888,10 @@ HRESULT CModuleConfiguration::ApplyConfigOverrideKeyValue(IHttpContext* context, { CheckError(GetDWORD(valueStart, &config->idlePageOutTimePeriod)); } - else if(0 == stricmp(keyStart, "skipIISCustomErrors")) - { - CheckError(GetBOOL(valueStart, &config->skipIISCustomErrors)); - } + else if(0 == stricmp(keyStart, "skipIISCustomErrors")) + { + CheckError(GetBOOL(valueStart, &config->skipIISCustomErrors)); + } return S_OK; Error: @@ -1251,7 +1251,7 @@ HRESULT CModuleConfiguration::GetConfig(IHttpContext* context, CModuleConfigurat CheckError(GetString(section, L"nodeProcessCommandLine", &c->nodeProcessCommandLine)); CheckError(GetString(section, L"interceptor", &c->interceptor)); CheckError(GetDWORD(section, L"idlePageOutTimePeriod", &c->idlePageOutTimePeriod)); - CheckError(GetBOOL(section, L"skipIISCustomErrors", &c->skipIISCustomErrors, FALSE)); + CheckError(GetBOOL(section, L"skipIISCustomErrors", &c->skipIISCustomErrors, FALSE)); // debuggerPathSegment @@ -1495,7 +1495,7 @@ LPWSTR CModuleConfiguration::GetConfigOverrides(IHttpContext* ctx) BOOL CModuleConfiguration::GetSkipIISCustomErrors(IHttpContext* ctx) { - GETCONFIG(skipIISCustomErrors) + GETCONFIG(skipIISCustomErrors) } HRESULT CModuleConfiguration::GenerateDebuggerConfig(IHttpContext* context, CModuleConfiguration *config) @@ -1586,12 +1586,12 @@ HRESULT CModuleConfiguration::GetDebuggerFilesPathSegmentHelper( DWORD *pdwDebuggerFilesPathSegmentSize ) { - HRESULT hr = S_OK; - HCRYPTPROV hProv = 0; + HRESULT hr = S_OK; + HCRYPTPROV hProv = 0; HCRYPTHASH hHash = 0; CHAR rgbDigits[] = "0123456789abcdef"; - BYTE rgbHash[32]; // sha256 ==> 32 bytes. - DWORD cbHash = 0; + BYTE rgbHash[32]; // sha256 ==> 32 bytes. + DWORD cbHash = 0; CHAR shaHash[MAX_HASH_CHAR + 1]; // we will only use first MAX_HASH_CHAR bytes of the sha256 hash ==> 32 hex chars. DWORD dwSHALength = 0; CHAR *pInput = NULL; @@ -1609,27 +1609,27 @@ HRESULT CModuleConfiguration::GetDebuggerFilesPathSegmentHelper( ErrorIf(dwInputSize != WideCharToMultiByte(CP_ACP, 0, pszScriptPath, dwScriptPathLen, pInput, dwInputSize, NULL, NULL), E_FAIL); pInput[dwInputSize] = '\0'; - // Get handle to the crypto provider - ErrorIf(!CryptAcquireContext(&hProv, - NULL, - NULL, - PROV_RSA_AES, - CRYPT_VERIFYCONTEXT), HRESULT_FROM_WIN32(GetLastError())); - + // Get handle to the crypto provider + ErrorIf(!CryptAcquireContext(&hProv, + NULL, + NULL, + PROV_RSA_AES, + CRYPT_VERIFYCONTEXT), HRESULT_FROM_WIN32(GetLastError())); + ErrorIf(!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash), HRESULT_FROM_WIN32(GetLastError())); ErrorIf(!CryptHashData(hHash, (BYTE*) pInput, strnlen_s(pInput, dwInputSize), 0), HRESULT_FROM_WIN32(GetLastError())); // sha256 ==> 32 bytes. - cbHash = 32; - ErrorIf(!CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0), HRESULT_FROM_WIN32(GetLastError())); - - dwIndex = 0; - // convert first (MAX_HASH_CHAR / 2) bytes to hexadecimal form. - for (DWORD i = 0; i < (MAX_HASH_CHAR / 2); i++, dwIndex=dwIndex+2) - { - shaHash[dwIndex] = rgbDigits[rgbHash[i] >> 4]; - shaHash[dwIndex+1] = rgbDigits[rgbHash[i] & 0xf]; + cbHash = 32; + ErrorIf(!CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0), HRESULT_FROM_WIN32(GetLastError())); + + dwIndex = 0; + // convert first (MAX_HASH_CHAR / 2) bytes to hexadecimal form. + for (DWORD i = 0; i < (MAX_HASH_CHAR / 2); i++, dwIndex=dwIndex+2) + { + shaHash[dwIndex] = rgbDigits[rgbHash[i] >> 4]; + shaHash[dwIndex+1] = rgbDigits[rgbHash[i] & 0xf]; } shaHash[dwIndex] = '\0'; diff --git a/src/iisnode/cmoduleconfiguration.h b/src/iisnode/cmoduleconfiguration.h index bf4615c9..0c0db787 100644 --- a/src/iisnode/cmoduleconfiguration.h +++ b/src/iisnode/cmoduleconfiguration.h @@ -52,7 +52,7 @@ class CModuleConfiguration : public IHttpStoredContext static BOOL invalid; SRWLOCK srwlock; LPWSTR configOverrides; - BOOL skipIISCustomErrors; + BOOL skipIISCustomErrors; static IHttpServer* server; static HTTP_MODULE_ID moduleId; @@ -130,7 +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 GetSkipIISCustomErrors(IHttpContext* ctx); + static BOOL GetSkipIISCustomErrors(IHttpContext* ctx); static HRESULT CreateNodeEnvironment(IHttpContext* ctx, DWORD debugPort, PCH namedPipe, PCH signalPipeName, PCH* env); diff --git a/src/iisnode/cprotocolbridge.cpp b/src/iisnode/cprotocolbridge.cpp index ba3c381a..fd1ba32a 100644 --- a/src/iisnode/cprotocolbridge.cpp +++ b/src/iisnode/cprotocolbridge.cpp @@ -462,7 +462,7 @@ void CProtocolBridge::SendEmptyResponse(IHttpContext* httpCtx, USHORT status, US // Internal iisnode errors should probably not set fTrySkipCustomErrors since these are just empty status responses. // Let IIS capture and replace these responses with more detailed messages depending on the custom error mode. - httpCtx->GetResponse()->SetStatus(status, reason, subStatus, hresult); + httpCtx->GetResponse()->SetStatus(status, reason, subStatus, hresult); if (disableCache) { From d8f9f59f25073869ca54b30de943e9ca163eb70e Mon Sep 17 00:00:00 2001 From: Jenny Lawrance Date: Wed, 17 Jan 2018 14:18:54 -0800 Subject: [PATCH 19/19] iisnode crash fix --- src/iisnode/cnodehttpmodule.cpp | 11 +++++++++++ src/version.txt | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/iisnode/cnodehttpmodule.cpp b/src/iisnode/cnodehttpmodule.cpp index 0ca88d7f..a4876b68 100644 --- a/src/iisnode/cnodehttpmodule.cpp +++ b/src/iisnode/cnodehttpmodule.cpp @@ -239,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) { diff --git a/src/version.txt b/src/version.txt index 7ada3a2d..18de5ee1 100644 --- a/src/version.txt +++ b/src/version.txt @@ -1 +1 @@ -0.2.26 +0.2.27