diff --git a/README.md b/README.md
index 73fa4b5e..130404da 100644
--- a/README.md
+++ b/README.md
@@ -50,7 +50,6 @@ This can be a head-scratcher since IIS Express 8 gives you both 32-bit and 64-bi
**Howtos**
=======
- [the basics](http://tomasz.janczuk.org/2011/08/hosting-nodejs-applications-in-iis-on.html)
-- [the basics (Pусский перевод)](http://softdroid.net/hosting-nodejs-applications-ru)
- [**NEW: websockets**] (http://tomasz.janczuk.org/2012/11/how-to-use-websockets-with-nodejs-apps.html)
- [using with express framework](http://tomasz.janczuk.org/2011/08/hosting-express-nodejs-applications-in.html)
- [using with URL rewrite module](http://tomasz.janczuk.org/2011/08/using-url-rewriting-with-nodejs.html)
@@ -61,7 +60,7 @@ This can be a head-scratcher since IIS Express 8 gives you both 32-bit and 64-bi
- [using with MVC](http://weblogs.asp.net/jgalloway/archive/2011/10/26/using-node-js-in-an-asp-net-mvc-application-with-iisnode.aspx)
- [portuguese: node.js no windows: instalando o iisnode](http://vivina.com.br/nodejs-windows-parte-2)
- [integrated debugging](http://tomasz.janczuk.org/2011/11/debug-nodejs-applications-on-windows.html)
-- [**NEW: integrated debugging with node-inspector v0.7.3**](http://www.ranjithr.com/?p=98)
+- [Debugging via VSCode](https://blog.immatt.com/2018/10/09/iisnode-modern-debugging-via-vscode/)
- [pub/sub server using faye](http://weblogs.asp.net/cibrax/archive/2011/12/12/transform-your-iis-into-a-real-time-pub-sub-engine-with-faye-node.aspx)
- [appharbor uses iisnode](http://blog.appharbor.com/2012/01/19/announcing-node-js-support)
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..e138ec5d
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,41 @@
+
+
+## Security
+
+Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
+
+If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
+
+## Reporting Security Issues
+
+**Please do not report security vulnerabilities through public GitHub issues.**
+
+Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
+
+If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
+
+You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
+
+Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
+
+ * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
+ * Full paths of source file(s) related to the manifestation of the issue
+ * The location of the affected source code (tag/branch/commit or direct URL)
+ * Any special configuration required to reproduce the issue
+ * Step-by-step instructions to reproduce the issue
+ * Proof-of-concept or exploit code (if possible)
+ * Impact of the issue, including how an attacker might exploit the issue
+
+This information will help us triage your report more quickly.
+
+If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
+
+## Preferred Languages
+
+We prefer all communications to be in English.
+
+## Policy
+
+Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
+
+
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..577b4512
--- /dev/null
+++ b/index.html
@@ -0,0 +1,164 @@
+
+
+
+ iisnode-debug
+
+
+
+ HTTP response diagnostics for iisnode
+
+ This request
+
+
+
+ Processing time [ms] |
+ N/A |
+
+
+ Named pipe connection retry count |
+ N/A |
+
+
+ HRESULT |
+ N/A |
+
+
+ Server DNS name |
+ N/A |
+
+
+ w3wp.exe PID |
+ N/A |
+
+
+ node.exe PID |
+ N/A |
+
+
+
+ Memory
+
+
+
+ Counters
+
+
+
+ Active node.exe processes serving this application |
+ N/A |
+
+
+ Active HTTP requests in this application |
+ N/A |
+
+
+ Active HTTP requests in this node.exe process |
+ N/A |
+
+
+ Total node.js requests processed by w3wp.exe |
+ N/A |
+
+
+
+ Environment
+
+
+
+ Version of iisnode |
+ N/A |
+
+
+ Server full DNS name |
+ N/A |
+
+
+ Full node.exe path |
+ N/A |
+
+
+
+ Actions
+
+ Bugs, feedback, questions
+ iisnode project home page
+
+ Debug node.js applications hosted in IIS using iisnode
+
+
+ Use Event Tracing for Windows (ETW) to get more diagnostics information
+
+
+ Windows Azure node.js developer center
+
+
+
+
+
diff --git a/src/config/iisnode_dev_x64.xml b/src/config/iisnode_dev_x64.xml
index cab240cf..9d6a435c 100644
--- a/src/config/iisnode_dev_x64.xml
+++ b/src/config/iisnode_dev_x64.xml
@@ -32,7 +32,7 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
-
+
@@ -60,5 +60,6 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
+
diff --git a/src/config/iisnode_dev_x86_on_x64.xml b/src/config/iisnode_dev_x86_on_x64.xml
index aaa0bbc9..ab17fba8 100644
--- a/src/config/iisnode_dev_x86_on_x64.xml
+++ b/src/config/iisnode_dev_x86_on_x64.xml
@@ -32,7 +32,7 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
-
+
@@ -60,5 +60,6 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
+
diff --git a/src/config/iisnode_dev_x86_on_x86.xml b/src/config/iisnode_dev_x86_on_x86.xml
index 9606a25e..efab7f7d 100644
--- a/src/config/iisnode_dev_x86_on_x86.xml
+++ b/src/config/iisnode_dev_x86_on_x86.xml
@@ -32,7 +32,7 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
-
+
@@ -60,5 +60,6 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
+
diff --git a/src/config/iisnode_express_schema.xml b/src/config/iisnode_express_schema.xml
index 2ad9eddc..a6c95a85 100644
--- a/src/config/iisnode_express_schema.xml
+++ b/src/config/iisnode_express_schema.xml
@@ -32,7 +32,7 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
-
+
@@ -60,5 +60,6 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
+
diff --git a/src/config/iisnode_express_schema_x64.xml b/src/config/iisnode_express_schema_x64.xml
index ddf6368a..fb39746b 100644
--- a/src/config/iisnode_express_schema_x64.xml
+++ b/src/config/iisnode_express_schema_x64.xml
@@ -32,7 +32,7 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
-
+
@@ -60,5 +60,6 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
+
diff --git a/src/config/iisnode_schema.xml b/src/config/iisnode_schema.xml
index 4088c009..62e8004f 100644
--- a/src/config/iisnode_schema.xml
+++ b/src/config/iisnode_schema.xml
@@ -32,7 +32,7 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
-
+
@@ -60,5 +60,6 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
+
diff --git a/src/config/iisnode_schema_x64.xml b/src/config/iisnode_schema_x64.xml
index 8d454eae..f1561969 100644
--- a/src/config/iisnode_schema_x64.xml
+++ b/src/config/iisnode_schema_x64.xml
@@ -32,7 +32,7 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
-
+
@@ -60,5 +60,6 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
+
diff --git a/src/iisnode/casyncmanager.cpp b/src/iisnode/casyncmanager.cpp
index e0235e8b..50786340 100644
--- a/src/iisnode/casyncmanager.cpp
+++ b/src/iisnode/casyncmanager.cpp
@@ -4,14 +4,15 @@ extern RtlNtStatusToDosError pRtlNtStatusToDosError;
void ASYNC_CONTEXT::RunSynchronousContinuations()
{
- while (this->continueSynchronously)
+ BOOL fCompletionPosted = FALSE;
+ while (!fCompletionPosted && this->continueSynchronously)
{
// The continueSynchronously is used to unwind the call stack
// to avoid stack overflow in case of a synchronous IO completions
this->continueSynchronously = FALSE;
DWORD bytesCompleteted = this->bytesCompleteted;
this->bytesCompleteted = 0;
- this->completionProcessor(S_OK, bytesCompleteted, (LPOVERLAPPED)this);
+ this->completionProcessor(S_OK, bytesCompleteted, (LPOVERLAPPED)this, &fCompletionPosted);
}
}
@@ -171,19 +172,31 @@ unsigned int WINAPI CAsyncManager::Worker(void* arg)
{
OVERLAPPED_ENTRY* entry = entries;
for (int i = 0; i < entriesRemoved; i++)
- {
+ {
+ BOOL fCompletionPosted = FALSE;
+
if (0L == entry->lpCompletionKey
&& NULL != (ctx = (ASYNC_CONTEXT*)entry->lpOverlapped)
&& NULL != ctx->completionProcessor) // regular IO completion - invoke custom processor
{
+
error = (entry->lpOverlapped->Internal == STATUS_SUCCESS) ? ERROR_SUCCESS
: pRtlNtStatusToDosError(entry->lpOverlapped->Internal);
ctx = (ASYNC_CONTEXT*)entry->lpOverlapped;
+
ctx->completionProcessor(
(0 == entry->dwNumberOfBytesTransferred && ERROR_SUCCESS == error) ? ERROR_NO_DATA : error,
entry->dwNumberOfBytesTransferred,
- (LPOVERLAPPED)ctx);
- ctx->RunSynchronousContinuations();
+ (LPOVERLAPPED)ctx,
+ &fCompletionPosted);
+
+ if(!fCompletionPosted)
+ {
+ ctx->RunSynchronousContinuations();
+ }
+
+ CNodeHttpStoredContext* storedCtx = (CNodeHttpStoredContext*)ctx->data;
+ storedCtx->DereferenceNodeHttpStoredContext();
}
else if (-1L == entry->lpCompletionKey) // shutdown initiated from Terminate
{
@@ -194,8 +207,14 @@ unsigned int WINAPI CAsyncManager::Worker(void* arg)
if (-2L == entry->lpCompletionKey) // completion of an alertable wait state timer initialized from OnTimer
{
ctx = (ASYNC_CONTEXT*)entry->lpOverlapped;
- ctx->completionProcessor(S_OK, 0, (LPOVERLAPPED)ctx);
- ctx->RunSynchronousContinuations();
+ ctx->completionProcessor(S_OK, 0, (LPOVERLAPPED)ctx, &fCompletionPosted);
+ if(!fCompletionPosted)
+ {
+ ctx->RunSynchronousContinuations();
+ }
+
+ CNodeHttpStoredContext* storedCtx = (CNodeHttpStoredContext*)ctx->data;
+ storedCtx->DereferenceNodeHttpStoredContext();
}
else if (-3L == entry->lpCompletionKey) // continuation initiated form PostContinuation
{
diff --git a/src/iisnode/casyncmanager.h b/src/iisnode/casyncmanager.h
index d3b5b044..be4a3ce5 100644
--- a/src/iisnode/casyncmanager.h
+++ b/src/iisnode/casyncmanager.h
@@ -1,9 +1,18 @@
#ifndef __CASYNCMANAGER_H__
#define __CASYNCMANAGER_H__
+typedef
+VOID
+(WINAPI *LPOVERLAPPED_COMPLETION_ROUTINE_IISNODE)(
+ _In_ DWORD dwErrorCode,
+ _In_ DWORD dwNumberOfBytesTransfered,
+ _Inout_ LPOVERLAPPED lpOverlapped,
+ _Inout_ BOOL * fCompletionPosted
+ );
+
typedef struct {
OVERLAPPED overlapped; // this member must be first in the struct
- LPOVERLAPPED_COMPLETION_ROUTINE completionProcessor;
+ LPOVERLAPPED_COMPLETION_ROUTINE_IISNODE completionProcessor;
BOOL continueSynchronously;
void* data;
HANDLE timer;
diff --git a/src/iisnode/cmoduleconfiguration.cpp b/src/iisnode/cmoduleconfiguration.cpp
index 46c0f538..3562ddd8 100644
--- a/src/iisnode/cmoduleconfiguration.cpp
+++ b/src/iisnode/cmoduleconfiguration.cpp
@@ -87,15 +87,21 @@ CModuleConfiguration::~CModuleConfiguration()
this->debuggerPathSegment = NULL;
}
+ if( NULL != this->debugPortRange )
+ {
+ delete [] this->debugPortRange;
+ this->debugPortRange = NULL;
+ }
+
if (NULL != this->node_env)
{
- delete this->node_env;
+ delete [] this->node_env;
this->node_env = NULL;
}
if (NULL != this->watchedFiles)
{
- delete this->watchedFiles;
+ delete [] this->watchedFiles;
this->watchedFiles = NULL;
}
@@ -187,7 +193,7 @@ HRESULT CModuleConfiguration::CreateNodeEnvironment(IHttpContext* ctx, DWORD deb
// allocate memory for new environment variables
- tmpSize = 32767 - environmentSize;
+ tmpSize = 65536; // hard coded for now, change this to auto allocate based on the values.
ErrorIf(NULL == (tmpIndex = tmpStart = new char[tmpSize]), ERROR_NOT_ENOUGH_MEMORY);
RtlZeroMemory(tmpIndex, tmpSize);
@@ -814,6 +820,10 @@ HRESULT CModuleConfiguration::ApplyConfigOverrideKeyValue(IHttpContext* context,
{
CheckError(GetDWORD(valueStart, &config->maxLogFiles));
}
+ else if (0 == strcmpi(keyStart, "nodeProcessStickySessions"))
+ {
+ CheckError(GetBOOL(valueStart, &config->nodeProcessStickySessions));
+ }
else if (0 == strcmpi(keyStart, "loggingEnabled"))
{
CheckError(GetBOOL(valueStart, &config->loggingEnabled));
@@ -1228,6 +1238,7 @@ HRESULT CModuleConfiguration::GetConfig(IHttpContext* context, CModuleConfigurat
CheckError(GetDWORD(section, L"maxTotalLogFileSizeInKB", &c->maxTotalLogFileSizeInKB));
CheckError(GetDWORD(section, L"maxLogFileSizeInKB", &c->maxLogFileSizeInKB));
CheckError(GetDWORD(section, L"maxLogFiles", &c->maxLogFiles));
+ CheckError(GetBOOL(section, L"nodeProcessStickySessions", &c->nodeProcessStickySessions, FALSE));
CheckError(GetBOOL(section, L"loggingEnabled", &c->loggingEnabled, TRUE));
CheckError(GetBOOL(section, L"devErrorsEnabled", &c->devErrorsEnabled, TRUE));
CheckError(GetBOOL(section, L"flushResponse", &c->flushResponse, FALSE));
@@ -1236,7 +1247,7 @@ HRESULT CModuleConfiguration::GetConfig(IHttpContext* context, CModuleConfigurat
CheckError(GetString(section, L"debuggerExtensionDll", &c->debuggerExtensionDll));
CheckError(GetBOOL(section, L"debugHeaderEnabled", &c->debugHeaderEnabled, FALSE));
CheckError(GetBOOL(section, L"recycleSignalEnabled", &c->recycleSignalEnabled, FALSE));
- CheckError(GetString(section, L"debuggerVirtualDir", &c->debuggerVirtualDir));
+ CheckError(GetString(section, L"debuggerVirtualDir", &c->debuggerVirtualDir));
c->debuggerVirtualDirLength = wcslen(c->debuggerVirtualDir);
CheckError(GetString(section, L"node_env", &c->node_env));
CheckError(GetString(section, L"debuggerPortRange", &c->debugPortRange));
@@ -1433,6 +1444,11 @@ BOOL CModuleConfiguration::GetDebuggingEnabled(IHttpContext* ctx)
GETCONFIG(debuggingEnabled)
}
+BOOL CModuleConfiguration::GetProcessStickySessions(IHttpContext* ctx)
+{
+ GETCONFIG(nodeProcessStickySessions)
+}
+
PWSTR CModuleConfiguration::GetDebuggerExtensionDll(IHttpContext* ctx)
{
GETCONFIG(debuggerExtensionDll)
diff --git a/src/iisnode/cmoduleconfiguration.h b/src/iisnode/cmoduleconfiguration.h
index c951fca3..8151cd3b 100644
--- a/src/iisnode/cmoduleconfiguration.h
+++ b/src/iisnode/cmoduleconfiguration.h
@@ -52,6 +52,7 @@ class CModuleConfiguration : public IHttpStoredContext
static BOOL invalid;
SRWLOCK srwlock;
LPWSTR configOverrides;
+ BOOL nodeProcessStickySessions;
static IHttpServer* server;
static HTTP_MODULE_ID moduleId;
@@ -129,6 +130,7 @@ class CModuleConfiguration : public IHttpStoredContext
static BOOL GetEnableXFF(IHttpContext* ctx);
static HRESULT GetPromoteServerVars(IHttpContext* ctx, char*** vars, int* count);
static LPWSTR GetConfigOverrides(IHttpContext* ctx);
+ static BOOL GetProcessStickySessions(IHttpContext* ctx);
static HRESULT CreateNodeEnvironment(IHttpContext* ctx, DWORD debugPort, PCH namedPipe, PCH signalPipeName, PCH* env);
diff --git a/src/iisnode/cnodeeventprovider.cpp b/src/iisnode/cnodeeventprovider.cpp
index 0d5b56f3..97df5d6c 100644
--- a/src/iisnode/cnodeeventprovider.cpp
+++ b/src/iisnode/cnodeeventprovider.cpp
@@ -107,10 +107,14 @@ HRESULT CNodeEventProvider::Log(IHttpContext *context, PCWSTR message, UCHAR lev
}
}
+ /*
+ commented because there might be a race condition after calling PostCompletion and then using the IHttpContext object.
+ Uncomment this only after making sure there is no race condition. (IHttpContext cannot be used after calling PostCompletion)
if( IsEnabled( context->GetTraceContext(), level ) )
{
CheckError( RaiseEvent( context->GetTraceContext(), message, level, activityId ) );
}
+ */
return S_OK;
Error:
diff --git a/src/iisnode/cnodehttpmodule.cpp b/src/iisnode/cnodehttpmodule.cpp
index 41f8a2ef..a4876b68 100644
--- a/src/iisnode/cnodehttpmodule.cpp
+++ b/src/iisnode/cnodehttpmodule.cpp
@@ -219,7 +219,9 @@ REQUEST_NOTIFICATION_STATUS CNodeHttpModule::OnAsyncCompletion(
bytesCompleted = async->bytesCompleteted;
async->bytesCompleteted = 0;
}
- async->completionProcessor(pCompletionInfo->GetCompletionStatus(), bytesCompleted, ctx->GetOverlapped());
+ BOOL fCompletionPosted = FALSE;
+ async->completionProcessor(pCompletionInfo->GetCompletionStatus(), bytesCompleted, ctx->GetOverlapped(), &fCompletionPosted);
+
async->RunSynchronousContinuations();
}
@@ -237,6 +239,17 @@ REQUEST_NOTIFICATION_STATUS CNodeHttpModule::OnAsyncCompletion(
//
result = RQ_NOTIFICATION_CONTINUE;
}
+ else
+ {
+ if (0 == value) // decreases ref count set to 1 in the ctor of CNodeHttpStoredContext
+ {
+ result = ctx->GetRequestNotificationStatus();
+ }
+ else
+ {
+ result = RQ_NOTIFICATION_PENDING;
+ }
+ }
switch (result)
{
@@ -266,6 +279,8 @@ REQUEST_NOTIFICATION_STATUS CNodeHttpModule::OnAsyncCompletion(
break;
};
+ ctx->DereferenceNodeHttpStoredContext();
+
return result;
}
diff --git a/src/iisnode/cnodehttpstoredcontext.cpp b/src/iisnode/cnodehttpstoredcontext.cpp
index 80fc9fef..bb0e1eed 100644
--- a/src/iisnode/cnodehttpstoredcontext.cpp
+++ b/src/iisnode/cnodehttpstoredcontext.cpp
@@ -6,7 +6,7 @@ CNodeHttpStoredContext::CNodeHttpStoredContext(CNodeApplication* nodeApplication
requestNotificationStatus(RQ_NOTIFICATION_PENDING), connectionRetryCount(0), pendingAsyncOperationCount(1),
targetUrl(NULL), targetUrlLength(0), childContext(NULL), isConnectionFromPool(FALSE), expectResponseBody(TRUE),
closeConnection(FALSE), isUpgrade(FALSE), upgradeContext(NULL), opaqueFlagSet(FALSE), requestPumpStarted(FALSE),
- responseChunkBufferSize(0)
+ responseChunkBufferSize(0), m_cRefs(1)
{
IHttpTraceContext* tctx;
LPCGUID pguid;
@@ -75,7 +75,7 @@ CNodeApplication* CNodeHttpStoredContext::GetNodeApplication()
return this->nodeApplication;
}
-void CNodeHttpStoredContext::SetNextProcessor(LPOVERLAPPED_COMPLETION_ROUTINE processor)
+void CNodeHttpStoredContext::SetNextProcessor(LPOVERLAPPED_COMPLETION_ROUTINE_IISNODE processor)
{
this->asyncContext.completionProcessor = processor;
this->SetContinueSynchronously(FALSE);
@@ -95,7 +95,7 @@ LPOVERLAPPED CNodeHttpStoredContext::InitializeOverlapped()
void CNodeHttpStoredContext::CleanupStoredContext()
{
- delete this;
+ DereferenceNodeHttpStoredContext();
}
CNodeProcess* CNodeHttpStoredContext::GetNodeProcess()
diff --git a/src/iisnode/cnodehttpstoredcontext.h b/src/iisnode/cnodehttpstoredcontext.h
index ae116f0a..f38875d7 100644
--- a/src/iisnode/cnodehttpstoredcontext.h
+++ b/src/iisnode/cnodehttpstoredcontext.h
@@ -40,12 +40,35 @@ class CNodeHttpStoredContext : public IHttpStoredContext
HTTP_DATA_CHUNK responseChunk;
DWORD responseChunkBufferSize;
CNodeEventProvider* eventProvider;
+ ~CNodeHttpStoredContext();
+
+ mutable LONG m_cRefs;
public:
// Context is owned by the caller
CNodeHttpStoredContext(CNodeApplication* nodeApplication, CNodeEventProvider* eventProvider, IHttpContext* context);
- ~CNodeHttpStoredContext();
+
+ VOID
+ ReferenceNodeHttpStoredContext(
+ VOID
+ )
+ {
+ InterlockedIncrement(&m_cRefs);
+ }
+
+ VOID
+ DereferenceNodeHttpStoredContext(
+ VOID
+ )
+ {
+ _ASSERT(m_cRefs != 0);
+
+ if (InterlockedDecrement(&m_cRefs) == 0)
+ {
+ delete this;
+ }
+ }
IHttpContext* GetHttpContext();
CNodeApplication* GetNodeApplication();
@@ -78,7 +101,7 @@ class CNodeHttpStoredContext : public IHttpStoredContext
IHttpContext* GetChildContext();
DWORD GetBytesCompleted();
- void SetNextProcessor(LPOVERLAPPED_COMPLETION_ROUTINE processor);
+ void SetNextProcessor(LPOVERLAPPED_COMPLETION_ROUTINE_IISNODE processor);
void SetNodeProcess(CNodeProcess* process);
void SetPipe(HANDLE pipe);
void SetConnectionRetryCount(DWORD count);
diff --git a/src/iisnode/cnodeprocess.cpp b/src/iisnode/cnodeprocess.cpp
index 1ab6683d..f9e0ed06 100644
--- a/src/iisnode/cnodeprocess.cpp
+++ b/src/iisnode/cnodeprocess.cpp
@@ -20,6 +20,18 @@ CNodeProcess::~CNodeProcess()
this->process = NULL;
}
+ if (NULL != this->startupInfo.hStdOutput && INVALID_HANDLE_VALUE != this->startupInfo.hStdOutput)
+ {
+ CloseHandle(this->startupInfo.hStdOutput);
+ this->startupInfo.hStdOutput = INVALID_HANDLE_VALUE;
+ }
+
+ if (NULL != this->startupInfo.hStdError && INVALID_HANDLE_VALUE != this->startupInfo.hStdError)
+ {
+ CloseHandle(this->startupInfo.hStdError);
+ this->startupInfo.hStdError = INVALID_HANDLE_VALUE;
+ }
+
if (NULL != this->processWatcher)
{
// The following check prevents a dead-lock between process watcher thread calling OnProcessExited
diff --git a/src/iisnode/cnodeprocessmanager.cpp b/src/iisnode/cnodeprocessmanager.cpp
index cb6df5d6..4d02f263 100644
--- a/src/iisnode/cnodeprocessmanager.cpp
+++ b/src/iisnode/cnodeprocessmanager.cpp
@@ -2,7 +2,7 @@
CNodeProcessManager::CNodeProcessManager(CNodeApplication* application, IHttpContext* context)
: application(application), processes(NULL), currentProcess(0), isClosing(FALSE),
- refCount(1), gracefulShutdownProcessCount(0)
+ refCount(1), gracefulShutdownProcessCount(0), stickySessions(FALSE)
{
if (this->GetApplication()->IsDebugMode())
{
@@ -13,6 +13,8 @@ CNodeProcessManager::CNodeProcessManager(CNodeApplication* application, IHttpCon
this->processCount = CModuleConfiguration::GetNodeProcessCountPerApplication(context);
}
+ this->stickySessions = CModuleConfiguration::GetProcessStickySessions(context);
+
// cache event provider since the application can be disposed prior to CNodeProcessManager
this->eventProvider = this->GetApplication()->GetApplicationManager()->GetEventProvider();
@@ -107,10 +109,57 @@ HRESULT CNodeProcessManager::AddProcess(int ordinal, IHttpContext* context)
return hr;
}
+int CNodeProcessManager::ExtractStickySessionsProcess( PCSTR pszCookie )
+{
+ const CHAR* pszKey = "iisnode.session.cookie";
+ const CHAR* pszDivider = "=";
+ const CHAR* pszNext = ";";
+ CHAR acProcess[10]; // characters needed for MAXDWORD64
+ memset(acProcess, 0, sizeof(acProcess));
+
+ const CHAR* pStart = strstr(pszCookie, pszKey);
+ const CHAR* pEnd = NULL;
+
+ if( pStart != NULL )
+ {
+ pStart = strstr(pStart, pszDivider);
+ if( pStart != NULL )
+ {
+ // skip the '=';
+ pStart++;
+ if( pStart != NULL )
+ {
+ pEnd = strstr(pStart, pszNext);
+ if(!pEnd)
+ {
+ pEnd = pStart;
+ while (*pEnd) /* Works because end-of-string and FALSE are identical. */
+ {
+ if((pEnd - pStart) >= sizeof(acProcess))
+ {
+ break;
+ }
+ pEnd++;
+ }
+ }
+
+ if((pEnd - pStart) < sizeof(acProcess))
+ {
+ memcpy(acProcess, pStart, pEnd - pStart); // copy result
+ return atoi(acProcess);
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
HRESULT CNodeProcessManager::Dispatch(CNodeHttpStoredContext* request)
{
HRESULT hr;
unsigned int tmpProcess, processToUse;
+ int processInCookie = -1;
CheckNull(request);
@@ -122,23 +171,56 @@ HRESULT CNodeProcessManager::Dispatch(CNodeHttpStoredContext* request)
if (!this->isClosing)
{
+ //
+ // if sticky sessions is enabled, use the process specified in the cookie else
// employ a round robin routing logic to get a "ticket" to use a process with a specific ordinal number
-
- if (1 == this->processCount)
+ //
+
+ if(this->stickySessions) // sticky sessions
{
- processToUse = 0;
+ PCSTR pszCookieHeader = NULL;
+ pszCookieHeader = request->GetHttpContext()->GetRequest()->GetHeader(HttpHeaderCookie);
+ if(pszCookieHeader != NULL) // There might be a sticky session
+ {
+ processInCookie = ExtractStickySessionsProcess(pszCookieHeader);
+ }
}
- else
+
+ if( processInCookie < 0)
{
- do
+ //
+ // employ a round robin routing logic to get a "ticket" to use a process with a specific ordinal number
+ //
+
+ if (1 == this->processCount)
+ {
+ processToUse = 0;
+ }
+ else
{
- tmpProcess = this->currentProcess;
- processToUse = (tmpProcess + 1) % this->processCount;
- } while (tmpProcess != InterlockedCompareExchange(&this->currentProcess, processToUse, tmpProcess));
+ do
+ {
+ tmpProcess = this->currentProcess;
+ processToUse = (tmpProcess + 1) % this->processCount;
+ } while (tmpProcess != InterlockedCompareExchange(&this->currentProcess, processToUse, tmpProcess));
+ }
+ }
+ else
+ {
+ // ensure the cookie did not carry a value outside of the possible processes
+ processToUse = (unsigned int)processInCookie % this->processCount;
}
- // try dispatch to that process
+ if(this->stickySessions && (processInCookie < 0))
+ {
+ // Set cookie for sticky session with selected process
+ CHAR buffer [255];
+ int cCount = sprintf (buffer, "iisnode.session.cookie=%d", processToUse);
+ ErrorIf( cCount < 0, E_OUTOFMEMORY );
+ CheckError(request->GetHttpContext()->GetResponse()->SetHeader(HttpHeaderSetCookie, buffer, cCount, FALSE));
+ }
+ // try dispatch to that process
if (NULL != this->processes[processToUse])
{
CheckError(this->processes[processToUse]->AcceptRequest(request));
@@ -172,7 +254,9 @@ HRESULT CNodeProcessManager::Dispatch(CNodeHttpStoredContext* request)
if (request)
{
this->GetEventProvider()->Log(request->GetHttpContext(), L"iisnode failed to accept a request beacuse the application is recycling", WINEVENT_LEVEL_ERROR, request->GetActivityId());
- CProtocolBridge::SendEmptyResponse(request, 503, CNodeConstants::IISNODE_ERROR_FAILED_ACCEPT_REQUEST_APP_RECYCLE, _T("Service Unavailable"), IISNODE_ERROR_APPLICATION_IS_RECYCLING);
+
+ BOOL fCompletionPosted = FALSE;
+ CProtocolBridge::SendEmptyResponse(request, 503, CNodeConstants::IISNODE_ERROR_FAILED_ACCEPT_REQUEST_APP_RECYCLE, _T("Service Unavailable"), IISNODE_ERROR_APPLICATION_IS_RECYCLING, &fCompletionPosted);
}
this->DecRef(); // incremented at the beginning of this method
@@ -185,7 +269,8 @@ HRESULT CNodeProcessManager::Dispatch(CNodeHttpStoredContext* request)
if (!CProtocolBridge::SendIisnodeError(request, hr))
{
- CProtocolBridge::SendEmptyResponse(request, 503, CNodeConstants::IISNODE_ERROR_INIT_PROCESS_REQUEST, _T("Service Unavailable"), hr);
+ BOOL fCompletionPosted = FALSE;
+ CProtocolBridge::SendEmptyResponse(request, 503, CNodeConstants::IISNODE_ERROR_INIT_PROCESS_REQUEST, _T("Service Unavailable"), hr, &fCompletionPosted);
}
this->DecRef(); // incremented at the beginning of this method
diff --git a/src/iisnode/cnodeprocessmanager.h b/src/iisnode/cnodeprocessmanager.h
index 0c41737e..ac0d741f 100644
--- a/src/iisnode/cnodeprocessmanager.h
+++ b/src/iisnode/cnodeprocessmanager.h
@@ -22,6 +22,7 @@ class CNodeProcessManager
CNodeApplication* application;
CNodeProcess** processes;
DWORD processCount;
+ BOOL stickySessions;
unsigned int currentProcess;
SRWLOCK srwlock;
DWORD gracefulShutdownTimeout;
@@ -40,6 +41,7 @@ class CNodeProcessManager
CNodeProcessManager(CNodeApplication* application, IHttpContext* context);
~CNodeProcessManager();
+ int ExtractStickySessionsProcess( PCSTR pszCookie );
HRESULT EmptyWorkingSet();
CNodeApplication* GetApplication();
HRESULT Initialize(IHttpContext* context);
diff --git a/src/iisnode/cprotocolbridge.cpp b/src/iisnode/cprotocolbridge.cpp
index 726758ff..996af08f 100644
--- a/src/iisnode/cprotocolbridge.cpp
+++ b/src/iisnode/cprotocolbridge.cpp
@@ -7,6 +7,9 @@ HRESULT CProtocolBridge::PostponeProcessing(CNodeHttpStoredContext* context, DWO
delay.QuadPart = dueTime;
delay.QuadPart *= -10000; // convert from ms to 100ns units
+ // will be dereferenced in AsynManager::Worker
+ context->ReferenceNodeHttpStoredContext();
+
return async->SetTimer(context->GetAsyncContext(), &delay);
}
@@ -193,10 +196,12 @@ BOOL CProtocolBridge::SendIisnodeError(CNodeHttpStoredContext* ctx, HRESULT hr)
ctx->SetPipe(INVALID_HANDLE_VALUE);
}
+ BOOL fCompletionPosted = FALSE;
CProtocolBridge::FinalizeResponseCore(
ctx,
RQ_NOTIFICATION_FINISH_REQUEST,
hr,
+ &fCompletionPosted,
ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider(),
L"iisnode posts completion from SendIisnodeError",
WINEVENT_LEVEL_VERBOSE);
@@ -405,7 +410,7 @@ BOOL CProtocolBridge::SendDevError(CNodeHttpStoredContext* context,
return false;
}
-HRESULT CProtocolBridge::SendEmptyResponse(CNodeHttpStoredContext* context, USHORT status, USHORT subStatus, PCTSTR reason, HRESULT hresult, BOOL disableCache)
+HRESULT CProtocolBridge::SendEmptyResponse(CNodeHttpStoredContext* context, USHORT status, USHORT subStatus, PCTSTR reason, HRESULT hresult, BOOL *pfCompletionPosted, BOOL disableCache)
{
context->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(context->GetHttpContext(),
L"iisnode request processing failed for reasons unrecognized by iisnode", WINEVENT_LEVEL_VERBOSE, context->GetActivityId());
@@ -427,6 +432,7 @@ HRESULT CProtocolBridge::SendEmptyResponse(CNodeHttpStoredContext* context, USHO
context,
RQ_NOTIFICATION_FINISH_REQUEST,
hresult,
+ pfCompletionPosted,
context->GetNodeApplication()->GetApplicationManager()->GetEventProvider(),
L"iisnode posts completion from SendEmtpyResponse",
WINEVENT_LEVEL_VERBOSE);
@@ -474,6 +480,8 @@ HRESULT CProtocolBridge::InitiateRequest(CNodeHttpStoredContext* context)
BOOL mainDebuggerPage = FALSE;
IHttpContext* child = NULL;
BOOL completionExpected;
+ BOOL fCompletionPosted = FALSE;
+ BOOL fReference = FALSE;
// determine what the target path of the request is
@@ -532,17 +540,22 @@ HRESULT CProtocolBridge::InitiateRequest(CNodeHttpStoredContext* context)
CheckError(child->GetRequest()->SetUrl(context->GetTargetUrl(), context->GetTargetUrlLength(), FALSE));
context->SetChildContext(child);
context->SetNextProcessor(CProtocolBridge::ChildContextCompleted);
-
+
+ context->ReferenceNodeHttpStoredContext();
+ fReference = TRUE;
+
CheckError(context->GetHttpContext()->ExecuteRequest(TRUE, child, 0, NULL, &completionExpected));
if (!completionExpected)
{
- CProtocolBridge::ChildContextCompleted(S_OK, 0, context->GetOverlapped());
+ CProtocolBridge::ChildContextCompleted(S_OK, 0, context->GetOverlapped(), &fCompletionPosted);
+ context->DereferenceNodeHttpStoredContext();
+ fReference = FALSE;
}
}
else
{
context->SetNextProcessor(CProtocolBridge::CreateNamedPipeConnection);
- CProtocolBridge::CreateNamedPipeConnection(S_OK, 0, context->InitializeOverlapped());
+ CProtocolBridge::CreateNamedPipeConnection(S_OK, 0, context->InitializeOverlapped(), &fCompletionPosted);
}
return S_OK;
@@ -555,10 +568,15 @@ HRESULT CProtocolBridge::InitiateRequest(CNodeHttpStoredContext* context)
context->SetChildContext(NULL);
}
+ if(fReference)
+ {
+ context->DereferenceNodeHttpStoredContext();
+ }
+
return hr;
}
-void WINAPI CProtocolBridge::ChildContextCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped)
+void WINAPI CProtocolBridge::ChildContextCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted)
{
CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped);
@@ -584,6 +602,7 @@ void WINAPI CProtocolBridge::ChildContextCompleted(DWORD error, DWORD bytesTrans
ctx,
RQ_NOTIFICATION_CONTINUE,
error,
+ pfCompletionPosted,
ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider(),
L"iisnode posts completion from ChildContextCompleted",
WINEVENT_LEVEL_VERBOSE);
@@ -591,7 +610,7 @@ void WINAPI CProtocolBridge::ChildContextCompleted(DWORD error, DWORD bytesTrans
return;
}
-void WINAPI CProtocolBridge::CreateNamedPipeConnection(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped)
+void WINAPI CProtocolBridge::CreateNamedPipeConnection(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted)
{
HRESULT hr;
CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped);
@@ -607,6 +626,12 @@ void WINAPI CProtocolBridge::CreateNamedPipeConnection(DWORD error, DWORD bytesT
if (INVALID_HANDLE_VALUE == pipe)
{
+ // Clean up old pipe handle, otherwise there will be a handle leak
+ HANDLE oldPipe = ctx->GetPipe();
+ if (oldPipe != NULL && oldPipe != INVALID_HANDLE_VALUE) {
+ CloseHandle(oldPipe);
+ }
+
ErrorIf(INVALID_HANDLE_VALUE == (pipe = CreateFile(
ctx->GetNodeProcess()->GetNamedPipeName(),
GENERIC_READ | GENERIC_WRITE,
@@ -635,7 +660,7 @@ void WINAPI CProtocolBridge::CreateNamedPipeConnection(DWORD error, DWORD bytesT
ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(),
L"iisnode created named pipe connection to the node.exe process", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId());
- CProtocolBridge::SendHttpRequestHeaders(ctx);
+ CProtocolBridge::SendHttpRequestHeaders(ctx, pfCompletionPosted);
return;
@@ -649,7 +674,7 @@ void WINAPI CProtocolBridge::CreateNamedPipeConnection(DWORD error, DWORD bytesT
{
ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(),
L"iisnode was unable to establish named pipe connection to the node.exe process because the named pipe server is too busy", WINEVENT_LEVEL_ERROR, ctx->GetActivityId());
- CProtocolBridge::SendEmptyResponse(ctx, 503, CNodeConstants::IISNODE_ERROR_PIPE_CONNECTION_TOO_BUSY, _T("Service Unavailable"), hr);
+ CProtocolBridge::SendEmptyResponse(ctx, 503, CNodeConstants::IISNODE_ERROR_PIPE_CONNECTION_TOO_BUSY, _T("Service Unavailable"), hr, pfCompletionPosted);
}
else
{
@@ -659,7 +684,8 @@ void WINAPI CProtocolBridge::CreateNamedPipeConnection(DWORD error, DWORD bytesT
500,
CNodeConstants::IISNODE_ERROR_PIPE_CONNECTION,
_T("Internal Server Error"),
- hr );
+ hr,
+ pfCompletionPosted);
}
}
else if (ctx->GetNodeProcess()->HasProcessExited())
@@ -673,7 +699,8 @@ void WINAPI CProtocolBridge::CreateNamedPipeConnection(DWORD error, DWORD bytesT
500,
CNodeConstants::IISNODE_ERROR_PIPE_CONNECTION_BEFORE_PROCESS_TERMINATED,
_T("Internal Server Error"),
- hr );
+ hr,
+ pfCompletionPosted);
}
else
{
@@ -693,13 +720,14 @@ void WINAPI CProtocolBridge::CreateNamedPipeConnection(DWORD error, DWORD bytesT
500,
CNodeConstants::IISNODE_ERROR_CONFIGURE_PIPE_CONNECTION,
_T("Internal Server Error"),
- hr );
+ hr,
+ pfCompletionPosted);
}
return;
}
-void CProtocolBridge::SendHttpRequestHeaders(CNodeHttpStoredContext* context)
+void CProtocolBridge::SendHttpRequestHeaders(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted)
{
HRESULT hr;
DWORD length;
@@ -754,11 +782,15 @@ void CProtocolBridge::SendHttpRequestHeaders(CNodeHttpStoredContext* context)
CheckError(CHttpProtocol::SerializeRequestHeaders(context, context->GetBufferRef(), context->GetBufferSizeRef(), &length));
context->SetNextProcessor(CProtocolBridge::SendHttpRequestHeadersCompleted);
+
+ context->ReferenceNodeHttpStoredContext();
if (WriteFile(context->GetPipe(), context->GetBuffer(), length, NULL, context->InitializeOverlapped()))
{
// completed synchronously
+ context->DereferenceNodeHttpStoredContext();
+
etw->Log(context->GetHttpContext(), L"iisnode initiated sending http request headers to the node.exe process and completed synchronously",
WINEVENT_LEVEL_VERBOSE,
&activityId);
@@ -768,16 +800,18 @@ void CProtocolBridge::SendHttpRequestHeaders(CNodeHttpStoredContext* context)
// - see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365683(v=vs.85).aspx
// and http://msdn.microsoft.com/en-us/library/windows/desktop/aa365538(v=vs.85).aspx
- CProtocolBridge::SendHttpRequestHeadersCompleted(S_OK, 0, context->GetOverlapped());
+ CProtocolBridge::SendHttpRequestHeadersCompleted(S_OK, 0, context->GetOverlapped(), pfCompletionPosted);
}
else
{
hr = GetLastError();
if (ERROR_IO_PENDING == hr)
{
+ // async WriteFile - will be dereferenced in AsyncManager::Worker
}
else
{
+ context->DereferenceNodeHttpStoredContext();
// error
if (context->GetIsConnectionFromPool())
@@ -786,7 +820,7 @@ void CProtocolBridge::SendHttpRequestHeaders(CNodeHttpStoredContext* context)
// try to create a brand new connection instead
context->SetConnectionRetryCount(1);
- CProtocolBridge::CreateNamedPipeConnection(S_OK, 0, context->GetOverlapped());
+ CProtocolBridge::CreateNamedPipeConnection(S_OK, 0, context->GetOverlapped(), pfCompletionPosted);
}
else
{
@@ -798,7 +832,8 @@ void CProtocolBridge::SendHttpRequestHeaders(CNodeHttpStoredContext* context)
500,
CNodeConstants::IISNODE_ERROR_FAILED_INIT_SEND_HTTP_HEADERS,
_T("Internal Server Error"),
- hr );
+ hr ,
+ pfCompletionPosted);
}
}
}
@@ -815,12 +850,13 @@ void CProtocolBridge::SendHttpRequestHeaders(CNodeHttpStoredContext* context)
500,
CNodeConstants::IISNODE_ERROR_FAILED_SERIALIZE_HTTP_HEADERS,
_T("Internal Server Error"),
- hr );
+ hr,
+ pfCompletionPosted);
return;
}
-void WINAPI CProtocolBridge::SendHttpRequestHeadersCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped)
+void WINAPI CProtocolBridge::SendHttpRequestHeadersCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted)
{
HRESULT hr;
CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped);
@@ -828,7 +864,7 @@ void WINAPI CProtocolBridge::SendHttpRequestHeadersCompleted(DWORD error, DWORD
CheckError(error);
ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(),
L"iisnode finished sending http request headers to the node.exe process", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId());
- CProtocolBridge::ReadRequestBody(ctx);
+ CProtocolBridge::ReadRequestBody(ctx, pfCompletionPosted);
return;
Error:
@@ -839,7 +875,7 @@ void WINAPI CProtocolBridge::SendHttpRequestHeadersCompleted(DWORD error, DWORD
// try to create a brand new connection instead
ctx->SetConnectionRetryCount(1);
- CProtocolBridge::CreateNamedPipeConnection(S_OK, 0, ctx->GetOverlapped());
+ CProtocolBridge::CreateNamedPipeConnection(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted);
}
else
{
@@ -849,22 +885,28 @@ void WINAPI CProtocolBridge::SendHttpRequestHeadersCompleted(DWORD error, DWORD
500,
CNodeConstants::IISNODE_ERROR_FAILED_SEND_HTTP_HEADERS,
_T("Internal Server Error"),
- hr );
+ hr,
+ pfCompletionPosted);
}
return;
}
-void CProtocolBridge::ReadRequestBody(CNodeHttpStoredContext* context)
+void CProtocolBridge::ReadRequestBody(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted)
{
HRESULT hr;
DWORD bytesReceived = 0;
BOOL completionPending = FALSE;
BOOL continueSynchronouslyNow = TRUE;
+ BOOL fReferenced = FALSE;
if (0 < context->GetHttpContext()->GetRequest()->GetRemainingEntityBytes() || context->GetIsUpgrade())
{
context->SetNextProcessor(CProtocolBridge::ReadRequestBodyCompleted);
+
+ // if async, will be dereferenced in OnAsyncCompletion
+ context->ReferenceNodeHttpStoredContext();
+ fReferenced = TRUE;
if (context->GetIsChunked())
{
@@ -877,6 +919,8 @@ void CProtocolBridge::ReadRequestBody(CNodeHttpStoredContext* context)
if (!completionPending)
{
+ context->DereferenceNodeHttpStoredContext();
+
context->SetContinueSynchronously(TRUE);
continueSynchronouslyNow = FALSE;
context->SetBytesCompleted(bytesReceived);
@@ -891,7 +935,7 @@ void CProtocolBridge::ReadRequestBody(CNodeHttpStoredContext* context)
context->SetBytesCompleted(bytesReceived);
if (continueSynchronouslyNow)
{
- CProtocolBridge::ReadRequestBodyCompleted(S_OK, 0, context->GetOverlapped());
+ CProtocolBridge::ReadRequestBodyCompleted(S_OK, 0, context->GetOverlapped(), pfCompletionPosted);
}
}
else
@@ -910,17 +954,16 @@ void CProtocolBridge::ReadRequestBody(CNodeHttpStoredContext* context)
if (context->GetIsUpgrade())
{
- CProtocolBridge::FinalizeUpgradeResponse(context, S_OK);
+ CProtocolBridge::FinalizeUpgradeResponse(context, S_OK, pfCompletionPosted);
}
else if (context->GetIsChunked() && !context->GetIsLastChunk())
{
// send the terminating zero-length chunk
-
- CProtocolBridge::ReadRequestBodyCompleted(S_OK, 0, context->GetOverlapped());
+ CProtocolBridge::ReadRequestBodyCompleted(S_OK, 0, context->GetOverlapped(), pfCompletionPosted);
}
else
{
- CProtocolBridge::StartReadResponse(context);
+ CProtocolBridge::StartReadResponse(context, pfCompletionPosted);
}
}
else
@@ -930,7 +973,7 @@ void CProtocolBridge::ReadRequestBody(CNodeHttpStoredContext* context)
if (context->GetIsUpgrade())
{
- CProtocolBridge::FinalizeUpgradeResponse(context, HRESULT_FROM_WIN32(hr));
+ CProtocolBridge::FinalizeUpgradeResponse(context, HRESULT_FROM_WIN32(hr), pfCompletionPosted);
}
else
{
@@ -938,14 +981,20 @@ void CProtocolBridge::ReadRequestBody(CNodeHttpStoredContext* context)
500,
CNodeConstants::IISNODE_ERROR_FAILED_READ_REQ_BODY,
_T("Internal Server Error"),
- HRESULT_FROM_WIN32(hr) );
+ HRESULT_FROM_WIN32(hr),
+ pfCompletionPosted);
}
}
+ if (fReferenced)
+ {
+ context->DereferenceNodeHttpStoredContext();
+ }
+
return;
}
-void WINAPI CProtocolBridge::ReadRequestBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped)
+void WINAPI CProtocolBridge::ReadRequestBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted)
{
CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped);
@@ -953,7 +1002,7 @@ void WINAPI CProtocolBridge::ReadRequestBodyCompleted(DWORD error, DWORD bytesTr
{
ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(),
L"iisnode read a chunk of http request body", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId());
- CProtocolBridge::SendRequestBody(ctx, bytesTransfered);
+ CProtocolBridge::SendRequestBody(ctx, bytesTransfered, pfCompletionPosted);
}
else if (ERROR_HANDLE_EOF == error || 0 == bytesTransfered)
{
@@ -962,17 +1011,17 @@ void WINAPI CProtocolBridge::ReadRequestBodyCompleted(DWORD error, DWORD bytesTr
if (ctx->GetIsUpgrade())
{
- CProtocolBridge::FinalizeUpgradeResponse(ctx, S_OK);
+ CProtocolBridge::FinalizeUpgradeResponse(ctx, S_OK, pfCompletionPosted);
}
else if (ctx->GetIsChunked() && !ctx->GetIsLastChunk())
{
// send the zero-length last chunk to indicate the end of a chunked entity body
- CProtocolBridge::SendRequestBody(ctx, 0);
+ CProtocolBridge::SendRequestBody(ctx, 0, pfCompletionPosted);
}
else
{
- CProtocolBridge::StartReadResponse(ctx);
+ CProtocolBridge::StartReadResponse(ctx, pfCompletionPosted);
}
}
else
@@ -982,7 +1031,7 @@ void WINAPI CProtocolBridge::ReadRequestBodyCompleted(DWORD error, DWORD bytesTr
if (ctx->GetIsUpgrade())
{
- CProtocolBridge::FinalizeUpgradeResponse(ctx, error);
+ CProtocolBridge::FinalizeUpgradeResponse(ctx, error, pfCompletionPosted);
}
else
{
@@ -990,12 +1039,13 @@ void WINAPI CProtocolBridge::ReadRequestBodyCompleted(DWORD error, DWORD bytesTr
500,
CNodeConstants::IISNODE_ERROR_FAILED_READ_REQ_BODY_COMPLETED,
_T("Internal Server Error"),
- error );
+ error,
+ pfCompletionPosted);
}
}
}
-void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chunkLength)
+void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chunkLength, BOOL *pfCompletionPosted)
{
// capture ETW provider since after a successful call to WriteFile the context may be asynchronously deleted
@@ -1058,10 +1108,15 @@ void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chu
context->SetNextProcessor(CProtocolBridge::SendRequestBodyCompleted);
+ // will derefence in AsyncManager::Worker
+ context->ReferenceNodeHttpStoredContext();
+
if (WriteFile(context->GetPipe(), (void*)buffer, length, NULL, context->InitializeOverlapped()))
{
// completed synchronously
+ context->DereferenceNodeHttpStoredContext();
+
etw->Log(context->GetHttpContext(), L"iisnode initiated sending http request body chunk to the node.exe process and completed synchronously",
WINEVENT_LEVEL_VERBOSE,
&activityId);
@@ -1071,7 +1126,7 @@ void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chu
// - see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365683(v=vs.85).aspx
// and http://msdn.microsoft.com/en-us/library/windows/desktop/aa365538(v=vs.85).aspx
- CProtocolBridge::SendRequestBodyCompleted(S_OK, 0, context->GetOverlapped());
+ CProtocolBridge::SendRequestBodyCompleted(S_OK, 0, context->GetOverlapped(), pfCompletionPosted);
}
else
{
@@ -1087,7 +1142,7 @@ void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chu
}
else if (ERROR_NO_DATA == hr)
{
- // Node.exe has closed the named pipe. This means it does not expect any more request data, but it does not mean there is no response.
+ context->DereferenceNodeHttpStoredContext(); // Node.exe has closed the named pipe. This means it does not expect any more request data, but it does not mean there is no response.
// This may happen even for POST requests if the node.js application does not register event handlers for the 'data' or 'end' request events.
// Ignore the write error and attempt to read the response instead (which might have been written by node.exe before the named pipe connection
// was closed). This may also happen for WebSocket traffic.
@@ -1098,16 +1153,17 @@ void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chu
if (context->GetIsUpgrade())
{
- CProtocolBridge::FinalizeUpgradeResponse(context, S_OK);
+ CProtocolBridge::FinalizeUpgradeResponse(context, S_OK, pfCompletionPosted);
}
else
{
- CProtocolBridge::StartReadResponse(context);
+ CProtocolBridge::StartReadResponse(context, pfCompletionPosted);
}
}
else
{
// error
+ context->DereferenceNodeHttpStoredContext();
etw->Log(context->GetHttpContext(), L"iisnode failed to initiate sending http request body chunk to the node.exe process",
WINEVENT_LEVEL_ERROR,
@@ -1115,7 +1171,7 @@ void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chu
if (context->GetIsUpgrade())
{
- CProtocolBridge::FinalizeUpgradeResponse(context, hr);
+ CProtocolBridge::FinalizeUpgradeResponse(context, hr, pfCompletionPosted);
}
else
{
@@ -1123,7 +1179,8 @@ void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chu
500,
CNodeConstants::IISNODE_ERROR_FAILED_INIT_SEND_REQ_BODY,
_T("Internal Server Error"),
- hr );
+ hr,
+ pfCompletionPosted );
}
}
}
@@ -1131,7 +1188,7 @@ void CProtocolBridge::SendRequestBody(CNodeHttpStoredContext* context, DWORD chu
return;
}
-void WINAPI CProtocolBridge::SendRequestBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped)
+void WINAPI CProtocolBridge::SendRequestBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted)
{
CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped);
@@ -1139,7 +1196,7 @@ void WINAPI CProtocolBridge::SendRequestBodyCompleted(DWORD error, DWORD bytesTr
{
ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(),
L"iisnode finished sending http request body chunk to the node.exe process", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId());
- CProtocolBridge::ReadRequestBody(ctx);
+ CProtocolBridge::ReadRequestBody(ctx, pfCompletionPosted);
}
else
{
@@ -1148,7 +1205,7 @@ void WINAPI CProtocolBridge::SendRequestBodyCompleted(DWORD error, DWORD bytesTr
if (ctx->GetIsUpgrade())
{
- CProtocolBridge::FinalizeUpgradeResponse(ctx, error);
+ CProtocolBridge::FinalizeUpgradeResponse(ctx, error, pfCompletionPosted);
}
else
{
@@ -1156,19 +1213,20 @@ void WINAPI CProtocolBridge::SendRequestBodyCompleted(DWORD error, DWORD bytesTr
500,
CNodeConstants::IISNODE_ERROR_FAILED_SEND_REQ_BODY,
_T("Internal Server Error"),
- error );
+ error,
+ pfCompletionPosted);
}
}
}
-void CProtocolBridge::StartReadResponse(CNodeHttpStoredContext* context)
+void CProtocolBridge::StartReadResponse(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted)
{
context->SetDataSize(0);
context->SetParsingOffset(0);
context->SetNextProcessor(CProtocolBridge::ProcessResponseStatusLine);
context->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(context->GetHttpContext(),
L"iisnode starting to read http response", WINEVENT_LEVEL_VERBOSE, context->GetActivityId());
- CProtocolBridge::ContinueReadResponse(context);
+ CProtocolBridge::ContinueReadResponse(context, pfCompletionPosted);
}
HRESULT CProtocolBridge::EnsureBuffer(CNodeHttpStoredContext* context)
@@ -1222,7 +1280,7 @@ HRESULT CProtocolBridge::EnsureBuffer(CNodeHttpStoredContext* context)
}
-void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context)
+void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted)
{
HRESULT hr;
DWORD bytesRead = 0;
@@ -1235,6 +1293,9 @@ void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context)
CheckError(CProtocolBridge::EnsureBuffer(context));
+ // will dereference in AsyncManager::Worker
+ context->ReferenceNodeHttpStoredContext();
+
if (ReadFile(
context->GetPipe(),
(char*)context->GetBuffer() + context->GetDataSize(),
@@ -1243,6 +1304,7 @@ void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context)
context->InitializeOverlapped()))
{
// read completed synchronously
+ context->DereferenceNodeHttpStoredContext();
etw->Log(context->GetHttpContext(), L"iisnode initiated reading http response chunk and completed synchronously",
WINEVENT_LEVEL_VERBOSE,
@@ -1253,7 +1315,7 @@ void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context)
// - see http://msdn.microsoft.com/en-us/library/windows/desktop/aa365683(v=vs.85).aspx
// and http://msdn.microsoft.com/en-us/library/windows/desktop/aa365538(v=vs.85).aspx
- context->GetAsyncContext()->completionProcessor(S_OK, bytesRead, context->GetOverlapped());
+ context->GetAsyncContext()->completionProcessor(S_OK, bytesRead, context->GetOverlapped(), pfCompletionPosted);
}
else if (ERROR_IO_PENDING == (hr = GetLastError()))
{
@@ -1265,13 +1327,15 @@ void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context)
}
else if (ERROR_BROKEN_PIPE == hr && context->GetCloseConnection())
{
+ context->DereferenceNodeHttpStoredContext();
// Termination of a connection indicates the end of the response body if Connection: close response header was present
- CProtocolBridge::FinalizeResponse(context);
+ CProtocolBridge::FinalizeResponse(context, pfCompletionPosted);
}
else
{
// error
+ context->DereferenceNodeHttpStoredContext();
etw->Log(context->GetHttpContext(), L"iisnode failed to initialize reading of http response chunk",
WINEVENT_LEVEL_ERROR,
@@ -1281,7 +1345,8 @@ void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context)
500,
CNodeConstants::IISNODE_ERROR_FAILED_INIT_READ_RESPONSE,
_T("Internal Server Error"),
- hr );
+ hr,
+ pfCompletionPosted);
}
return;
@@ -1294,12 +1359,13 @@ void CProtocolBridge::ContinueReadResponse(CNodeHttpStoredContext* context)
500,
CNodeConstants::IISNODE_ERROR_FAILED_ALLOC_MEM_READ_RESPONSE,
_T("Internal Server Error"),
- hr );
+ hr,
+ pfCompletionPosted);
return;
}
-void WINAPI CProtocolBridge::ProcessResponseStatusLine(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped)
+void WINAPI CProtocolBridge::ProcessResponseStatusLine(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted)
{
HRESULT hr;
CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped);
@@ -1315,14 +1381,14 @@ void WINAPI CProtocolBridge::ProcessResponseStatusLine(DWORD error, DWORD bytesT
L"iisnode finished processing http response status line", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId());
ctx->SetNextProcessor(CProtocolBridge::ProcessResponseHeaders);
- CProtocolBridge::ProcessResponseHeaders(S_OK, 0, ctx->GetOverlapped());
+ CProtocolBridge::ProcessResponseHeaders(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted);
return;
Error:
if (ERROR_MORE_DATA == hr)
{
- CProtocolBridge::ContinueReadResponse(ctx);
+ CProtocolBridge::ContinueReadResponse(ctx, pfCompletionPosted);
}
else
{
@@ -1332,7 +1398,8 @@ void WINAPI CProtocolBridge::ProcessResponseStatusLine(DWORD error, DWORD bytesT
500,
CNodeConstants::IISNODE_ERROR_FAILED_PROCESS_HTTP_STATUS_LINE,
_T("Internal Server Error"),
- hr );
+ hr,
+ pfCompletionPosted);
}
return;
@@ -1450,7 +1517,7 @@ HRESULT CProtocolBridge::AddDebugHeader(CNodeHttpStoredContext* context)
return hr;
}
-void WINAPI CProtocolBridge::ProcessResponseHeaders(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped)
+void WINAPI CProtocolBridge::ProcessResponseHeaders(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted)
{
HRESULT hr;
CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped);
@@ -1474,7 +1541,7 @@ void WINAPI CProtocolBridge::ProcessResponseHeaders(DWORD error, DWORD bytesTran
ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(),
L"iisnode determined the HTTP response does not have entity body", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId());
- CProtocolBridge::FinalizeResponse(ctx);
+ CProtocolBridge::FinalizeResponse(ctx, pfCompletionPosted);
}
else
{
@@ -1523,7 +1590,7 @@ void WINAPI CProtocolBridge::ProcessResponseHeaders(DWORD error, DWORD bytesTran
ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(),
L"iisnode finished processing http response headers", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId());
- ctx->GetAsyncContext()->completionProcessor(S_OK, 0, ctx->GetOverlapped());
+ ctx->GetAsyncContext()->completionProcessor(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted);
}
return;
@@ -1531,7 +1598,7 @@ void WINAPI CProtocolBridge::ProcessResponseHeaders(DWORD error, DWORD bytesTran
if (ERROR_MORE_DATA == hr)
{
- CProtocolBridge::ContinueReadResponse(ctx);
+ CProtocolBridge::ContinueReadResponse(ctx, pfCompletionPosted);
}
else
{
@@ -1541,13 +1608,14 @@ void WINAPI CProtocolBridge::ProcessResponseHeaders(DWORD error, DWORD bytesTran
500,
CNodeConstants::IISNODE_ERROR_FAILED_PROCESS_HTTP_HEADERS,
_T("Internal Server Error"),
- hr );
+ hr,
+ pfCompletionPosted);
}
return;
}
-void WINAPI CProtocolBridge::ProcessChunkHeader(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped)
+void WINAPI CProtocolBridge::ProcessChunkHeader(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted)
{
HRESULT hr;
CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped);
@@ -1564,7 +1632,8 @@ void WINAPI CProtocolBridge::ProcessChunkHeader(DWORD error, DWORD bytesTransfer
L"iisnode finished processing http response body chunk header", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId());
ctx->SetNextProcessor(CProtocolBridge::ProcessResponseBody);
- CProtocolBridge::ProcessResponseBody(S_OK, 0, ctx->GetOverlapped());
+
+ CProtocolBridge::ProcessResponseBody(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted);
return;
@@ -1572,7 +1641,7 @@ void WINAPI CProtocolBridge::ProcessChunkHeader(DWORD error, DWORD bytesTransfer
if (ERROR_MORE_DATA == hr)
{
- CProtocolBridge::ContinueReadResponse(ctx);
+ CProtocolBridge::ContinueReadResponse(ctx, pfCompletionPosted);
}
else
{
@@ -1582,13 +1651,14 @@ void WINAPI CProtocolBridge::ProcessChunkHeader(DWORD error, DWORD bytesTransfer
500,
CNodeConstants::IISNODE_ERROR_FAILED_PROCESS_RESPONSE_BODY_CHUNK_HEADER,
_T("Internal Server Error"),
- hr );
+ hr,
+ pfCompletionPosted);
}
return;
}
-void CProtocolBridge::EnsureRequestPumpStarted(CNodeHttpStoredContext* context)
+void CProtocolBridge::EnsureRequestPumpStarted(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted)
{
if (context->GetOpaqueFlagSet() && !context->GetRequestPumpStarted())
{
@@ -1596,19 +1666,20 @@ void CProtocolBridge::EnsureRequestPumpStarted(CNodeHttpStoredContext* context)
// only after the 101 Switching Protocols response had been sent.
context->SetRequestPumpStarted();
- CProtocolBridge::ReadRequestBody(context->GetUpgradeContext());
+ CProtocolBridge::ReadRequestBody(context->GetUpgradeContext(), pfCompletionPosted);
ASYNC_CONTEXT* async = context->GetUpgradeContext()->GetAsyncContext();
async->RunSynchronousContinuations();
}
}
-void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped)
+void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted)
{
HRESULT hr;
CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped);
HTTP_DATA_CHUNK* chunk;
DWORD bytesSent;
BOOL completionExpected;
+ BOOL fReferenced = FALSE;
ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(),
L"iisnode starting to process http response body", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId());
@@ -1646,6 +1717,10 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe
ctx->SetNextProcessor(CProtocolBridge::SendResponseBodyCompleted);
ctx->SetBytesCompleted(bytesToSend);
+ // will be dereferenced in OnAsyncCompletion
+ ctx->ReferenceNodeHttpStoredContext();
+ fReferenced = TRUE;
+
CheckError(ctx->GetHttpContext()->GetResponse()->WriteEntityChunks(
chunk,
1,
@@ -1659,6 +1734,7 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe
if (!completionExpected)
{
+ ctx->DereferenceNodeHttpStoredContext();
ctx->SetContinueSynchronously(TRUE);
}
}
@@ -1667,7 +1743,7 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe
// process next chunk of the chunked encoding
ctx->SetNextProcessor(CProtocolBridge::ProcessChunkHeader);
- CProtocolBridge::ProcessChunkHeader(S_OK, 0, ctx->GetOverlapped());
+ CProtocolBridge::ProcessChunkHeader(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted);
}
else
{
@@ -1680,7 +1756,7 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe
{
// read more body data
- CProtocolBridge::ContinueReadResponse(ctx);
+ CProtocolBridge::ContinueReadResponse(ctx, pfCompletionPosted);
}
else
{
@@ -1688,11 +1764,11 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe
if (ctx->GetIsUpgrade())
{
- CProtocolBridge::FinalizeUpgradeResponse(ctx, S_OK);
+ CProtocolBridge::FinalizeUpgradeResponse(ctx, S_OK, pfCompletionPosted);
}
else
{
- CProtocolBridge::FinalizeResponse(ctx);
+ CProtocolBridge::FinalizeResponse(ctx, pfCompletionPosted);
}
}
@@ -1703,13 +1779,13 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe
{
// Termination of a connection indicates the end of the upgraded request
- CProtocolBridge::FinalizeUpgradeResponse(ctx, S_OK);
+ CProtocolBridge::FinalizeUpgradeResponse(ctx, S_OK, pfCompletionPosted);
}
else if (ERROR_BROKEN_PIPE == hr && ctx->GetCloseConnection())
{
// Termination of a connection indicates the end of the response body if Connection: close response header was present
- CProtocolBridge::FinalizeResponse(ctx);
+ CProtocolBridge::FinalizeResponse(ctx, pfCompletionPosted);
}
else
{
@@ -1718,7 +1794,7 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe
if (ctx->GetIsUpgrade())
{
- CProtocolBridge::FinalizeUpgradeResponse(ctx, hr);
+ CProtocolBridge::FinalizeUpgradeResponse(ctx, hr, pfCompletionPosted);
}
else
{
@@ -1726,19 +1802,26 @@ void WINAPI CProtocolBridge::ProcessResponseBody(DWORD error, DWORD bytesTransfe
500,
CNodeConstants::IISNODE_ERROR_FAILED_SEND_RESPONSE_BODY_CHUNK,
_T("Internal Server Error"),
- hr );
+ hr,
+ pfCompletionPosted);
}
}
+ if (fReferenced)
+ {
+ ctx->DereferenceNodeHttpStoredContext();
+ }
+
return;
}
-void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped)
+void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted)
{
HRESULT hr;
CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped);
DWORD bytesSent;
BOOL completionExpected = FALSE;
+ BOOL fReference = FALSE;
CheckError(error);
@@ -1754,7 +1837,7 @@ void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesT
if (ctx->GetIsLastChunk() && ctx->GetChunkLength() == ctx->GetChunkTransmitted())
{
- CProtocolBridge::FinalizeResponse(ctx);
+ CProtocolBridge::FinalizeResponse(ctx, pfCompletionPosted);
}
else
{
@@ -1765,12 +1848,18 @@ void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesT
ctx->SetNextProcessor(CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush);
ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(),
L"iisnode initiated flushing http response body chunk", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId());
+ ctx->ReferenceNodeHttpStoredContext();
+ fReference = TRUE;
ctx->GetHttpContext()->GetResponse()->Flush(TRUE, TRUE, &bytesSent, &completionExpected);
}
if (!completionExpected)
{
- CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(S_OK, 0, ctx->GetOverlapped());
+ CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted);
+ if(fReference)
+ {
+ ctx->DereferenceNodeHttpStoredContext();
+ }
}
}
@@ -1782,7 +1871,7 @@ void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesT
if (ctx->GetIsUpgrade())
{
- CProtocolBridge::FinalizeUpgradeResponse(ctx, hr);
+ CProtocolBridge::FinalizeUpgradeResponse(ctx, hr, pfCompletionPosted);
}
else
{
@@ -1790,31 +1879,34 @@ void WINAPI CProtocolBridge::SendResponseBodyCompleted(DWORD error, DWORD bytesT
500,
CNodeConstants::IISNODE_ERROR_FAILED_FLUSH_RESPONSE_BODY,
_T("Internal Server Error"),
- hr );
+ hr,
+ pfCompletionPosted);
}
return;
}
-void WINAPI CProtocolBridge::ProcessUpgradeResponse(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped)
+void WINAPI CProtocolBridge::ProcessUpgradeResponse(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted)
{
- BOOL completionExpected;
+ BOOL completionExpected = FALSE;
DWORD bytesSent;
CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped);
ctx->SetNextProcessor(CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush);
ctx->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(ctx->GetHttpContext(),
L"iisnode initiated flushing http upgrade response headers", WINEVENT_LEVEL_VERBOSE, ctx->GetActivityId());
+ ctx->ReferenceNodeHttpStoredContext();
ctx->GetHttpContext()->GetResponse()->Flush(TRUE, TRUE, &bytesSent, &completionExpected);
if (!completionExpected)
{
- CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(S_OK, 0, ctx->GetOverlapped());
+ CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted);
+ ctx->DereferenceNodeHttpStoredContext();
}
}
-void WINAPI CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped)
+void WINAPI CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * pfCompletionPosted)
{
HRESULT hr;
CNodeHttpStoredContext* ctx = CNodeHttpStoredContext::Get(overlapped);
@@ -1822,11 +1914,11 @@ void WINAPI CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(DWORD
CheckError(error);
// Start reading the request bytes if the request was an accepted HTTP Upgrade
- CProtocolBridge::EnsureRequestPumpStarted(ctx);
+ CProtocolBridge::EnsureRequestPumpStarted(ctx, pfCompletionPosted);
// Continue on to reading the response body
ctx->SetNextProcessor(CProtocolBridge::ProcessResponseBody);
- CProtocolBridge::ProcessResponseBody(S_OK, 0, ctx->GetOverlapped());
+ CProtocolBridge::ProcessResponseBody(S_OK, 0, ctx->GetOverlapped(), pfCompletionPosted);
return;
Error:
@@ -1837,12 +1929,13 @@ void WINAPI CProtocolBridge::ContinueProcessResponseBodyAfterPartialFlush(DWORD
500,
CNodeConstants::IISNODE_ERROR_FAILED_FLUSH_RESPONSE_BODY_PARTIAL_FLUSH,
_T("Internal Server Error"),
- hr );
+ hr,
+ pfCompletionPosted);
return;
}
-void CProtocolBridge::FinalizeUpgradeResponse(CNodeHttpStoredContext* context, HRESULT hresult)
+void CProtocolBridge::FinalizeUpgradeResponse(CNodeHttpStoredContext* context, HRESULT hresult, BOOL *pfCompletionPosted)
{
context->SetNextProcessor(NULL);
context->SetHresult(hresult);
@@ -1858,7 +1951,12 @@ void CProtocolBridge::FinalizeUpgradeResponse(CNodeHttpStoredContext* context, H
CloseHandle(context->GetPipe());
context->SetPipe(INVALID_HANDLE_VALUE);
context->GetHttpContext()->GetResponse()->SetNeedDisconnect();
+
+ // will dereference in OnAsyncCompletion
+ context->ReferenceNodeHttpStoredContext();
+
context->GetHttpContext()->PostCompletion(0);
+ *pfCompletionPosted = TRUE;
}
else
{
@@ -1869,7 +1967,7 @@ void CProtocolBridge::FinalizeUpgradeResponse(CNodeHttpStoredContext* context, H
}
}
-void CProtocolBridge::FinalizeResponse(CNodeHttpStoredContext* context)
+void CProtocolBridge::FinalizeResponse(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted)
{
context->GetNodeApplication()->GetApplicationManager()->GetEventProvider()->Log(context->GetHttpContext(),
@@ -1889,12 +1987,13 @@ void CProtocolBridge::FinalizeResponse(CNodeHttpStoredContext* context)
context,
RQ_NOTIFICATION_CONTINUE,
S_OK,
+ pfCompletionPosted,
context->GetNodeApplication()->GetApplicationManager()->GetEventProvider(),
L"iisnode posts completion from FinalizeResponse",
WINEVENT_LEVEL_VERBOSE);
}
-HRESULT CProtocolBridge::FinalizeResponseCore(CNodeHttpStoredContext* context, REQUEST_NOTIFICATION_STATUS status, HRESULT error, CNodeEventProvider* log, PCWSTR etw, UCHAR level)
+HRESULT CProtocolBridge::FinalizeResponseCore(CNodeHttpStoredContext* context, REQUEST_NOTIFICATION_STATUS status, HRESULT error, BOOL *pfCompletionPosted, CNodeEventProvider* log, PCWSTR etw, UCHAR level)
{
context->SetRequestNotificationStatus(status);
context->SetNextProcessor(NULL);
@@ -1910,8 +2009,12 @@ HRESULT CProtocolBridge::FinalizeResponseCore(CNodeHttpStoredContext* context, R
if (0 == context->DecreasePendingAsyncOperationCount()) // decreases ref count increased in the ctor of CNodeApplication::Dispatch
{
+ // will be dereferenced in OnAsyncCompletion
+ context->ReferenceNodeHttpStoredContext();
+
log->Log(etw, level, context->GetActivityId());
context->GetHttpContext()->PostCompletion(0);
+ *pfCompletionPosted = TRUE;
}
return S_OK;
@@ -1937,10 +2040,14 @@ HRESULT CProtocolBridge::SendDebugRedirect(CNodeHttpStoredContext* context, CNod
response->SetStatus(301, "Moved Permanently");
CheckError(context->GetHttpContext()->GetResponse()->Redirect(path, FALSE, TRUE));
+
+ BOOL fCompletionPosted = FALSE;
+
CProtocolBridge::FinalizeResponseCore(
context,
RQ_NOTIFICATION_FINISH_REQUEST,
S_OK,
+ &fCompletionPosted,
log,
L"iisnode redirected debugging request",
WINEVENT_LEVEL_VERBOSE);
diff --git a/src/iisnode/cprotocolbridge.h b/src/iisnode/cprotocolbridge.h
index 5ce4ab80..054f5e59 100644
--- a/src/iisnode/cprotocolbridge.h
+++ b/src/iisnode/cprotocolbridge.h
@@ -11,48 +11,48 @@ class CProtocolBridge
// utility
static HRESULT PostponeProcessing(CNodeHttpStoredContext* context, DWORD dueTime);
static HRESULT EnsureBuffer(CNodeHttpStoredContext* context);
- static HRESULT FinalizeResponseCore(CNodeHttpStoredContext * context, REQUEST_NOTIFICATION_STATUS status, HRESULT error, CNodeEventProvider* log, PCWSTR etw, UCHAR level);
+ static HRESULT FinalizeResponseCore(CNodeHttpStoredContext * context, REQUEST_NOTIFICATION_STATUS status, HRESULT error, BOOL *pfCompletionPosted, CNodeEventProvider* log, PCWSTR etw, UCHAR level);
static BOOL IsLocalCall(IHttpContext* ctx);
static BOOL SendDevError(CNodeHttpStoredContext* context, USHORT status, USHORT subStatus, PCTSTR reason, HRESULT hresult, BOOL disableCache = FALSE);
static HRESULT AddDebugHeader(CNodeHttpStoredContext* context);
// processing stages
- static void WINAPI ChildContextCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped);
- static void WINAPI CreateNamedPipeConnection(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped);
+ static void WINAPI ChildContextCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted);
+ static void WINAPI CreateNamedPipeConnection(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted);
- static void SendHttpRequestHeaders(CNodeHttpStoredContext* context);
- static void WINAPI SendHttpRequestHeadersCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped);
+ static void SendHttpRequestHeaders(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted);
+ static void WINAPI SendHttpRequestHeadersCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted);
- static void ReadRequestBody(CNodeHttpStoredContext* context);
- static void WINAPI ReadRequestBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped);
+ static void ReadRequestBody(CNodeHttpStoredContext* context, BOOL * fCompletionPosted);
+ static void WINAPI ReadRequestBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted);
- static void SendRequestBody(CNodeHttpStoredContext* context, DWORD chunkLength);
- static void WINAPI SendRequestBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped);
+ static void SendRequestBody(CNodeHttpStoredContext* context, DWORD chunkLength, BOOL *pfCompletionPosted);
+ static void WINAPI SendRequestBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted);
- static void StartReadResponse(CNodeHttpStoredContext* context);
- static void ContinueReadResponse(CNodeHttpStoredContext* context);
- static void WINAPI ProcessResponseStatusLine(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped);
- static void WINAPI ProcessResponseHeaders(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped);
+ static void StartReadResponse(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted);
+ static void ContinueReadResponse(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted);
+ static void WINAPI ProcessResponseStatusLine(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted);
+ static void WINAPI ProcessResponseHeaders(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted);
- static void WINAPI ProcessChunkHeader(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped);
- static void WINAPI ProcessResponseBody(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped);
+ static void WINAPI ProcessChunkHeader(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted);
+ static void WINAPI ProcessResponseBody(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted);
- static void WINAPI ProcessUpgradeResponse(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped);
- static void WINAPI ContinueProcessResponseBodyAfterPartialFlush(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped);
+ static void WINAPI ProcessUpgradeResponse(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted);
+ static void WINAPI ContinueProcessResponseBodyAfterPartialFlush(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted);
- static void EnsureRequestPumpStarted(CNodeHttpStoredContext* context);
+ static void EnsureRequestPumpStarted(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted);
- static void FinalizeResponse(CNodeHttpStoredContext* context);
- static void FinalizeUpgradeResponse(CNodeHttpStoredContext* context, HRESULT hresult);
+ static void FinalizeResponse(CNodeHttpStoredContext* context, BOOL *pfCompletionPosted);
+ static void FinalizeUpgradeResponse(CNodeHttpStoredContext* context, HRESULT hresult, BOOL *pfCompletionPosted);
public:
- static void WINAPI SendResponseBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped);
+ static void WINAPI SendResponseBodyCompleted(DWORD error, DWORD bytesTransfered, LPOVERLAPPED overlapped, BOOL * fCompletionPosted);
static HRESULT InitiateRequest(CNodeHttpStoredContext* context);
static BOOL SendIisnodeError(IHttpContext* httpCtx, HRESULT hr);
static BOOL SendIisnodeError(CNodeHttpStoredContext* ctx, HRESULT hr);
- static HRESULT SendEmptyResponse(CNodeHttpStoredContext* context, USHORT status, USHORT subStatus, PCTSTR reason, HRESULT hresult, BOOL disableCache = FALSE);
+ static HRESULT SendEmptyResponse(CNodeHttpStoredContext* context, USHORT status, USHORT subStatus, PCTSTR reason, HRESULT hresult, BOOL *pfCompletionPosted, BOOL disableCache = FALSE);
static HRESULT SendSyncResponse(IHttpContext* httpCtx, USHORT status, PCTSTR reason, HRESULT hresult, BOOL disableCache, PCSTR htmlBody);
static void SendEmptyResponse(IHttpContext* httpCtx, USHORT status, USHORT subStatus, PCTSTR reason, HRESULT hresult, BOOL disableCache = FALSE);
static HRESULT SendDebugRedirect(CNodeHttpStoredContext* context, CNodeEventProvider* log);
diff --git a/src/version.txt b/src/version.txt
index d88290b2..18de5ee1 100644
--- a/src/version.txt
+++ b/src/version.txt
@@ -1 +1 @@
-0.2.21
+0.2.27