11package nginx
22
33import (
4+ "fmt"
45 "os"
56 "regexp"
67 "strings"
@@ -97,26 +98,114 @@ func updateDynamicModulesStatus() {
9798 return
9899 }
99100
100- // Regular expression to find loaded dynamic modules in nginx -T output
101- // Look for lines like "load_module modules/ngx_http_image_filter_module.so;"
102- loadModuleRe := regexp .MustCompile (`load_module\s+(?:modules/|/.*/)([a-zA-Z0-9_-]+)\.so;` )
101+ // Use the shared regex function to find loaded dynamic modules
102+ loadModuleRe := GetLoadModuleRegex ()
103103 matches := loadModuleRe .FindAllStringSubmatch (out , - 1 )
104104
105105 for _ , match := range matches {
106106 if len (match ) > 1 {
107- // Extract the module name without path and suffix
108- moduleName := match [1 ]
109- // Some normalization to match format in GetModules
110- moduleName = strings . TrimPrefix ( moduleName , "ngx_" )
111- moduleName = strings . TrimSuffix ( moduleName , "_module" )
112- module , ok := modulesCache .Get (moduleName )
107+ // Extract the module name from load_module statement and normalize it
108+ loadModuleName := match [1 ]
109+ normalizedName := normalizeModuleNameFromLoadModule ( loadModuleName )
110+
111+ // Try to find the module in our cache using the normalized name
112+ module , ok := modulesCache .Get (normalizedName )
113113 if ok {
114114 module .Loaded = true
115115 }
116116 }
117117 }
118118}
119119
120+ // GetLoadModuleRegex returns a compiled regular expression to match nginx load_module statements.
121+ // It matches both quoted and unquoted module paths:
122+ // - load_module "/usr/local/nginx/modules/ngx_stream_module.so";
123+ // - load_module modules/ngx_http_upstream_fair_module.so;
124+ //
125+ // The regex captures the module name (without path and extension).
126+ func GetLoadModuleRegex () * regexp.Regexp {
127+ // Pattern explanation:
128+ // load_module\s+ - matches "load_module" followed by whitespace
129+ // "? - optional opening quote
130+ // (?:[^"\s]+/)? - non-capturing group for optional path (any non-quote, non-space chars ending with /)
131+ // ([a-zA-Z0-9_-]+) - capturing group for module name
132+ // \.so - matches ".so" extension
133+ // "? - optional closing quote
134+ // \s*; - optional whitespace followed by semicolon
135+ return regexp .MustCompile (`load_module\s+"?(?:[^"\s]+/)?([a-zA-Z0-9_-]+)\.so"?\s*;` )
136+ }
137+
138+ // normalizeModuleNameFromLoadModule converts a module name from load_module statement
139+ // to match the format used in configure arguments.
140+ // Examples:
141+ // - "ngx_stream_module" -> "stream"
142+ // - "ngx_http_geoip_module" -> "http_geoip"
143+ // - "ngx_stream_geoip_module" -> "stream_geoip"
144+ // - "ngx_http_image_filter_module" -> "http_image_filter"
145+ func normalizeModuleNameFromLoadModule (moduleName string ) string {
146+ // Remove "ngx_" prefix if present
147+ normalized := strings .TrimPrefix (moduleName , "ngx_" )
148+
149+ // Remove "_module" suffix if present
150+ normalized = strings .TrimSuffix (normalized , "_module" )
151+
152+ return normalized
153+ }
154+
155+ // normalizeModuleNameFromConfigure converts a module name from configure arguments
156+ // to a consistent format for internal use.
157+ // Examples:
158+ // - "stream" -> "stream"
159+ // - "http_geoip_module" -> "http_geoip"
160+ // - "http_image_filter_module" -> "http_image_filter"
161+ func normalizeModuleNameFromConfigure (moduleName string ) string {
162+ // Remove "_module" suffix if present to keep consistent format
163+ normalized := strings .TrimSuffix (moduleName , "_module" )
164+
165+ return normalized
166+ }
167+
168+ // getExpectedLoadModuleName converts a configure argument module name
169+ // to the expected load_module statement module name.
170+ // Examples:
171+ // - "stream" -> "ngx_stream_module"
172+ // - "http_geoip" -> "ngx_http_geoip_module"
173+ // - "stream_geoip" -> "ngx_stream_geoip_module"
174+ func getExpectedLoadModuleName (configureModuleName string ) string {
175+ normalized := normalizeModuleNameFromConfigure (configureModuleName )
176+ return "ngx_" + normalized + "_module"
177+ }
178+
179+ // GetModuleMapping returns a map showing the relationship between different module name formats.
180+ // This is useful for debugging and understanding how module names are processed.
181+ // Returns a map with normalized names as keys and mapping info as values.
182+ func GetModuleMapping () map [string ]map [string ]string {
183+ modules := GetModules ()
184+ mapping := make (map [string ]map [string ]string )
185+
186+ modulesCacheLock .RLock ()
187+ defer modulesCacheLock .RUnlock ()
188+
189+ // Use AllFromFront() to iterate through the ordered map
190+ for normalizedName , module := range modules .AllFromFront () {
191+ if module == nil {
192+ continue
193+ }
194+
195+ expectedLoadName := getExpectedLoadModuleName (normalizedName )
196+
197+ mapping [normalizedName ] = map [string ]string {
198+ "normalized" : normalizedName ,
199+ "expected_load_module" : expectedLoadName ,
200+ "dynamic" : fmt .Sprintf ("%t" , module .Dynamic ),
201+ "loaded" : fmt .Sprintf ("%t" , module .Loaded ),
202+ "params" : module .Params ,
203+ }
204+ }
205+
206+ return mapping
207+ }
208+
120209func GetModules () * orderedmap.OrderedMap [string , * Module ] {
121210 modulesCacheLock .RLock ()
122211 cachedModules := modulesCache
@@ -165,6 +254,9 @@ func GetModules() *orderedmap.OrderedMap[string, *Module] {
165254 continue
166255 }
167256
257+ // Normalize the module name for consistent internal representation
258+ normalizedModuleName := normalizeModuleNameFromConfigure (module )
259+
168260 // Determine if the module is dynamic
169261 isDynamic := false
170262 if strings .Contains (out , "--with-" + module + "=dynamic" ) ||
@@ -176,8 +268,8 @@ func GetModules() *orderedmap.OrderedMap[string, *Module] {
176268 params = ""
177269 }
178270
179- modulesCache .Set (module , & Module {
180- Name : module ,
271+ modulesCache .Set (normalizedModuleName , & Module {
272+ Name : normalizedModuleName ,
181273 Params : params ,
182274 Dynamic : isDynamic ,
183275 Loaded : ! isDynamic , // Static modules are always loaded
0 commit comments