[mapguide-commits] r10219 - in branches/4.0/MgDev: Common/Foundation/System Web/src/HttpHandler Web/src/IsapiAgent Web/src/WebSupport
svn_mapguide at osgeo.org
svn_mapguide at osgeo.org
Mon Oct 19 09:06:15 PDT 2026
Author: jng
Date: 2026-10-19 09:06:14 -0700 (Mon, 19 Oct 2026)
New Revision: 10219
Modified:
branches/4.0/MgDev/Common/Foundation/System/Configuration.cpp
branches/4.0/MgDev/Common/Foundation/System/Configuration.h
branches/4.0/MgDev/Common/Foundation/System/ConfigurationHeap.cpp
branches/4.0/MgDev/Web/src/HttpHandler/HttpRequest.cpp
branches/4.0/MgDev/Web/src/HttpHandler/HttpRequestResponseHandler.cpp
branches/4.0/MgDev/Web/src/IsapiAgent/IsapiAgent.cpp
branches/4.0/MgDev/Web/src/WebSupport/InitializeWebTier.cpp
Log:
Fix web tier initialisation failures leaving the process permanently broken
A failed MgInitializeWebTier() call poisoned the process-wide MgConfiguration
singleton for the life of the process. LoadConfiguration() opens the ACE
configuration heap before importing the file, and ACE_Configuration_Heap::open()
can only succeed once (EBUSY afterwards). When the import failed the heap was
left open with m_fileLoaded still false, and every later attempt took the EBUSY
branch, which LoadConfiguration() merely logged - reporting success without
loading anything. MgInitializeWebTierInternal() then carried on with default
values and latched itself as initialised, so only a process restart could clear
the state.
Symptoms this fixes:
* The MapAgent (ISAPI) failing every request, in MapAgentCommon::LogRequest()
at MgConfiguration::GetBoolValue(), until the application pool was recycled.
* "Failed to load the resource file Resources/mapguide_en.res for locale en"
from every call after a failed first call, because ResourcesPath fell back
to its default relative path.
* Coordinate system and resource failures being reported from a web tier that
claimed to be initialised.
Changes:
* Common/Foundation/System/Configuration.*: report a failure to open the ACE
heap as well as a failure to import the file, and add DiscardInstance() so a
caller can drop an instance whose heap can never be re-opened.
* Common/Foundation/System/ConfigurationHeap.cpp: do not index the section
collection with a stale -1 when ACE resolves a section name that IndexOf()
did not find. ACE compares section names case-insensitively and IndexOf()
does not, so a case-different section name in webconfig.ini threw
MgIndexOutOfRangeException out of MgConfiguration::GetValue().
* Web/src/WebSupport/InitializeWebTier.cpp: discard the configuration instance
when the load throws, so a retry reloads instead of reusing a half-open heap;
discard it from MgUninitializeWebTier() so a re-initialisation reloads
instead of reusing a heap built on ACE state that ACE::fini() has torn down;
and restore the throws for the coordinate system initialisation failures,
which had been reduced to a bare `new MgException(...)` (leaked, not thrown)
and, for a NULL catalog, let execution carry on to dereference that NULL.
* Web/src/IsapiAgent/IsapiAgent.cpp: APPL_PHYSICAL_PATH was read into an
uninitialised buffer with the GetServerVariable() result ignored, so a
missing variable produced a garbage configuration path and a strlen() over
uninitialised stack. Zero and check the buffer, and fall back to the
extension's own directory - the mapagent application directory the variable
would have named.
* Web/src/HttpHandler/*: restore two more throws of the same flavour. An
unknown OPERATION reached a NULL handler factory function pointer, and
disabling WFS/WMS/OGC via AgentProperties was ignored.
Verified on Windows x64 Release (WebTier.sln, v142 toolset): a first
MgInitializeWebTier() with a missing configuration now throws
MgConfigurationLoadFailedException with the tier left uninitialised, and
subsequent calls with a valid configuration load it and initialise successfully.
Previously they returned normally with no configuration loaded, or failed with
MgFileNotFoundException on Resources/mapguide_en.res. LogRequest's
AgentProperties lookup succeeds afterwards, as does a re-initialisation after
MgUninitializeWebTier().
Fixes #2224
Fixes #2196
Fixes #2401
Modified: branches/4.0/MgDev/Common/Foundation/System/Configuration.cpp
===================================================================
--- branches/4.0/MgDev/Common/Foundation/System/Configuration.cpp 2026-09-22 11:16:06 UTC (rev 10218)
+++ branches/4.0/MgDev/Common/Foundation/System/Configuration.cpp 2026-10-19 16:06:14 UTC (rev 10219)
@@ -71,6 +71,23 @@
return MgConfiguration::sm_configuration;
}
+/// <summary>
+/// Discards the process-wide MgConfiguration, if one exists.
+/// </summary>
+void MgConfiguration::DiscardInstance()
+{
+ // Serialize with GetInstance(). The lock is recursive, so this is safe to call from
+ // within MgInitializeWebTier(), which holds the same lock.
+ ACE_MT (ACE_GUARD (ACE_Recursive_Thread_Mutex, ace_mon, *ACE_Static_Object_Lock::instance ()));
+
+ MG_CONFIGURATION_TRY()
+
+ // Do not assign the instance to a Ptr anywhere else; this is the only owner.
+ MgConfiguration::sm_configuration = (MgConfiguration*)NULL;
+
+ MG_CONFIGURATION_CATCH_AND_THROW(L"MgConfiguration.DiscardInstance")
+}
+
///----------------------------------------------------------------------------
/// <summary>
/// Loads the configuration file specified
@@ -97,25 +114,22 @@
{
m_fileName = fileName;
- if(m_config.Open())
+ if (m_config.Open() && m_config.ImportConfig(fileName))
{
- if(m_config.ImportConfig(fileName))
- {
- m_fileLoaded = true;
- }
- else
- {
- MgStringCollection arguments;
- arguments.Add(fileName);
-
- throw new MgException(MgExceptionCodes::MgConfigurationLoadFailedException,
- L"MgConfiguration.LoadConfiguration",
- __LINE__, __WFILE__, &arguments, L"", NULL);
- }
+ m_fileLoaded = true;
}
else
{
- ACE_ERROR((LM_ERROR, ACE_TEXT("%p\n"), ACE_TEXT("MgConfiguration::LoadConfiguration()")));
+ // Report a failure to open the heap as well as a failure to import the file.
+ // The heap stays open after either failure and cannot be re-opened, so this
+ // instance can never be loaded; callers must discard it (see DiscardInstance)
+ // rather than treat this as a successful load.
+ MgStringCollection arguments;
+ arguments.Add(fileName);
+
+ throw new MgException(MgExceptionCodes::MgConfigurationLoadFailedException,
+ L"MgConfiguration.LoadConfiguration",
+ __LINE__, __WFILE__, &arguments, L"", NULL);
}
}
Modified: branches/4.0/MgDev/Common/Foundation/System/Configuration.h
===================================================================
--- branches/4.0/MgDev/Common/Foundation/System/Configuration.h 2026-09-22 11:16:06 UTC (rev 10218)
+++ branches/4.0/MgDev/Common/Foundation/System/Configuration.h 2026-10-19 16:06:14 UTC (rev 10219)
@@ -83,6 +83,19 @@
static MgConfiguration* GetInstance();
/// \brief
+ /// Discards the process-wide instance, if one exists.
+ ///
+ /// The underlying ACE configuration heap can only be opened once per instance,
+ /// so an instance whose LoadConfiguration() failed can never load a file. Callers
+ /// that want to retry a failed load must discard the instance first, so that the
+ /// next GetInstance() starts from a clean heap.
+ ///
+ /// Do not call this while the instance is in use by another thread.
+ ///
+
+ static void DiscardInstance();
+
+ /// \brief
/// Loads the configuration file specified
///
/// \param fileName
Modified: branches/4.0/MgDev/Common/Foundation/System/ConfigurationHeap.cpp
===================================================================
--- branches/4.0/MgDev/Common/Foundation/System/ConfigurationHeap.cpp 2026-09-22 11:16:06 UTC (rev 10218)
+++ branches/4.0/MgDev/Common/Foundation/System/ConfigurationHeap.cpp 2026-10-19 16:06:14 UTC (rev 10219)
@@ -332,6 +332,15 @@
{
return false;
}
+
+ if (index < 0)
+ {
+ // ACE compares section names case-insensitively, whereas IndexOf() does not,
+ // so ACE can resolve a section this collection has no entry for. Do not index
+ // the collection with the stale -1; report the section as not found instead.
+ return false;
+ }
+
section = m_sectionCollection->GetItem(index);
}
Modified: branches/4.0/MgDev/Web/src/HttpHandler/HttpRequest.cpp
===================================================================
--- branches/4.0/MgDev/Web/src/HttpHandler/HttpRequest.cpp 2026-09-22 11:16:06 UTC (rev 10218)
+++ branches/4.0/MgDev/Web/src/HttpHandler/HttpRequest.cpp 2026-10-19 16:06:14 UTC (rev 10219)
@@ -325,7 +325,7 @@
//find in the map the creator for the http mapping class
MgHttpRequestResponseHandler* (*createFunc)(MgHttpRequest*) = httpClassCreators[OperationName];
if(createFunc == NULL)
- new MgException(MgExceptionCodes::MgInvalidOperationException, L"MgHttpRequest::CreateRequestResponseHandler", __LINE__, __WFILE__, NULL, L"", NULL);
+ throw new MgException(MgExceptionCodes::MgInvalidOperationException, L"MgHttpRequest::CreateRequestResponseHandler", __LINE__, __WFILE__, NULL, L"", NULL);
//instantiate the http class
returnHandler = (*createFunc)(this);
Modified: branches/4.0/MgDev/Web/src/HttpHandler/HttpRequestResponseHandler.cpp
===================================================================
--- branches/4.0/MgDev/Web/src/HttpHandler/HttpRequestResponseHandler.cpp 2026-09-22 11:16:06 UTC (rev 10218)
+++ branches/4.0/MgDev/Web/src/HttpHandler/HttpRequestResponseHandler.cpp 2026-10-19 16:06:14 UTC (rev 10219)
@@ -69,7 +69,7 @@
config->GetBoolValue(MgConfigProperties::AgentPropertiesSection, disableProperty, bDisable, false);
if (bDisable)
{
- new MgException(MgExceptionCodes::MgInvalidOperationException, L"HttpRequestResponseHandler.InitializeCommonParamters", __LINE__, __WFILE__, NULL, L"", NULL);
+ throw new MgException(MgExceptionCodes::MgInvalidOperationException, L"HttpRequestResponseHandler.InitializeCommonParamters", __LINE__, __WFILE__, NULL, L"", NULL);
}
}
Modified: branches/4.0/MgDev/Web/src/IsapiAgent/IsapiAgent.cpp
===================================================================
--- branches/4.0/MgDev/Web/src/IsapiAgent/IsapiAgent.cpp 2026-09-22 11:16:06 UTC (rev 10218)
+++ branches/4.0/MgDev/Web/src/IsapiAgent/IsapiAgent.cpp 2026-10-19 16:06:14 UTC (rev 10219)
@@ -21,6 +21,7 @@
#include "stdafx.h"
#include "httpext.h"
#include "stdio.h"
+#include <stdlib.h>
#include "WebSupport.h"
#include "IsapiPostParser.h"
#include "Base64.h"
@@ -31,10 +32,17 @@
void Initialize(EXTENSION_CONTROL_BLOCK *pECB);
+static STRING GetModuleDirectory();
STRING gConfigPath;
+static HMODULE g_hModule = NULL;
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
+ if (DLL_PROCESS_ATTACH == ul_reason_for_call)
+ {
+ g_hModule = (HMODULE)hModule;
+ }
+
return TRUE;
}
@@ -231,14 +239,23 @@
{
if (!IsWebTierInitialized())
{
+ // GetServerVariable leaves the buffer untouched when the variable is not found,
+ // so it must start out empty and the return value must be checked.
DWORD dwSize = 1024;
- char path[1024];
- pECB->GetServerVariable(pECB->ConnID, (LPSTR)MapAgentStrings::PhysicalPath, path, &dwSize);
+ char path[1024] = {0};
+ BOOL bPhysicalPath = pECB->GetServerVariable(pECB->ConnID, (LPSTR)MapAgentStrings::PhysicalPath, path, &dwSize);
- if (strlen(path) > 0)
+ if (bPhysicalPath && strlen(path) > 0)
{
gConfigPath = MgUtil::MultiByteToWideChar(string(path));
}
+ else
+ {
+ // APPL_PHYSICAL_PATH is not always supplied (IIS 6 does not honour it). The
+ // extension is installed in the mapagent application directory, which is what
+ // the variable would have held, so use this module's own directory instead.
+ gConfigPath = GetModuleDirectory();
+ }
STRING configFile = gConfigPath;
configFile.append(L"\\");
@@ -251,3 +268,31 @@
MG_CATCH_AND_THROW(L"IsapiAgent.Initialize");
}
}
+
+
+// Returns the directory containing this extension, without a trailing separator.
+static STRING GetModuleDirectory()
+{
+ wchar_t modulePath[_MAX_PATH + 1];
+ DWORD length = 0;
+
+ if (NULL != g_hModule)
+ {
+ length = GetModuleFileNameW(g_hModule, modulePath, _MAX_PATH);
+ }
+
+ if (length == 0 || length >= _MAX_PATH)
+ {
+ return L"";
+ }
+
+ wchar_t* lastSeparator = wcsrchr(modulePath, L'\\');
+ if (NULL == lastSeparator)
+ {
+ return L"";
+ }
+
+ *lastSeparator = L'\0';
+
+ return modulePath;
+}
Modified: branches/4.0/MgDev/Web/src/WebSupport/InitializeWebTier.cpp
===================================================================
--- branches/4.0/MgDev/Web/src/WebSupport/InitializeWebTier.cpp 2026-09-22 11:16:06 UTC (rev 10218)
+++ branches/4.0/MgDev/Web/src/WebSupport/InitializeWebTier.cpp 2026-10-19 16:06:14 UTC (rev 10219)
@@ -44,7 +44,19 @@
// Load configuration file
MgConfiguration* config = MgConfiguration::GetInstance();
- config->LoadConfiguration(configFile);
+ try
+ {
+ config->LoadConfiguration(configFile);
+ }
+ catch (...)
+ {
+ // A failed load leaves the ACE configuration heap open, and that heap cannot be
+ // re-opened, so the instance can never be loaded. Discard it so that the next
+ // request retries the initialization instead of running against a configuration
+ // that was never loaded.
+ MgConfiguration::DiscardInstance();
+ throw;
+ }
// Init the Anonymous user check
MgHttpAnonymousCheck* anonCheck = MgHttpAnonymousCheck::GetInstance();
@@ -87,7 +99,7 @@
//Check catalog
if (NULL == csCatalog.p)
{
- new MgException(MgExceptionCodes::MgCoordinateSystemInitializationFailedException,
+ throw new MgException(MgExceptionCodes::MgCoordinateSystemInitializationFailedException,
L"MgInitializeWebTierInternal", __LINE__, __WFILE__, NULL, L"", NULL);
}
@@ -107,7 +119,7 @@
LibraryStatus status = csCatalog->GetLibraryStatus();
if(lsInitialized != status)
{
- new MgException(MgExceptionCodes::MgCoordinateSystemInitializationFailedException,
+ throw new MgException(MgExceptionCodes::MgCoordinateSystemInitializationFailedException,
L"MgInitializeWebTierInternal", __LINE__, __WFILE__, NULL, L"", NULL);
}
@@ -137,6 +149,11 @@
ACE_OS::sleep(2);
}
+ // The configuration heap cannot be re-opened once opened, and it is built on ACE state
+ // that is about to be torn down, so drop it. A later MgInitializeWebTier() then loads
+ // the configuration again, exactly as it does in a fresh process.
+ MgConfiguration::DiscardInstance();
+
// Uninitialize ACE
ACE::fini();
More information about the mapguide-commits
mailing list