fix #158: enable YAML node.config with iisnode settings

This commit is contained in:
Tomasz Janczuk 2012-05-04 14:20:06 -07:00
Родитель 8df17e4dfa
Коммит 66ab0abb69
25 изменённых файлов: 1079 добавлений и 116 удалений

Просмотреть файл

@ -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, &timestamp)
&& 0 != memcmp(&timestamp, &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, &timestamp, 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-&lt;server_variable_name&gt; 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 &lt;key&gt;: &lt;value&gt; 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
--&gt;
&lt;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"
/&gt;
&lt;!--
@ -171,5 +181,145 @@ console.log('Application started at location ' + process.env.PORT);</pre>
&lt;/system.webServer&gt;
&lt;/configuration&gt;</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-&lt;server_variable_name&gt;
# 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>

1
test/functional/www/125_node_config_autoupdate/.gitignore поставляемый Normal file
Просмотреть файл

@ -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>