зеркало из https://github.com/Azure/iisnode.git
fix #158: enable YAML node.config with iisnode settings
This commit is contained in:
Родитель
8df17e4dfa
Коммит
66ab0abb69
|
@ -50,8 +50,9 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
|
|||
<attribute name="appendToExistingLog" type="bool" defaultValue="false"/>
|
||||
<attribute name="devErrorsEnabled" type="bool" defaultValue="true"/>
|
||||
<attribute name="flushResponse" type="bool" defaultValue="false"/>
|
||||
<attribute name="watchedFiles" type="string" defaultValue="*.js"/>
|
||||
<attribute name="watchedFiles" type="string" defaultValue="*.js;node.config"/>
|
||||
<attribute name="enableXFF" type="bool" defaultValue="false"/>
|
||||
<attribute name="promoteServerVars" type="string" defaultValue=""/>
|
||||
<attribute name="configOverrides" type="string" defaultValue="node.config"/>
|
||||
</sectionSchema>
|
||||
</configSchema>
|
||||
|
|
|
@ -50,8 +50,9 @@ Details at http://learn.iis.net/page.aspx/241/configuration-extensibility/
|
|||
<attribute name="appendToExistingLog" type="bool" defaultValue="false"/>
|
||||
<attribute name="devErrorsEnabled" type="bool" defaultValue="true"/>
|
||||
<attribute name="flushResponse" type="bool" defaultValue="false"/>
|
||||
<attribute name="watchedFiles" type="string" defaultValue="*.js"/>
|
||||
<attribute name="watchedFiles" type="string" defaultValue="*.js;node.config"/>
|
||||
<attribute name="enableXFF" type="bool" defaultValue="false"/>
|
||||
<attribute name="promoteServerVars" type="string" defaultValue=""/>
|
||||
<attribute name="configOverrides" type="string" defaultValue="node.config"/>
|
||||
</sectionSchema>
|
||||
</configSchema>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#include "precomp.h"
|
||||
|
||||
CFileWatcher::CFileWatcher()
|
||||
: completionPort(NULL), worker(NULL), directories(NULL), uncFileSharePollingInterval(0)
|
||||
: completionPort(NULL), worker(NULL), directories(NULL), uncFileSharePollingInterval(0),
|
||||
configOverridesFileName(NULL), configOverridesFileNameLength(0)
|
||||
{
|
||||
InitializeCriticalSection(&this->syncRoot);
|
||||
}
|
||||
|
@ -38,6 +39,12 @@ CFileWatcher::~CFileWatcher()
|
|||
delete currentDirectory;
|
||||
}
|
||||
|
||||
if (NULL != this->configOverridesFileName)
|
||||
{
|
||||
delete [] this->configOverridesFileName;
|
||||
this->configOverridesFileName = NULL;
|
||||
}
|
||||
|
||||
DeleteCriticalSection(&this->syncRoot);
|
||||
}
|
||||
|
||||
|
@ -48,6 +55,14 @@ HRESULT CFileWatcher::Initialize(IHttpContext* context)
|
|||
ErrorIf(NULL == (this->completionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1)), GetLastError());
|
||||
this->uncFileSharePollingInterval = CModuleConfiguration::GetUNCFileChangesPollingInterval(context);
|
||||
|
||||
LPWSTR overrides = CModuleConfiguration::GetConfigOverrides(context);
|
||||
if (overrides && *overrides != L'\0')
|
||||
{
|
||||
this->configOverridesFileNameLength = wcslen(overrides);
|
||||
ErrorIf(NULL == (this->configOverridesFileName = new WCHAR[this->configOverridesFileNameLength + 1]), ERROR_NOT_ENOUGH_MEMORY);
|
||||
wcscpy(this->configOverridesFileName, overrides);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
Error:
|
||||
|
||||
|
@ -271,6 +286,13 @@ HRESULT CFileWatcher::WatchFile(PCWSTR directoryName, DWORD directoryNameLength,
|
|||
file->wildcard = wildcard;
|
||||
this->GetWatchedFileTimestamp(file, &file->lastWrite);
|
||||
|
||||
// determine if this is the yaml config file
|
||||
|
||||
if (this->configOverridesFileNameLength == (endFileName - startFileName))
|
||||
{
|
||||
file->yamlConfig = 0 == memcmp(this->configOverridesFileName, startFileName, this->configOverridesFileNameLength * sizeof WCHAR);
|
||||
}
|
||||
|
||||
// find matching directory watcher entry
|
||||
|
||||
directory = this->directories;
|
||||
|
@ -531,6 +553,14 @@ BOOL CFileWatcher::ScanDirectory(WatchedDirectory* directory, BOOL unc)
|
|||
if (S_OK == CFileWatcher::GetWatchedFileTimestamp(file, ×tamp)
|
||||
&& 0 != memcmp(×tamp, &file->lastWrite, sizeof FILETIME))
|
||||
{
|
||||
if (file->yamlConfig)
|
||||
{
|
||||
// the node.config file has changed
|
||||
// invalidate the configuration such that on next message it will be re-created
|
||||
|
||||
CModuleConfiguration::Invalidate();
|
||||
}
|
||||
|
||||
memcpy(&file->lastWrite, ×tamp, sizeof FILETIME);
|
||||
file->callback(file->manager, file->application);
|
||||
return TRUE;
|
||||
|
|
|
@ -18,6 +18,7 @@ private:
|
|||
BOOL unc;
|
||||
BOOL wildcard;
|
||||
FILETIME lastWrite;
|
||||
BOOL yamlConfig;
|
||||
struct _WatchedFile* next;
|
||||
} WatchedFile;
|
||||
|
||||
|
@ -35,6 +36,8 @@ private:
|
|||
WatchedDirectory* directories;
|
||||
DWORD uncFileSharePollingInterval;
|
||||
CRITICAL_SECTION syncRoot;
|
||||
LPWSTR configOverridesFileName;
|
||||
DWORD configOverridesFileNameLength;
|
||||
|
||||
static unsigned int WINAPI Worker(void* arg);
|
||||
BOOL ScanDirectory(WatchedDirectory* directory, BOOL unc);
|
||||
|
|
|
@ -4,11 +4,15 @@ IHttpServer* CModuleConfiguration::server = NULL;
|
|||
|
||||
HTTP_MODULE_ID CModuleConfiguration::moduleId = NULL;
|
||||
|
||||
BOOL CModuleConfiguration::invalid = FALSE;
|
||||
|
||||
CModuleConfiguration::CModuleConfiguration()
|
||||
: nodeProcessCommandLine(NULL), logDirectoryNameSuffix(NULL), debuggerPathSegment(NULL),
|
||||
debugPortRange(NULL), debugPortStart(0), debugPortEnd(0), node_env(NULL), watchedFiles(NULL),
|
||||
enableXFF(FALSE), promoteServerVars(NULL)
|
||||
enableXFF(FALSE), promoteServerVars(NULL), promoteServerVarsRaw(NULL), configOverridesFileName(NULL),
|
||||
configOverrides(NULL)
|
||||
{
|
||||
InitializeSRWLock(&this->srwlock);
|
||||
}
|
||||
|
||||
CModuleConfiguration::~CModuleConfiguration()
|
||||
|
@ -56,6 +60,29 @@ CModuleConfiguration::~CModuleConfiguration()
|
|||
delete [] this->promoteServerVars;
|
||||
this->promoteServerVars = NULL;
|
||||
}
|
||||
|
||||
if (NULL != this->promoteServerVarsRaw)
|
||||
{
|
||||
delete [] this->promoteServerVarsRaw;
|
||||
this->promoteServerVarsRaw = NULL;
|
||||
}
|
||||
|
||||
if (NULL != this->configOverridesFileName)
|
||||
{
|
||||
delete [] this->configOverridesFileName;
|
||||
this->configOverridesFileName = NULL;
|
||||
}
|
||||
|
||||
if (NULL != this->configOverrides)
|
||||
{
|
||||
delete [] this->configOverrides;
|
||||
this->configOverrides = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CModuleConfiguration::Invalidate()
|
||||
{
|
||||
CModuleConfiguration::invalid = TRUE;
|
||||
}
|
||||
|
||||
HRESULT CModuleConfiguration::Initialize(IHttpServer* server, HTTP_MODULE_ID moduleId)
|
||||
|
@ -437,6 +464,509 @@ Error:
|
|||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CModuleConfiguration::GetDWORD(char* str, DWORD* value)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (str)
|
||||
{
|
||||
long v = atol(str);
|
||||
ErrorIf((v == LONG_MAX || v == LONG_MIN) && errno == ERANGE, E_FAIL);
|
||||
*value = (DWORD)v;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
Error:
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CModuleConfiguration::GetBOOL(char* str, BOOL* value)
|
||||
{
|
||||
if (!str)
|
||||
{
|
||||
*value = FALSE;
|
||||
}
|
||||
else if (0 == strcmpi(str, "false") || 0 == strcmpi(str, "0") || 0 == strcmpi(str, "no"))
|
||||
{
|
||||
*value = FALSE;
|
||||
}
|
||||
else if (0 == strcmpi(str, "true") || 0 == strcmpi(str, "1") || 0 == strcmpi(str, "yes"))
|
||||
{
|
||||
*value = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CModuleConfiguration::GetString(char* str, LPWSTR* value)
|
||||
{
|
||||
HRESULT hr;
|
||||
int wcharSize, bytesConverted;
|
||||
|
||||
if (*value)
|
||||
{
|
||||
delete [] *value;
|
||||
*value = NULL;
|
||||
}
|
||||
|
||||
if (!str)
|
||||
{
|
||||
str = "";
|
||||
}
|
||||
|
||||
ErrorIf(0 == (wcharSize = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0)), GetLastError());
|
||||
ErrorIf(NULL == (*value = new WCHAR[wcharSize]), ERROR_NOT_ENOUGH_MEMORY);
|
||||
ErrorIf(wcharSize != MultiByteToWideChar(CP_ACP, 0, str, -1, *value, wcharSize), GetLastError());
|
||||
|
||||
return S_OK;
|
||||
Error:
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CModuleConfiguration::ApplyConfigOverrideKeyValue(IHttpContext* context, CModuleConfiguration* config, char* keyStart, char* keyEnd, char* valueStart, char* valueEnd)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
keyEnd++;
|
||||
*keyEnd = 0;
|
||||
|
||||
if (valueEnd)
|
||||
{
|
||||
valueEnd++;
|
||||
*valueEnd = 0;
|
||||
}
|
||||
|
||||
if (0 == strcmpi(keyStart, "asyncCompletionThreadCount"))
|
||||
{
|
||||
CheckError(GetDWORD(valueStart, &config->asyncCompletionThreadCount));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "nodeProcessCountPerApplication"))
|
||||
{
|
||||
CheckError(GetDWORD(valueStart, &config->nodeProcessCountPerApplication));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "maxConcurrentRequestsPerProcess"))
|
||||
{
|
||||
CheckError(GetDWORD(valueStart, &config->maxConcurrentRequestsPerProcess));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "maxNamedPipeConnectionRetry"))
|
||||
{
|
||||
CheckError(GetDWORD(valueStart, &config->maxNamedPipeConnectionRetry));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "namedPipeConnectionRetryDelay"))
|
||||
{
|
||||
CheckError(GetDWORD(valueStart, &config->namedPipeConnectionRetryDelay));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "maxNamedPipeConnectionPoolSize"))
|
||||
{
|
||||
CheckError(GetDWORD(valueStart, &config->maxNamedPipeConnectionPoolSize));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "maxNamedPipePooledConnectionAge"))
|
||||
{
|
||||
CheckError(GetDWORD(valueStart, &config->maxNamedPipePooledConnectionAge));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "initialRequestBufferSize"))
|
||||
{
|
||||
CheckError(GetDWORD(valueStart, &config->initialRequestBufferSize));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "maxRequestBufferSize"))
|
||||
{
|
||||
CheckError(GetDWORD(valueStart, &config->maxRequestBufferSize));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "uncFileChangesPollingInterval"))
|
||||
{
|
||||
CheckError(GetDWORD(valueStart, &config->uncFileChangesPollingInterval));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "gracefulShutdownTimeout"))
|
||||
{
|
||||
CheckError(GetDWORD(valueStart, &config->gracefulShutdownTimeout));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "logFileFlushInterval"))
|
||||
{
|
||||
CheckError(GetDWORD(valueStart, &config->logFileFlushInterval));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "maxLogFileSizeInKB"))
|
||||
{
|
||||
CheckError(GetDWORD(valueStart, &config->maxLogFileSizeInKB));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "loggingEnabled"))
|
||||
{
|
||||
CheckError(GetBOOL(valueStart, &config->loggingEnabled));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "appendToExistingLog"))
|
||||
{
|
||||
CheckError(GetBOOL(valueStart, &config->appendToExistingLog));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "devErrorsEnabled"))
|
||||
{
|
||||
CheckError(GetBOOL(valueStart, &config->devErrorsEnabled));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "flushResponse"))
|
||||
{
|
||||
CheckError(GetBOOL(valueStart, &config->flushResponse));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "debuggingEnabled"))
|
||||
{
|
||||
CheckError(GetBOOL(valueStart, &config->debuggingEnabled));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "enableXFF"))
|
||||
{
|
||||
CheckError(GetBOOL(valueStart, &config->enableXFF));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "logDirectoryNameSuffix"))
|
||||
{
|
||||
CheckError(GetString(valueStart, &config->logDirectoryNameSuffix));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "node_env"))
|
||||
{
|
||||
CheckError(GetString(valueStart, &config->node_env));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "debugPortRange"))
|
||||
{
|
||||
CheckError(GetString(valueStart, &config->debugPortRange));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "watchedFiles"))
|
||||
{
|
||||
CheckError(GetString(valueStart, &config->watchedFiles));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "promoteServerVars"))
|
||||
{
|
||||
CheckError(GetString(valueStart, &config->promoteServerVarsRaw));
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "debuggerPathSegment"))
|
||||
{
|
||||
CheckError(GetString(valueStart, &config->debuggerPathSegment));
|
||||
config->debuggerPathSegmentLength = wcslen(config->debuggerPathSegment);
|
||||
}
|
||||
else if (0 == strcmpi(keyStart, "nodeProcessCommandLine"))
|
||||
{
|
||||
if (config->nodeProcessCommandLine)
|
||||
{
|
||||
delete [] config->nodeProcessCommandLine;
|
||||
config->nodeProcessCommandLine = NULL;
|
||||
}
|
||||
|
||||
ErrorIf(NULL == (config->nodeProcessCommandLine = new char[MAX_PATH]), ERROR_NOT_ENOUGH_MEMORY);
|
||||
if (valueStart)
|
||||
{
|
||||
strcpy(config->nodeProcessCommandLine, valueStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(config->nodeProcessCommandLine, "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return S_OK;
|
||||
Error:
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CModuleConfiguration::ApplyYamlConfigOverrides(IHttpContext* context, CModuleConfiguration* config)
|
||||
{
|
||||
HRESULT hr;
|
||||
PCWSTR scriptTranslated;
|
||||
DWORD scriptTranslatedLength;
|
||||
HANDLE overridesHandle = INVALID_HANDLE_VALUE;
|
||||
DWORD fileSize;
|
||||
DWORD bytesRead;
|
||||
char* content = NULL;
|
||||
char* lineStart;
|
||||
char* lineEnd;
|
||||
char* colon;
|
||||
char* comment;
|
||||
char *keyStart, *keyEnd;
|
||||
char *valueStart, *valueEnd;
|
||||
|
||||
if (config->configOverrides == L'\0')
|
||||
{
|
||||
// no file name with config overrides specified, return success
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// construct absolute file name by replacing the script name in the script translated path with the config override file name
|
||||
|
||||
if (!config->configOverridesFileName)
|
||||
{
|
||||
scriptTranslated = context->GetScriptTranslated(&scriptTranslatedLength);
|
||||
ErrorIf(NULL == (config->configOverridesFileName = new WCHAR[scriptTranslatedLength + wcslen(config->configOverrides) + 1]), ERROR_NOT_ENOUGH_MEMORY);
|
||||
wcscpy(config->configOverridesFileName, scriptTranslated);
|
||||
while (scriptTranslatedLength > 0 && config->configOverridesFileName[scriptTranslatedLength] != L'\\')
|
||||
scriptTranslatedLength--;
|
||||
wcscpy(config->configOverridesFileName + scriptTranslatedLength + 1, config->configOverrides);
|
||||
}
|
||||
|
||||
// open configuration override file if it exists
|
||||
|
||||
if (INVALID_HANDLE_VALUE == (overridesHandle = CreateFileW(
|
||||
config->configOverridesFileName,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
NULL))) {
|
||||
hr = GetLastError();
|
||||
|
||||
// if file does not exist, clean up and return success (i.e. no config overrides specified)
|
||||
ErrorIf(ERROR_FILE_NOT_FOUND == hr, S_OK);
|
||||
|
||||
// if other error occurred, return the error
|
||||
ErrorIf(TRUE, hr);
|
||||
}
|
||||
|
||||
// read file content
|
||||
|
||||
ErrorIf(INVALID_FILE_SIZE == (fileSize = GetFileSize(overridesHandle, NULL)), GetLastError());
|
||||
ErrorIf(0 == fileSize, S_OK); // empty file, clean up and return success
|
||||
ErrorIf(NULL == (content = new char[fileSize + 1]), ERROR_NOT_ENOUGH_MEMORY);
|
||||
ErrorIf(!ReadFile(overridesHandle, content, fileSize, &bytesRead, NULL), GetLastError());
|
||||
ErrorIf(fileSize != bytesRead, E_FAIL);
|
||||
content[bytesRead] = 0;
|
||||
CloseHandle(overridesHandle);
|
||||
overridesHandle = INVALID_HANDLE_VALUE;
|
||||
|
||||
// parse file
|
||||
|
||||
lineStart = lineEnd = content;
|
||||
while (*lineStart)
|
||||
{
|
||||
// determine the placement of a comment and colon as well as the end of the line
|
||||
colon = comment = NULL;
|
||||
while (*lineEnd && *lineEnd != '\r' && *lineEnd != '\n')
|
||||
{
|
||||
if (*lineEnd == ':')
|
||||
{
|
||||
if (!colon)
|
||||
colon = lineEnd;
|
||||
}
|
||||
else if (*lineEnd == '#')
|
||||
{
|
||||
if (!comment)
|
||||
comment = lineEnd;
|
||||
}
|
||||
|
||||
lineEnd++;
|
||||
}
|
||||
|
||||
// comment will be the sentinel of the end of this line
|
||||
if (!comment)
|
||||
comment = lineEnd;
|
||||
|
||||
// skip whitespace at the end of this line and beginning of next
|
||||
while (*lineEnd == ' ' || *lineEnd == '\r' || *lineEnd == '\n')
|
||||
lineEnd++;
|
||||
|
||||
// skip whitespace at the beginning of line
|
||||
while (lineStart < comment && *lineStart == ' ')
|
||||
lineStart++;
|
||||
|
||||
if (lineStart < comment)
|
||||
{
|
||||
// there is a non-whitespace character before the end of the line or comment on that line
|
||||
// assume <key>: <value> [#<comment>] syntax of the line
|
||||
keyStart = lineStart;
|
||||
ErrorIf(!colon, E_FAIL); // there is no colon on the line
|
||||
ErrorIf(keyStart == colon, E_FAIL); // colon is the first non-whitespace character on the line
|
||||
|
||||
// find end of key name
|
||||
while (lineStart < colon && *lineStart != ' ')
|
||||
lineStart++;
|
||||
keyEnd = lineStart - 1;
|
||||
|
||||
// skip whitespace between end of key name and colon
|
||||
while (lineStart < colon && *lineStart == ' ')
|
||||
lineStart++;
|
||||
ErrorIf(lineStart != colon, E_FAIL); // non-whitespace character found
|
||||
|
||||
// skip whitespace before value
|
||||
lineStart++;
|
||||
while (lineStart < comment && *lineStart == ' ')
|
||||
lineStart++;
|
||||
|
||||
if (lineStart == comment)
|
||||
{
|
||||
// empty value
|
||||
valueStart = valueEnd = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
valueStart = lineStart;
|
||||
|
||||
// find end of value as a last non-whitespace character before comment
|
||||
valueEnd = comment - 1;
|
||||
while (valueEnd > valueStart && *valueEnd == ' ')
|
||||
valueEnd--;
|
||||
|
||||
CheckError(CModuleConfiguration::ApplyConfigOverrideKeyValue(context, config, keyStart, keyEnd, valueStart, valueEnd));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// move on to the next line
|
||||
lineStart = lineEnd;
|
||||
}
|
||||
|
||||
hr = S_OK; // fall through to cleanup in the Error section
|
||||
Error:
|
||||
|
||||
if (overridesHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(overridesHandle);
|
||||
overridesHandle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if (content)
|
||||
{
|
||||
delete [] content;
|
||||
content = NULL;
|
||||
}
|
||||
|
||||
return S_OK == hr ? S_OK : IISNODE_ERROR_UNABLE_TO_READ_CONFIGURATION_OVERRIDE;
|
||||
}
|
||||
|
||||
HRESULT CModuleConfiguration::TokenizePromoteServerVars(CModuleConfiguration* c)
|
||||
{
|
||||
HRESULT hr;
|
||||
size_t i;
|
||||
size_t varLength;
|
||||
LPWSTR start, end;
|
||||
wchar_t terminator;
|
||||
|
||||
if (c->promoteServerVarsRaw)
|
||||
{
|
||||
if (NULL != c->promoteServerVars)
|
||||
{
|
||||
for (int i = 0; i < c->promoteServerVarsCount; i++)
|
||||
{
|
||||
if (c->promoteServerVars[i])
|
||||
{
|
||||
delete [] c->promoteServerVars[i];
|
||||
}
|
||||
}
|
||||
|
||||
delete [] c->promoteServerVars;
|
||||
c->promoteServerVars = NULL;
|
||||
}
|
||||
|
||||
if (*c->promoteServerVarsRaw == L'\0')
|
||||
{
|
||||
c->promoteServerVarsCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// determine number of server variables
|
||||
|
||||
c->promoteServerVarsCount = 1;
|
||||
start = c->promoteServerVarsRaw;
|
||||
while (*start)
|
||||
{
|
||||
if (L',' == *start)
|
||||
{
|
||||
c->promoteServerVarsCount++;
|
||||
}
|
||||
|
||||
start++;
|
||||
}
|
||||
|
||||
// tokenize server variable names (comma delimited list)
|
||||
|
||||
ErrorIf(NULL == (c->promoteServerVars = new char*[c->promoteServerVarsCount]), ERROR_NOT_ENOUGH_MEMORY);
|
||||
RtlZeroMemory(c->promoteServerVars, c->promoteServerVarsCount * sizeof(char*));
|
||||
|
||||
i = 0;
|
||||
end = c->promoteServerVarsRaw;
|
||||
while (*end)
|
||||
{
|
||||
start = end;
|
||||
while (*end && L',' != *end)
|
||||
{
|
||||
end++;
|
||||
}
|
||||
|
||||
if (start != end)
|
||||
{
|
||||
terminator = *end;
|
||||
*end = L'\0';
|
||||
ErrorIf(0 != wcstombs_s(&varLength, NULL, 0, start, _TRUNCATE), ERROR_CAN_NOT_COMPLETE);
|
||||
ErrorIf(NULL == (c->promoteServerVars[i] = new char[varLength]), ERROR_NOT_ENOUGH_MEMORY);
|
||||
ErrorIf(0 != wcstombs_s(&varLength, c->promoteServerVars[i], varLength, start, _TRUNCATE), ERROR_CAN_NOT_COMPLETE);
|
||||
i++;
|
||||
*end = terminator;
|
||||
}
|
||||
|
||||
if (*end)
|
||||
{
|
||||
end++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete [] c->promoteServerVarsRaw;
|
||||
c->promoteServerVarsRaw = NULL;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
Error:
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CModuleConfiguration::ApplyDefaults(CModuleConfiguration* c)
|
||||
{
|
||||
if (0 == c->asyncCompletionThreadCount)
|
||||
{
|
||||
// default number of async completion threads is the number of processors
|
||||
|
||||
SYSTEM_INFO info;
|
||||
|
||||
GetSystemInfo(&info);
|
||||
c->asyncCompletionThreadCount = 0 == info.dwNumberOfProcessors ? 4 : info.dwNumberOfProcessors;
|
||||
}
|
||||
|
||||
if (0 == c->nodeProcessCountPerApplication)
|
||||
{
|
||||
// default number of node.exe processes to create per node.js application
|
||||
|
||||
SYSTEM_INFO info;
|
||||
|
||||
GetSystemInfo(&info);
|
||||
c->nodeProcessCountPerApplication = 0 == info.dwNumberOfProcessors ? 1 : info.dwNumberOfProcessors;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CModuleConfiguration::EnsureCurrent(IHttpContext* context, CModuleConfiguration* config)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if (config && CModuleConfiguration::invalid)
|
||||
{
|
||||
// yaml config has changed and needs to be re-read; this condition was set by the CFileWatcher
|
||||
|
||||
ENTER_SRW_EXCLUSIVE(config->srwlock)
|
||||
|
||||
if (CModuleConfiguration::invalid)
|
||||
{
|
||||
CheckError(CModuleConfiguration::ApplyYamlConfigOverrides(context, config));
|
||||
CheckError(CModuleConfiguration::TokenizePromoteServerVars(config));
|
||||
CheckError(CModuleConfiguration::ApplyDefaults(config));
|
||||
CModuleConfiguration::invalid = FALSE;
|
||||
}
|
||||
|
||||
LEAVE_SRW_EXCLUSIVE(config->srwlock)
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
Error:
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT CModuleConfiguration::GetConfig(IHttpContext* context, CModuleConfiguration** config)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
@ -444,14 +974,12 @@ HRESULT CModuleConfiguration::GetConfig(IHttpContext* context, CModuleConfigurat
|
|||
IAppHostElement* section = NULL;
|
||||
LPWSTR commandLine = NULL;
|
||||
size_t i;
|
||||
size_t varLength;
|
||||
LPWSTR serverVars = NULL;
|
||||
LPWSTR start, end;
|
||||
wchar_t terminator;
|
||||
CheckNull(config);
|
||||
|
||||
*config = (CModuleConfiguration*)context->GetMetadata()->GetModuleContextContainer()->GetModuleContext(moduleId);
|
||||
|
||||
CheckError(CModuleConfiguration::EnsureCurrent(context, *config));
|
||||
|
||||
if (NULL == *config)
|
||||
{
|
||||
ErrorIf(NULL == (c = new CModuleConfiguration()), ERROR_NOT_ENOUGH_MEMORY);
|
||||
|
@ -480,6 +1008,8 @@ HRESULT CModuleConfiguration::GetConfig(IHttpContext* context, CModuleConfigurat
|
|||
CheckError(GetString(section, L"debuggerPortRange", &c->debugPortRange));
|
||||
CheckError(GetString(section, L"watchedFiles", &c->watchedFiles));
|
||||
CheckError(GetBOOL(section, L"enableXFF", &c->enableXFF));
|
||||
CheckError(GetString(section, L"promoteServerVars", &c->promoteServerVarsRaw));
|
||||
CheckError(GetString(section, L"configOverrides", &c->configOverrides));
|
||||
|
||||
// debuggerPathSegment
|
||||
|
||||
|
@ -494,89 +1024,22 @@ HRESULT CModuleConfiguration::GetConfig(IHttpContext* context, CModuleConfigurat
|
|||
delete [] commandLine;
|
||||
commandLine = NULL;
|
||||
|
||||
// promoteServerVars
|
||||
// apply config setting overrides from the optional YAML configuration file
|
||||
|
||||
CheckError(GetString(section, L"promoteServerVars", &serverVars));
|
||||
if (*serverVars == L'\0')
|
||||
{
|
||||
c->promoteServerVarsCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// determine number of server variables
|
||||
CheckError(CModuleConfiguration::ApplyYamlConfigOverrides(context, c));
|
||||
|
||||
c->promoteServerVarsCount = 1;
|
||||
start = serverVars;
|
||||
while (*start)
|
||||
{
|
||||
if (L',' == *start)
|
||||
{
|
||||
c->promoteServerVarsCount++;
|
||||
}
|
||||
// tokenize promoteServerVars
|
||||
|
||||
start++;
|
||||
}
|
||||
CheckError(CModuleConfiguration::TokenizePromoteServerVars(c));
|
||||
|
||||
// tokenize server variable names (comma delimited list)
|
||||
|
||||
ErrorIf(NULL == (c->promoteServerVars = new char*[c->promoteServerVarsCount]), ERROR_NOT_ENOUGH_MEMORY);
|
||||
RtlZeroMemory(c->promoteServerVars, c->promoteServerVarsCount * sizeof(char*));
|
||||
|
||||
i = 0;
|
||||
end = serverVars;
|
||||
while (*end)
|
||||
{
|
||||
start = end;
|
||||
while (*end && L',' != *end)
|
||||
{
|
||||
end++;
|
||||
}
|
||||
|
||||
if (start != end)
|
||||
{
|
||||
terminator = *end;
|
||||
*end = L'\0';
|
||||
ErrorIf(0 != wcstombs_s(&varLength, NULL, 0, start, _TRUNCATE), ERROR_CAN_NOT_COMPLETE);
|
||||
ErrorIf(NULL == (c->promoteServerVars[i] = new char[varLength]), ERROR_NOT_ENOUGH_MEMORY);
|
||||
ErrorIf(0 != wcstombs_s(&varLength, c->promoteServerVars[i], varLength, start, _TRUNCATE), ERROR_CAN_NOT_COMPLETE);
|
||||
i++;
|
||||
*end = terminator;
|
||||
}
|
||||
|
||||
if (*end)
|
||||
{
|
||||
end++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete [] serverVars;
|
||||
serverVars = NULL;
|
||||
// done with section
|
||||
|
||||
section->Release();
|
||||
section = NULL;
|
||||
|
||||
// apply defaults
|
||||
|
||||
if (0 == c->asyncCompletionThreadCount)
|
||||
{
|
||||
// default number of async completion threads is the number of processors
|
||||
|
||||
SYSTEM_INFO info;
|
||||
|
||||
GetSystemInfo(&info);
|
||||
c->asyncCompletionThreadCount = 0 == info.dwNumberOfProcessors ? 4 : info.dwNumberOfProcessors;
|
||||
}
|
||||
|
||||
if (0 == c->nodeProcessCountPerApplication)
|
||||
{
|
||||
// default number of node.exe processes to create per node.js application
|
||||
|
||||
SYSTEM_INFO info;
|
||||
|
||||
GetSystemInfo(&info);
|
||||
c->nodeProcessCountPerApplication = 0 == info.dwNumberOfProcessors ? 1 : info.dwNumberOfProcessors;
|
||||
}
|
||||
CheckError(CModuleConfiguration::ApplyDefaults(c));
|
||||
|
||||
// CR: check for ERROR_ALREADY_ASSIGNED to detect a race in creation of this section
|
||||
// CR: refcounting may be needed if synchronous code paths proove too long (race with config changes)
|
||||
|
@ -600,12 +1063,6 @@ Error:
|
|||
commandLine = NULL;
|
||||
}
|
||||
|
||||
if (NULL != serverVars)
|
||||
{
|
||||
delete [] serverVars;
|
||||
serverVars = NULL;
|
||||
}
|
||||
|
||||
if (NULL != c)
|
||||
{
|
||||
delete c;
|
||||
|
@ -750,6 +1207,11 @@ BOOL CModuleConfiguration::GetEnableXFF(IHttpContext* ctx)
|
|||
GETCONFIG(enableXFF)
|
||||
}
|
||||
|
||||
LPWSTR CModuleConfiguration::GetConfigOverrides(IHttpContext* ctx)
|
||||
{
|
||||
GETCONFIG(configOverrides)
|
||||
}
|
||||
|
||||
HRESULT CModuleConfiguration::GetDebugPortRange(IHttpContext* ctx, DWORD* start, DWORD* end)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
|
|
@ -37,13 +37,26 @@ private:
|
|||
BOOL enableXFF;
|
||||
char** promoteServerVars;
|
||||
int promoteServerVarsCount;
|
||||
LPWSTR promoteServerVarsRaw;
|
||||
LPWSTR configOverridesFileName;
|
||||
static BOOL invalid;
|
||||
SRWLOCK srwlock;
|
||||
LPWSTR configOverrides;
|
||||
|
||||
static IHttpServer* server;
|
||||
static HTTP_MODULE_ID moduleId;
|
||||
static HRESULT GetConfigSection(IHttpContext* context, IAppHostElement** section, OLECHAR* configElement = L"system.webServer/iisnode");
|
||||
static HRESULT GetString(IAppHostElement* section, LPCWSTR propertyName, LPWSTR* value);
|
||||
static HRESULT GetBOOL(IAppHostElement* section, LPCWSTR propertyName, BOOL* value);
|
||||
static HRESULT GetDWORD(char* str, DWORD* value);
|
||||
static HRESULT GetBOOL(char* str, BOOL* value);
|
||||
static HRESULT GetString(char* str, LPWSTR* value);
|
||||
static HRESULT GetDWORD(IAppHostElement* section, LPCWSTR propertyName, DWORD* value);
|
||||
static HRESULT ApplyConfigOverrideKeyValue(IHttpContext* context, CModuleConfiguration* config, char* keyStart, char* keyEnd, char* valueStart, char* valueEnd);
|
||||
static HRESULT ApplyYamlConfigOverrides(IHttpContext* context, CModuleConfiguration* config);
|
||||
static HRESULT TokenizePromoteServerVars(CModuleConfiguration* c);
|
||||
static HRESULT ApplyDefaults(CModuleConfiguration* c);
|
||||
static HRESULT EnsureCurrent(IHttpContext* context, CModuleConfiguration* config);
|
||||
|
||||
CModuleConfiguration();
|
||||
~CModuleConfiguration();
|
||||
|
@ -81,9 +94,12 @@ public:
|
|||
static DWORD GetMaxNamedPipePooledConnectionAge(IHttpContext* ctx);
|
||||
static BOOL GetEnableXFF(IHttpContext* ctx);
|
||||
static HRESULT GetPromoteServerVars(IHttpContext* ctx, char*** vars, int* count);
|
||||
static LPWSTR GetConfigOverrides(IHttpContext* ctx);
|
||||
|
||||
static HRESULT CreateNodeEnvironment(IHttpContext* ctx, DWORD debugPort, PCH namedPipe, PCH* env);
|
||||
|
||||
static void Invalidate();
|
||||
|
||||
virtual void CleanupStoredContext();
|
||||
};
|
||||
|
||||
|
|
|
@ -13,23 +13,20 @@ HRESULT CNodeApplicationManager::Initialize(IHttpContext* context)
|
|||
HRESULT hr = S_OK;
|
||||
CModuleConfiguration *config;
|
||||
|
||||
if (!this->initialized)
|
||||
if (S_OK != (hr = CModuleConfiguration::GetConfig(context, &config)))
|
||||
{
|
||||
hr = IISNODE_ERROR_UNABLE_TO_READ_CONFIGURATION_OVERRIDE == hr ? hr : IISNODE_ERROR_UNABLE_TO_READ_CONFIGURATION;
|
||||
}
|
||||
else if (!this->initialized)
|
||||
{
|
||||
if (S_OK != CModuleConfiguration::GetConfig(context, &config))
|
||||
{
|
||||
hr = IISNODE_ERROR_UNABLE_TO_READ_CONFIGURATION;
|
||||
}
|
||||
else
|
||||
{
|
||||
ENTER_SRW_EXCLUSIVE(this->srwlock)
|
||||
ENTER_SRW_EXCLUSIVE(this->srwlock)
|
||||
|
||||
if (!this->initialized)
|
||||
{
|
||||
hr = this->InitializeCore(context);
|
||||
}
|
||||
|
||||
LEAVE_SRW_EXCLUSIVE(this->srwlock)
|
||||
if (!this->initialized)
|
||||
{
|
||||
hr = this->InitializeCore(context);
|
||||
}
|
||||
|
||||
LEAVE_SRW_EXCLUSIVE(this->srwlock)
|
||||
}
|
||||
|
||||
return hr;
|
||||
|
|
|
@ -45,20 +45,41 @@ BOOL CProtocolBridge::IsLocalCall(IHttpContext* ctx)
|
|||
|
||||
BOOL CProtocolBridge::SendIisnodeError(IHttpContext* httpCtx, HRESULT hr)
|
||||
{
|
||||
if (IISNODE_ERROR_UNABLE_TO_READ_CONFIGURATION == hr)
|
||||
if (IISNODE_ERROR_UNABLE_TO_READ_CONFIGURATION == hr || IISNODE_ERROR_UNABLE_TO_READ_CONFIGURATION_OVERRIDE == hr)
|
||||
{
|
||||
if (CProtocolBridge::IsLocalCall(httpCtx))
|
||||
{
|
||||
CProtocolBridge::SendSyncResponse(
|
||||
httpCtx,
|
||||
200,
|
||||
"OK",
|
||||
hr,
|
||||
TRUE,
|
||||
"iisnode was unable to read the configuration file. Make sure the web.config file syntax is correct. In particular, verify the "
|
||||
" <a href=""https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config"">"
|
||||
"iisnode configuration section</a> matches the expected schema. The schema of the iisnode section that your version of iisnode requires is stored in the "
|
||||
"%systemroot%\\system32\\inetsrv\\config\\schema\\iisnode_schema.xml file.");
|
||||
switch (hr) {
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
|
||||
case IISNODE_ERROR_UNABLE_TO_READ_CONFIGURATION:
|
||||
CProtocolBridge::SendSyncResponse(
|
||||
httpCtx,
|
||||
200,
|
||||
"OK",
|
||||
hr,
|
||||
TRUE,
|
||||
"iisnode was unable to read the configuration file. Make sure the web.config file syntax is correct. In particular, verify the "
|
||||
" <a href=""https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config"">"
|
||||
"iisnode configuration section</a> matches the expected schema. The schema of the iisnode section that your version of iisnode requires is stored in the "
|
||||
"%systemroot%\\system32\\inetsrv\\config\\schema\\iisnode_schema.xml file.");
|
||||
break;
|
||||
|
||||
case IISNODE_ERROR_UNABLE_TO_READ_CONFIGURATION_OVERRIDE:
|
||||
CProtocolBridge::SendSyncResponse(
|
||||
httpCtx,
|
||||
200,
|
||||
"OK",
|
||||
hr,
|
||||
TRUE,
|
||||
"iisnode was unable to read the configuration file node.config. Make sure the node.config file syntax is correct. For reference, check "
|
||||
" <a href=""https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/node.config"">"
|
||||
"the sample node.config file</a>. The property names recognized in the node.config file of your version of iisnode are stored in the "
|
||||
"%systemroot%\\system32\\inetsrv\\config\\schema\\iisnode_schema.xml file.");
|
||||
break;
|
||||
};
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
|
@ -9,5 +9,6 @@
|
|||
#define IISNODE_ERROR_UNABLE_TO_CREATE_LOG_FILE 1029L
|
||||
#define IISNODE_ERROR_UNABLE_TO_CREATE_DEBUGGER_FILES 1030L
|
||||
#define IISNODE_ERROR_UNABLE_TO_READ_CONFIGURATION 1031L
|
||||
#define IISNODE_ERROR_UNABLE_TO_READ_CONFIGURATION_OVERRIDE 1032L
|
||||
|
||||
#endif
|
|
@ -257,6 +257,8 @@ copy /y $(ProjectDir)\..\config\* $(ProjectDir)\..\..\build\$(Configuration)\$(P
|
|||
<None Include="..\..\test\functional\tests\121_watchedFiles.js" />
|
||||
<None Include="..\..\test\functional\tests\122_multipleResponseHeaders.js" />
|
||||
<None Include="..\..\test\functional\tests\123_upload.js" />
|
||||
<None Include="..\..\test\functional\tests\124_node_config_override.js" />
|
||||
<None Include="..\..\test\functional\tests\125_node_config_autoupdate.js" />
|
||||
<None Include="..\..\test\functional\tests\200_samples.bat" />
|
||||
<None Include="..\..\test\functional\tests\node_modules\iisnodeassert.js" />
|
||||
<None Include="..\..\test\functional\tests\parts\106_autoupdate_first.js" />
|
||||
|
@ -316,6 +318,12 @@ copy /y $(ProjectDir)\..\config\* $(ProjectDir)\..\..\build\$(Configuration)\$(P
|
|||
<None Include="..\..\test\functional\www\122_multipleResponseHeaders\web.config" />
|
||||
<None Include="..\..\test\functional\www\123_upload\hello.js" />
|
||||
<None Include="..\..\test\functional\www\123_upload\web.config" />
|
||||
<None Include="..\..\test\functional\www\124_node_config_override\hello.js" />
|
||||
<None Include="..\..\test\functional\www\124_node_config_override\node.config" />
|
||||
<None Include="..\..\test\functional\www\124_node_config_override\web.config" />
|
||||
<None Include="..\..\test\functional\www\125_node_config_autoupdate\.gitignore" />
|
||||
<None Include="..\..\test\functional\www\125_node_config_autoupdate\hello.js" />
|
||||
<None Include="..\..\test\functional\www\125_node_config_autoupdate\web.config" />
|
||||
<None Include="..\..\test\performance\client.bat" />
|
||||
<None Include="..\..\test\performance\localRun.bat" />
|
||||
<None Include="..\..\test\performance\readme.txt" />
|
||||
|
@ -326,6 +334,7 @@ copy /y $(ProjectDir)\..\config\* $(ProjectDir)\..\..\build\$(Configuration)\$(P
|
|||
<None Include="..\config\iisnode_schema.xml" />
|
||||
<None Include="..\config\iisnode_schema_x64.xml" />
|
||||
<None Include="..\samples\configuration\hello.js" />
|
||||
<None Include="..\samples\configuration\node.config" />
|
||||
<None Include="..\samples\configuration\readme.htm">
|
||||
<DeploymentContent>true</DeploymentContent>
|
||||
</None>
|
||||
|
|
|
@ -138,6 +138,12 @@
|
|||
<Filter Include="Tests\functional\www\123_upload">
|
||||
<UniqueIdentifier>{18d24dd3-6582-41ad-856d-949ed0626792}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Tests\functional\www\124_node_config_override">
|
||||
<UniqueIdentifier>{ebde3d11-88f9-44b3-ba68-1752293b7729}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Tests\functional\www\125_node_config_autoupdate">
|
||||
<UniqueIdentifier>{e8b955a6-1062-48cf-8799-e5481a1c759b}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
|
@ -600,6 +606,33 @@
|
|||
<None Include="..\..\test\functional\tests\123_upload.js">
|
||||
<Filter>Tests\functional\tests</Filter>
|
||||
</None>
|
||||
<None Include="..\samples\configuration\node.config">
|
||||
<Filter>Samples\configuration</Filter>
|
||||
</None>
|
||||
<None Include="..\..\test\functional\www\124_node_config_override\hello.js">
|
||||
<Filter>Tests\functional\www\124_node_config_override</Filter>
|
||||
</None>
|
||||
<None Include="..\..\test\functional\www\124_node_config_override\node.config">
|
||||
<Filter>Tests\functional\www\124_node_config_override</Filter>
|
||||
</None>
|
||||
<None Include="..\..\test\functional\www\124_node_config_override\web.config">
|
||||
<Filter>Tests\functional\www\124_node_config_override</Filter>
|
||||
</None>
|
||||
<None Include="..\..\test\functional\www\125_node_config_autoupdate\.gitignore">
|
||||
<Filter>Tests\functional\www\125_node_config_autoupdate</Filter>
|
||||
</None>
|
||||
<None Include="..\..\test\functional\www\125_node_config_autoupdate\hello.js">
|
||||
<Filter>Tests\functional\www\125_node_config_autoupdate</Filter>
|
||||
</None>
|
||||
<None Include="..\..\test\functional\www\125_node_config_autoupdate\web.config">
|
||||
<Filter>Tests\functional\www\125_node_config_autoupdate</Filter>
|
||||
</None>
|
||||
<None Include="..\..\test\functional\tests\124_node_config_override.js">
|
||||
<Filter>Tests\functional\tests</Filter>
|
||||
</None>
|
||||
<None Include="..\..\test\functional\tests\125_node_config_autoupdate.js">
|
||||
<Filter>Tests\functional\tests</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="iisnode.rc" />
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
# The optional node.config file provides overrides of the iisnode configuration settings specified in web.config.
|
||||
|
||||
# node_env - determines the environment (production, development, staging, ...) in which
|
||||
# child node processes run; if nonempty, is propagated to the child node processes as their NODE_ENV
|
||||
# environment variable; the default is the value of the IIS worker process'es NODE_ENV
|
||||
# environment variable
|
||||
|
||||
node_env: %node_env%
|
||||
|
||||
# nodeProcessCommandLine - command line starting the node executable; in shared
|
||||
# hosting environments this setting would typically be locked at the machine scope.
|
||||
|
||||
# nodeProcessCommandLine: "%programfiles%\nodejs\node.exe"
|
||||
|
||||
# nodeProcessCountPerApplication - number of node.exe processes that IIS will start per application;
|
||||
# setting this value to 0 results in creating one node.exe process per each processor on the machine
|
||||
|
||||
nodeProcessCountPerApplication: 1
|
||||
|
||||
# maxConcurrentRequestsPerProcess - maximum number of reqeusts one node process can
|
||||
# handle at a time
|
||||
|
||||
maxConcurrentRequestsPerProcess: 1024
|
||||
|
||||
# maxNamedPipeConnectionRetry - number of times IIS will retry to establish a named pipe connection with a
|
||||
# node process in order to send a new HTTP request
|
||||
|
||||
maxNamedPipeConnectionRetry: 24
|
||||
|
||||
# namedPipeConnectionRetryDelay - delay in milliseconds between connection retries
|
||||
|
||||
namedPipeConnectionRetryDelay: 250
|
||||
|
||||
# maxNamedPipeConnectionPoolSize - maximum number of named pipe connections that will be kept in a connection pool;
|
||||
# connection pooling helps improve the performance of applications that process a large number of short lived HTTP requests
|
||||
|
||||
maxNamedPipeConnectionPoolSize: 512
|
||||
|
||||
# maxNamedPipePooledConnectionAge - age of a pooled connection in milliseconds after which the connection is not reused for
|
||||
# subsequent requests
|
||||
|
||||
maxNamedPipePooledConnectionAge: 30000
|
||||
|
||||
# asyncCompletionThreadCount - size of the IO thread pool maintained by the IIS module to process asynchronous IO; setting it
|
||||
# to 0 (default) results in creating one thread per each processor on the machine
|
||||
|
||||
asyncCompletionThreadCount: 0
|
||||
|
||||
# initialRequestBufferSize - initial size in bytes of a memory buffer allocated for a new HTTP request
|
||||
|
||||
initialRequestBufferSize: 4096
|
||||
|
||||
# maxRequestBufferSize - maximum size in bytes of a memory buffer allocated per request; this is a hard limit of
|
||||
# the serialized form of HTTP request or response headers block
|
||||
|
||||
maxRequestBufferSize: 65536
|
||||
|
||||
# watchedFiles - semi-colon separated list of files that will be watched for changes; a change to a file causes the application to recycle;
|
||||
# each entry consists of an optional directory name plus required file name which are relative to the directory where the main application entry point
|
||||
# is located; wild cards are allowed in the file name portion only; for example: "*.js;node_modules\foo\lib\options.json;app_data\*.config.json"
|
||||
|
||||
watchedFiles: *.js;node.config
|
||||
|
||||
# uncFileChangesPollingInterval - applications are recycled when the underlying *.js file is modified; if the file resides
|
||||
# on a UNC share, the only reliable way to detect such modifications is to periodically poll for them; this setting
|
||||
# controls the polling interval
|
||||
|
||||
uncFileChangesPollingInterval: 5000
|
||||
|
||||
# gracefulShutdownTimeout - when a node.js file is modified, all node processes handling running this application are recycled;
|
||||
# this setting controls the time (in milliseconds) given for currently active requests to gracefully finish before the
|
||||
# process is terminated; during this time, all new requests are already dispatched to a new node process based on the fresh version
|
||||
# of the application
|
||||
|
||||
gracefulShutdownTimeout: 60000
|
||||
|
||||
# loggingEnabled - controls whether stdout and stderr streams from node processes are captured and made available over HTTP
|
||||
|
||||
loggingEnabled: true
|
||||
|
||||
# logDirectoryNameSuffix - suffix of the directory name that will store files with stdout and stderr captures; directly name is created
|
||||
# by appending this suffix to the file name of the node.js application; individual log files are stored in that directory, one per node
|
||||
# process (in files of the form x.txt, where x is between 0 and nodeProcessCountPerApplication - 1); given a node.js application at
|
||||
# http://localhost/node/hello.js, log files would by default be stored at http://localhost/node/hello.js.logs/0.txt (thrugh 3.txt);
|
||||
# SECURITY NOTE: if log files contain sensitive information, this setting should be modified to contain enough entropy to be considered
|
||||
# cryptographically secure; in most situations, a GUID is sufficient
|
||||
|
||||
logDirectoryNameSuffix: logs
|
||||
|
||||
# debuggingEnabled - controls whether the built-in debugger is available
|
||||
|
||||
debuggingEnabled: true
|
||||
|
||||
# debuggerPortRange - range of TCP ports that can be used for communication between the node-inspector debugger and the debugee; iisnode
|
||||
# will round robin through this port range for subsequent debugging sessions and pick the next available (free) port to use from the range
|
||||
|
||||
debuggerPortRange: 5058-6058
|
||||
|
||||
# debuggerPathSegment - URL path segment used to access the built-in node-inspector debugger; given a node.js application at
|
||||
http://foo.com/bar/baz.js, the debugger can be accessed at http://foo.com/bar/baz.js/{debuggerPathSegment}, by default
|
||||
http://foo.com/bar/baz.js/debug
|
||||
|
||||
debuggerPathSegment: debug
|
||||
|
||||
# maxLogFileSizeInKB - maximum size of a log file in KB; once a log file exceeds this limit it is truncated back to empty
|
||||
|
||||
maxLogFileSizeInKB: 128
|
||||
|
||||
# appendToExistingLog - determines whether pre-existing log files are appended to or created fresh when a node process with a given ordinal
|
||||
# number starts; appending may be useful to diagnose unorderly node process terminations or recycling
|
||||
|
||||
appendToExistingLog: false
|
||||
|
||||
# logFileFlushInterval - interval in milliseconds for flushing logs to the log files
|
||||
|
||||
logFileFlushInterval: 5000
|
||||
|
||||
# devErrorsEnabled - controls how much information is sent back in the HTTP response to the browser when an error occurrs in iisnode;
|
||||
# when true, error conditions in iisnode result in HTTP 200 response with the body containing error details; when false,
|
||||
# iisnode will return generic HTTP 5xx responses
|
||||
|
||||
devErrorsEnabled: true
|
||||
|
||||
# flushResponse - controls whether each HTTP response body chunk is immediately flushed by iisnode; flushing each body chunk incurs
|
||||
# CPU cost but may improve latency in streaming scenarios
|
||||
|
||||
flushResponse: false
|
||||
|
||||
# enableXFF - controls whether iisnode adds or modifies the X-Forwarded-For request HTTP header with the IP address of the remote host
|
||||
|
||||
enableXFF: false
|
||||
|
||||
# promoteServerVars - comma delimited list of IIS server variables that will be propagated to the node.exe process in the form of
|
||||
# x-iisnode-<server_variable_name>
|
||||
# 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:
|
|
@ -13,12 +13,13 @@
|
|||
configuration</h2>
|
||||
<p>
|
||||
There are several configuration options that can be controlled from the system.webServer/iisnode
|
||||
section of the configurtion file. Review web.config below for detailed description
|
||||
section of the web.config configurtion file or the node.config file. Review web.config or node.config below for detailed description
|
||||
of them.</p>
|
||||
visit the node.js endpoint at <a href="hello.js">hello.js</a><br />
|
||||
visit the logs at <a href="hello.js.logs/0.txt">logs</a> (only available after you
|
||||
first visit the endpoint)<br />
|
||||
debug the hello.js endpoint at <a href="hello.js/debug" target="_blank">hello.js/debug</a> (requires WebKit enabled browser)<br />
|
||||
debug the hello.js endpoint at <a href="hello.js/debug" target="_blank">hello.js/debug</a>
|
||||
(requires WebKit enabled browser)<br />
|
||||
<p>
|
||||
<b>code</b></p>
|
||||
<pre>var http = require('http');
|
||||
|
@ -130,6 +131,14 @@ console.log('Application started at location ' + process.env.PORT);</pre>
|
|||
x-iisnode-<server_variable_name> 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"
|
||||
|
||||
* configOverrides - optional file name containing overrides of configuration settings of the iisnode section of web.config;
|
||||
the format of the file is a small subset of YAML: each setting is represented as a <key>: <value> on a separate line
|
||||
and comments start with # until the end of the line, e.g.
|
||||
# This is a sample node.config file
|
||||
nodeProcessCountPerApplication: 2
|
||||
maxRequestBufferSize: 8192 # increasing from the default
|
||||
# maxConcurrentRequestsPerProcess: 512 - commented out setting
|
||||
|
||||
-->
|
||||
|
||||
<iisnode
|
||||
|
@ -143,7 +152,7 @@ console.log('Application started at location ' + process.env.PORT);</pre>
|
|||
asyncCompletionThreadCount="0"
|
||||
initialRequestBufferSize="4096"
|
||||
maxRequestBufferSize="65536"
|
||||
watchedFiles="*.js"
|
||||
watchedFiles="*.js;node.conf"
|
||||
uncFileChangesPollingInterval="5000"
|
||||
gracefulShutdownTimeout="60000"
|
||||
loggingEnabled="true"
|
||||
|
@ -157,6 +166,7 @@ console.log('Application started at location ' + process.env.PORT);</pre>
|
|||
flushResponse="false"
|
||||
enableXFF="false"
|
||||
promoteServerVars=""
|
||||
configOverrides="node.conf"
|
||||
/>
|
||||
|
||||
<!--
|
||||
|
@ -171,5 +181,145 @@ console.log('Application started at location ' + process.env.PORT);</pre>
|
|||
|
||||
</system.webServer>
|
||||
</configuration></pre>
|
||||
<p>
|
||||
<b>node.config</b></p>
|
||||
<pre># The optional node.config file provides overrides of the iisnode configuration settings specified in web.config.
|
||||
|
||||
# node_env - determines the environment (production, development, staging, ...) in which
|
||||
# child node processes run; if nonempty, is propagated to the child node processes as their NODE_ENV
|
||||
# environment variable; the default is the value of the IIS worker process'es NODE_ENV
|
||||
# environment variable
|
||||
|
||||
node_env: %node_env%
|
||||
|
||||
# nodeProcessCommandLine - command line starting the node executable; in shared
|
||||
# hosting environments this setting would typically be locked at the machine scope.
|
||||
|
||||
# nodeProcessCommandLine: "%programfiles%\nodejs\node.exe"
|
||||
|
||||
# nodeProcessCountPerApplication - number of node.exe processes that IIS will start per application;
|
||||
# setting this value to 0 results in creating one node.exe process per each processor on the machine
|
||||
|
||||
nodeProcessCountPerApplication: 1
|
||||
|
||||
# maxConcurrentRequestsPerProcess - maximum number of reqeusts one node process can
|
||||
# handle at a time
|
||||
|
||||
maxConcurrentRequestsPerProcess: 1024
|
||||
|
||||
# maxNamedPipeConnectionRetry - number of times IIS will retry to establish a named pipe connection with a
|
||||
# node process in order to send a new HTTP request
|
||||
|
||||
maxNamedPipeConnectionRetry: 24
|
||||
|
||||
# namedPipeConnectionRetryDelay - delay in milliseconds between connection retries
|
||||
|
||||
namedPipeConnectionRetryDelay: 250
|
||||
|
||||
# maxNamedPipeConnectionPoolSize - maximum number of named pipe connections that will be kept in a connection pool;
|
||||
# connection pooling helps improve the performance of applications that process a large number of short lived HTTP requests
|
||||
|
||||
maxNamedPipeConnectionPoolSize: 512
|
||||
|
||||
# maxNamedPipePooledConnectionAge - age of a pooled connection in milliseconds after which the connection is not reused for
|
||||
# subsequent requests
|
||||
|
||||
maxNamedPipePooledConnectionAge: 30000
|
||||
|
||||
# asyncCompletionThreadCount - size of the IO thread pool maintained by the IIS module to process asynchronous IO; setting it
|
||||
# to 0 (default) results in creating one thread per each processor on the machine
|
||||
|
||||
asyncCompletionThreadCount: 0
|
||||
|
||||
# initialRequestBufferSize - initial size in bytes of a memory buffer allocated for a new HTTP request
|
||||
|
||||
initialRequestBufferSize: 4096
|
||||
|
||||
# maxRequestBufferSize - maximum size in bytes of a memory buffer allocated per request; this is a hard limit of
|
||||
# the serialized form of HTTP request or response headers block
|
||||
|
||||
maxRequestBufferSize: 65536
|
||||
|
||||
# watchedFiles - semi-colon separated list of files that will be watched for changes; a change to a file causes the application to recycle;
|
||||
# each entry consists of an optional directory name plus required file name which are relative to the directory where the main application entry point
|
||||
# is located; wild cards are allowed in the file name portion only; for example: "*.js;node_modules\foo\lib\options.json;app_data\*.config.json"
|
||||
|
||||
watchedFiles: *.js;node.config
|
||||
|
||||
# uncFileChangesPollingInterval - applications are recycled when the underlying *.js file is modified; if the file resides
|
||||
# on a UNC share, the only reliable way to detect such modifications is to periodically poll for them; this setting
|
||||
# controls the polling interval
|
||||
|
||||
uncFileChangesPollingInterval: 5000
|
||||
|
||||
# gracefulShutdownTimeout - when a node.js file is modified, all node processes handling running this application are recycled;
|
||||
# this setting controls the time (in milliseconds) given for currently active requests to gracefully finish before the
|
||||
# process is terminated; during this time, all new requests are already dispatched to a new node process based on the fresh version
|
||||
# of the application
|
||||
|
||||
gracefulShutdownTimeout: 60000
|
||||
|
||||
# loggingEnabled - controls whether stdout and stderr streams from node processes are captured and made available over HTTP
|
||||
|
||||
loggingEnabled: true
|
||||
|
||||
# logDirectoryNameSuffix - suffix of the directory name that will store files with stdout and stderr captures; directly name is created
|
||||
# by appending this suffix to the file name of the node.js application; individual log files are stored in that directory, one per node
|
||||
# process (in files of the form x.txt, where x is between 0 and nodeProcessCountPerApplication - 1); given a node.js application at
|
||||
# http://localhost/node/hello.js, log files would by default be stored at http://localhost/node/hello.js.logs/0.txt (thrugh 3.txt);
|
||||
# SECURITY NOTE: if log files contain sensitive information, this setting should be modified to contain enough entropy to be considered
|
||||
# cryptographically secure; in most situations, a GUID is sufficient
|
||||
|
||||
logDirectoryNameSuffix: logs
|
||||
|
||||
# debuggingEnabled - controls whether the built-in debugger is available
|
||||
|
||||
debuggingEnabled: true
|
||||
|
||||
# debuggerPortRange - range of TCP ports that can be used for communication between the node-inspector debugger and the debugee; iisnode
|
||||
# will round robin through this port range for subsequent debugging sessions and pick the next available (free) port to use from the range
|
||||
|
||||
debuggerPortRange: 5058-6058
|
||||
|
||||
# debuggerPathSegment - URL path segment used to access the built-in node-inspector debugger; given a node.js application at
|
||||
http://foo.com/bar/baz.js, the debugger can be accessed at http://foo.com/bar/baz.js/{debuggerPathSegment}, by default
|
||||
http://foo.com/bar/baz.js/debug
|
||||
|
||||
debuggerPathSegment: debug
|
||||
|
||||
# maxLogFileSizeInKB - maximum size of a log file in KB; once a log file exceeds this limit it is truncated back to empty
|
||||
|
||||
maxLogFileSizeInKB: 128
|
||||
|
||||
# appendToExistingLog - determines whether pre-existing log files are appended to or created fresh when a node process with a given ordinal
|
||||
# number starts; appending may be useful to diagnose unorderly node process terminations or recycling
|
||||
|
||||
appendToExistingLog: false
|
||||
|
||||
# logFileFlushInterval - interval in milliseconds for flushing logs to the log files
|
||||
|
||||
logFileFlushInterval: 5000
|
||||
|
||||
# devErrorsEnabled - controls how much information is sent back in the HTTP response to the browser when an error occurrs in iisnode;
|
||||
# when true, error conditions in iisnode result in HTTP 200 response with the body containing error details; when false,
|
||||
# iisnode will return generic HTTP 5xx responses
|
||||
|
||||
devErrorsEnabled: true
|
||||
|
||||
# flushResponse - controls whether each HTTP response body chunk is immediately flushed by iisnode; flushing each body chunk incurs
|
||||
# CPU cost but may improve latency in streaming scenarios
|
||||
|
||||
flushResponse: false
|
||||
|
||||
# enableXFF - controls whether iisnode adds or modifies the X-Forwarded-For request HTTP header with the IP address of the remote host
|
||||
|
||||
enableXFF: false
|
||||
|
||||
# promoteServerVars - comma delimited list of IIS server variables that will be propagated to the node.exe process in the form of
|
||||
# x-iisnode-<server_variable_name>
|
||||
# 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:</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -96,6 +96,14 @@
|
|||
x-iisnode-<server_variable_name> 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"
|
||||
|
||||
* configOverrides - optional file name containing overrides of configuration settings of the iisnode section of web.config;
|
||||
the format of the file is a small subset of YAML: each setting is represented as a <key>: <value> on a separate line
|
||||
and comments start with # until the end of the line, e.g.
|
||||
# This is a sample node.config file
|
||||
nodeProcessCountPerApplication: 2
|
||||
maxRequestBufferSize: 8192 # increasing from the default
|
||||
# maxConcurrentRequestsPerProcess: 512 - commented out setting
|
||||
|
||||
-->
|
||||
|
||||
<iisnode
|
||||
|
@ -109,7 +117,7 @@
|
|||
asyncCompletionThreadCount="0"
|
||||
initialRequestBufferSize="4096"
|
||||
maxRequestBufferSize="65536"
|
||||
watchedFiles="*.js"
|
||||
watchedFiles="*.js;node.config"
|
||||
uncFileChangesPollingInterval="5000"
|
||||
gracefulShutdownTimeout="60000"
|
||||
loggingEnabled="true"
|
||||
|
@ -124,6 +132,7 @@
|
|||
flushResponse="false"
|
||||
enableXFF="false"
|
||||
promoteServerVars=""
|
||||
configOverrides="node.config"
|
||||
/>
|
||||
|
||||
<!--
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
<File Id="filC84C6F4F3A4A26E6C60922E9EB27A44A" KeyPath="no" Source="$(var.sourcedir)\www\configuration\hello.js" />
|
||||
<File Id="fil71B6426C908BE42E25A29282F767DFF0" KeyPath="no" Source="$(var.sourcedir)\www\configuration\readme.htm" />
|
||||
<File Id="fil2F0416F09F068096D5E253A91DCDFB4D" KeyPath="no" Source="$(var.sourcedir)\www\configuration\web.config" />
|
||||
<File Id="fil2F0416F09F068096D5E253A91DCDFB4F" KeyPath="no" Source="$(var.sourcedir)\www\configuration\node.config" />
|
||||
<RegistryKey Root="HKCU" Key="Software\Microsoft\iisnode\www\configuration" Action="createAndRemoveOnUninstall">
|
||||
<RegistryValue Name="hello.js" Value="[ProductVersion]" Type="string" KeyPath="yes"/>
|
||||
</RegistryKey>
|
||||
|
|
|
@ -78,6 +78,9 @@
|
|||
<Component Id="cmp43304DBF7D5F428226B66D8BCE98F79C" Guid="D677A41F-77E9-4133-A646-54B6800A824B">
|
||||
<File Id="fil2F0416F09F068096D5E253A91DCDFB4D" KeyPath="yes" Source="$(var.sourcedir)\www\configuration\web.config" />
|
||||
</Component>
|
||||
<Component Id="cmp43304DBF7D5F428226B66D8BCE98F79D" Guid="D677A41F-77E9-4133-A646-54B6800A824C">
|
||||
<File Id="fil2F0416F09F068096D5E253A91DCDFB4E" KeyPath="yes" Source="$(var.sourcedir)\www\configuration\node.config" />
|
||||
</Component>
|
||||
</Directory>
|
||||
<Directory Id="dir45A892E29212AE212D756BFAD925773B" Name="defaultdocument">
|
||||
<Component Id="cmpB18006FAB8C1983F7075672963FB789C" Guid="8977FE53-FB08-4FA5-86F9-1E3F25B4A6CB">
|
||||
|
@ -348,6 +351,7 @@
|
|||
<ComponentRef Id="cmp97BBEA440C75C9AA1726A3083CD6B0AA" />
|
||||
<ComponentRef Id="cmp35E8791EC1813578EC05F557AD0DB971" />
|
||||
<ComponentRef Id="cmp43304DBF7D5F428226B66D8BCE98F79C" />
|
||||
<ComponentRef Id="cmp43304DBF7D5F428226B66D8BCE98F79D" />
|
||||
<ComponentRef Id="cmpB18006FAB8C1983F7075672963FB789C" />
|
||||
<ComponentRef Id="cmp4EE8AC791973A668E0BD31E0C7C56779" />
|
||||
<ComponentRef Id="cmp797D37F6545ADEB8D9388E1F4E27274F" />
|
||||
|
|
|
@ -78,6 +78,9 @@
|
|||
<Component Win64="yes" Id="cmp43304DBF7D5F428226B66D8BCE98F79C" Guid="D677A41F-77E9-4133-A646-54B6800A824B">
|
||||
<File Id="fil2F0416F09F068096D5E253A91DCDFB4D" KeyPath="yes" Source="$(var.sourcedir)\www\configuration\web.config" />
|
||||
</Component>
|
||||
<Component Win64="yes" Id="cmp43304DBF7D5F428226B66D8BCE98F79D" Guid="D677A41F-77E9-4133-A646-54B6800A824C">
|
||||
<File Id="fil2F0416F09F068096D5E253A91DCDFB4E" KeyPath="yes" Source="$(var.sourcedir)\www\configuration\node.config" />
|
||||
</Component>
|
||||
</Directory>
|
||||
<Directory Id="dir45A892E29212AE212D756BFAD925773B" Name="defaultdocument">
|
||||
<Component Win64="yes" Id="cmpB18006FAB8C1983F7075672963FB789C" Guid="8977FE53-FB08-4FA5-86F9-1E3F25B4A6CB">
|
||||
|
@ -348,6 +351,7 @@
|
|||
<ComponentRef Id="cmp97BBEA440C75C9AA1726A3083CD6B0AA" />
|
||||
<ComponentRef Id="cmp35E8791EC1813578EC05F557AD0DB971" />
|
||||
<ComponentRef Id="cmp43304DBF7D5F428226B66D8BCE98F79C" />
|
||||
<ComponentRef Id="cmp43304DBF7D5F428226B66D8BCE98F79D" />
|
||||
<ComponentRef Id="cmpB18006FAB8C1983F7075672963FB789C" />
|
||||
<ComponentRef Id="cmp4EE8AC791973A668E0BD31E0C7C56779" />
|
||||
<ComponentRef Id="cmp797D37F6545ADEB8D9388E1F4E27274F" />
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
Number of processes has been changed from 2 specified in web.config to 4 specified in node.config
|
||||
*/
|
||||
|
||||
var iisnodeassert = require("iisnodeassert");
|
||||
|
||||
iisnodeassert.sequence([
|
||||
iisnodeassert.get(10000, "/124_node_config_override/hello.js", 200, "Hello, world 1"),
|
||||
iisnodeassert.get(2000, "/124_node_config_override/hello.js", 200, "Hello, world 1"),
|
||||
iisnodeassert.get(2000, "/124_node_config_override/hello.js", 200, "Hello, world 1"),
|
||||
iisnodeassert.get(2000, "/124_node_config_override/hello.js", 200, "Hello, world 1"),
|
||||
iisnodeassert.get(2000, "/124_node_config_override/hello.js", 200, "Hello, world 2"),
|
||||
iisnodeassert.get(2000, "/124_node_config_override/hello.js", 200, "Hello, world 2"),
|
||||
iisnodeassert.get(2000, "/124_node_config_override/hello.js", 200, "Hello, world 2"),
|
||||
iisnodeassert.get(2000, "/124_node_config_override/hello.js", 200, "Hello, world 2")
|
||||
]);
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
Changes in node.config recycle the application
|
||||
*/
|
||||
|
||||
var iisnodeassert = require("iisnodeassert");
|
||||
|
||||
iisnodeassert.sequence([
|
||||
iisnodeassert.get(10000, "/125_node_config_autoupdate/hello.js?revert", 200, "node.config reverted"),
|
||||
iisnodeassert.get(10000, "/125_node_config_autoupdate/hello.js", 200, "Hello, world 1"),
|
||||
iisnodeassert.get(2000, "/125_node_config_autoupdate/hello.js", 200, "Hello, world 1"),
|
||||
iisnodeassert.get(2000, "/125_node_config_autoupdate/hello.js", 200, "Hello, world 2"),
|
||||
iisnodeassert.get(2000, "/125_node_config_autoupdate/hello.js", 200, "Hello, world 2"),
|
||||
iisnodeassert.get(2000, "/125_node_config_autoupdate/hello.js?update", 200, "node.config updated"),
|
||||
iisnodeassert.get(10000, "/125_node_config_autoupdate/hello.js", 200, "Hello, world 1"),
|
||||
iisnodeassert.get(2000, "/125_node_config_autoupdate/hello.js", 200, "Hello, world 1"),
|
||||
iisnodeassert.get(2000, "/125_node_config_autoupdate/hello.js", 200, "Hello, world 1"),
|
||||
iisnodeassert.get(2000, "/125_node_config_autoupdate/hello.js", 200, "Hello, world 1"),
|
||||
iisnodeassert.get(2000, "/125_node_config_autoupdate/hello.js", 200, "Hello, world 2"),
|
||||
iisnodeassert.get(2000, "/125_node_config_autoupdate/hello.js", 200, "Hello, world 2"),
|
||||
iisnodeassert.get(2000, "/125_node_config_autoupdate/hello.js", 200, "Hello, world 2"),
|
||||
iisnodeassert.get(2000, "/125_node_config_autoupdate/hello.js", 200, "Hello, world 2")
|
||||
]);
|
|
@ -0,0 +1,8 @@
|
|||
var http = require('http');
|
||||
|
||||
var n = 1;
|
||||
|
||||
http.createServer(function (req, res) {
|
||||
res.writeHead(200, {'Content-Type': 'text/html'});
|
||||
res.end('Hello, world ' + n++);
|
||||
}).listen(process.env.PORT);
|
|
@ -0,0 +1 @@
|
|||
nodeProcessCountPerApplication: 4
|
|
@ -0,0 +1,8 @@
|
|||
<configuration>
|
||||
<system.webServer>
|
||||
<handlers>
|
||||
<add name="iisnode" path="hello.js" verb="*" modules="iisnode" />
|
||||
</handlers>
|
||||
<iisnode nodeProcessCountPerApplication="2" />
|
||||
</system.webServer>
|
||||
</configuration>
|
|
@ -0,0 +1 @@
|
|||
node.config
|
|
@ -0,0 +1,19 @@
|
|||
var http = require('http');
|
||||
|
||||
var n = 1;
|
||||
|
||||
http.createServer(function (req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/html' });
|
||||
var query = require('url').parse(req.url).query;
|
||||
if (query === 'update') {
|
||||
require('fs').writeFileSync(require('path').resolve(__dirname, 'node.config'), 'nodeProcessCountPerApplication: 4');
|
||||
res.end('node.config updated');
|
||||
}
|
||||
else if (query === 'revert') {
|
||||
require('fs').writeFileSync(require('path').resolve(__dirname, 'node.config'), 'nodeProcessCountPerApplication: 2');
|
||||
res.end('node.config reverted');
|
||||
}
|
||||
else {
|
||||
res.end('Hello, world ' + n++);
|
||||
}
|
||||
}).listen(process.env.PORT);
|
|
@ -0,0 +1,8 @@
|
|||
<configuration>
|
||||
<system.webServer>
|
||||
<handlers>
|
||||
<add name="iisnode" path="hello.js" verb="*" modules="iisnode" />
|
||||
</handlers>
|
||||
<iisnode nodeProcessCountPerApplication="2" />
|
||||
</system.webServer>
|
||||
</configuration>
|
Загрузка…
Ссылка в новой задаче