Browse Source

fix: filter out nginx var targets in proxy parsing

Jacky 1 day ago
parent
commit
ab0fc97d1a
2 changed files with 89 additions and 13 deletions
  1. 11 7
      internal/upstream/proxy_parser.go
  2. 78 6
      internal/upstream/proxy_parser_test.go

+ 11 - 7
internal/upstream/proxy_parser.go

@@ -106,15 +106,14 @@ func parseLocationProxyPass(content string) []ProxyTarget {
 func parseProxyPassURL(proxyPass string) ProxyTarget {
 	proxyPass = strings.TrimSpace(proxyPass)
 
+	// Skip URLs that contain Nginx variables
+	if strings.Contains(proxyPass, "$") {
+		return ProxyTarget{}
+	}
+
 	// Handle HTTP/HTTPS URLs (e.g., "http://backend")
 	if strings.HasPrefix(proxyPass, "http://") || strings.HasPrefix(proxyPass, "https://") {
-		// Handle URLs with nginx variables by extracting the base URL before variables
-		baseURL := proxyPass
-		if dollarIndex := strings.Index(proxyPass, "$"); dollarIndex != -1 {
-			baseURL = proxyPass[:dollarIndex]
-		}
-
-		if parsedURL, err := url.Parse(baseURL); err == nil {
+		if parsedURL, err := url.Parse(proxyPass); err == nil {
 			host := parsedURL.Hostname()
 			port := parsedURL.Port()
 
@@ -168,6 +167,11 @@ func parseServerAddress(serverAddr string, targetType string) ProxyTarget {
 
 	addr := parts[0]
 
+	// Check if the address contains Nginx variables - skip if it does
+	if strings.Contains(addr, "$") {
+		return ProxyTarget{}
+	}
+
 	// Handle IPv6 addresses
 	if strings.HasPrefix(addr, "[") {
 		// IPv6 format: [::1]:8080

+ 78 - 6
internal/upstream/proxy_parser_test.go

@@ -364,10 +364,9 @@ server {
 
 	// Expected targets:
 	// - 1 upstream server from my-tcp
-	// - 1 proxy_pass target (example.com with variable port should still be parsed)
+	// - proxy_pass example.com:$server_port should be filtered out due to Nginx variable
 	expectedTargets := []ProxyTarget{
 		{Host: "127.0.0.1", Port: "9000", Type: "upstream"},
-		{Host: "example.com", Port: "$server_port", Type: "proxy_pass"},
 	}
 
 	if len(targets) != len(expectedTargets) {
@@ -499,16 +498,13 @@ server {
 	// Expected targets:
 	// - 2 upstream servers from backend_api
 	// - 2 upstream servers from backend_ws
-	// - 1 direct proxy_pass (external.example.com:8443)
-	// - 1 direct proxy_pass (static.example.com:80)
 	// - proxy_pass with upstream references should be ignored
+	// - proxy_pass with variables should be filtered out
 	expectedTargets := []ProxyTarget{
 		{Host: "api1.example.com", Port: "8080", Type: "upstream"},
 		{Host: "api2.example.com", Port: "8080", Type: "upstream"},
 		{Host: "ws1.example.com", Port: "9000", Type: "upstream"},
 		{Host: "ws2.example.com", Port: "9000", Type: "upstream"},
-		{Host: "external.example.com", Port: "8443", Type: "proxy_pass"},
-		{Host: "static.example.com", Port: "80", Type: "proxy_pass"},
 	}
 
 	if len(targets) != len(expectedTargets) {
@@ -533,3 +529,79 @@ server {
 		}
 	}
 }
+
+func TestFilterNginxVariables(t *testing.T) {
+	config := `upstream backend {
+    server 192.168.1.100:8080;
+    server api.example.com:$custom_port;  # Should be filtered out
+    server $backend_host:9000;            # Should be filtered out
+}
+
+server {
+    listen 80;
+    location /api {
+        proxy_pass http://api.example.com:$server_port/;  # Should be filtered out
+    }
+    location /static {
+        proxy_pass http://static.example.com:8080/;       # Should be kept
+    }
+}
+
+server {
+    listen 3306;
+    proxy_pass mysql.$domain:3306;        # Should be filtered out
+}
+
+server {
+    listen 5432;
+    proxy_pass postgres.example.com:5432; # Should be kept
+}`
+
+	targets := ParseProxyTargetsFromRawContent(config)
+
+	// Print actual results for debugging
+	t.Logf("Found %d targets:", len(targets))
+	for i, target := range targets {
+		t.Logf("Target %d: Host=%s, Port=%s, Type=%s", i+1, target.Host, target.Port, target.Type)
+	}
+
+	// Expected targets: only those without Nginx variables
+	expectedTargets := []ProxyTarget{
+		{Host: "192.168.1.100", Port: "8080", Type: "upstream"},
+		{Host: "static.example.com", Port: "8080", Type: "proxy_pass"},
+		{Host: "postgres.example.com", Port: "5432", Type: "proxy_pass"},
+	}
+
+	if len(targets) != len(expectedTargets) {
+		t.Errorf("Expected %d targets, got %d", len(expectedTargets), len(targets))
+		return
+	}
+
+	// Create a map for easier comparison
+	targetMap := make(map[string]ProxyTarget)
+	for _, target := range targets {
+		key := target.Host + ":" + target.Port + ":" + target.Type
+		targetMap[key] = target
+	}
+
+	for _, expected := range expectedTargets {
+		key := expected.Host + ":" + expected.Port + ":" + expected.Type
+		if _, found := targetMap[key]; !found {
+			t.Errorf("Expected target not found: %+v", expected)
+		}
+	}
+
+	// Verify that targets with variables are filtered out
+	variableTargets := []string{
+		"api.example.com:$custom_port:upstream",
+		"$backend_host:9000:upstream",
+		"api.example.com:$server_port:proxy_pass",
+		"mysql.$domain:3306:proxy_pass",
+	}
+
+	for _, varTarget := range variableTargets {
+		if _, found := targetMap[varTarget]; found {
+			t.Errorf("Variable target should have been filtered out but was found: %s", varTarget)
+		}
+	}
+}