|
@@ -0,0 +1,199 @@
|
|
|
+package nginx
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "io"
|
|
|
+ "net/http"
|
|
|
+ "os"
|
|
|
+ "regexp"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+ "time"
|
|
|
+
|
|
|
+ "github.com/pkg/errors"
|
|
|
+)
|
|
|
+
|
|
|
+// StubStatusInfo Store the stub_status module status
|
|
|
+type StubStatusInfo struct {
|
|
|
+ Enabled bool `json:"stub_status_enabled"` // stub_status module is enabled
|
|
|
+ URL string `json:"stub_status_url"` // stub_status access address
|
|
|
+}
|
|
|
+
|
|
|
+type StubStatusData struct {
|
|
|
+ Active int `json:"active"`
|
|
|
+ Accepts int `json:"accepts"`
|
|
|
+ Handled int `json:"handled"`
|
|
|
+ Requests int `json:"requests"`
|
|
|
+ Reading int `json:"reading"`
|
|
|
+ Writing int `json:"writing"`
|
|
|
+ Waiting int `json:"waiting"`
|
|
|
+}
|
|
|
+
|
|
|
+const (
|
|
|
+ StubStatusPort = 51828
|
|
|
+ StubStatusPath = "/stub_status"
|
|
|
+ StubStatusHost = "localhost"
|
|
|
+ StubStatusProtocol = "http"
|
|
|
+ StubStatusAllow = "127.0.0.1"
|
|
|
+ StubStatusDeny = "all"
|
|
|
+ StubStatusConfigName = "stub_status_nginx-ui.conf"
|
|
|
+)
|
|
|
+
|
|
|
+// GetStubStatusData Get the stub_status module data
|
|
|
+func GetStubStatusData() (bool, *StubStatusData, error) {
|
|
|
+ result := &StubStatusData{
|
|
|
+ Active: 0,
|
|
|
+ Accepts: 0,
|
|
|
+ Handled: 0,
|
|
|
+ Requests: 0,
|
|
|
+ Reading: 0,
|
|
|
+ Writing: 0,
|
|
|
+ Waiting: 0,
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get the stub_status status information
|
|
|
+ enabled, statusURL := IsStubStatusEnabled()
|
|
|
+ if !enabled {
|
|
|
+ return false, result, fmt.Errorf("stub_status is not enabled")
|
|
|
+ }
|
|
|
+
|
|
|
+ // Create an HTTP client
|
|
|
+ client := &http.Client{
|
|
|
+ Timeout: 5 * time.Second,
|
|
|
+ }
|
|
|
+
|
|
|
+ // Send a request to get the stub_status data
|
|
|
+ resp, err := client.Get(statusURL)
|
|
|
+ if err != nil {
|
|
|
+ return enabled, result, fmt.Errorf("failed to get stub status: %v", err)
|
|
|
+ }
|
|
|
+ defer resp.Body.Close()
|
|
|
+
|
|
|
+ // Read the response content
|
|
|
+ body, err := io.ReadAll(resp.Body)
|
|
|
+ if err != nil {
|
|
|
+ return enabled, result, fmt.Errorf("failed to read response body: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Parse the response content
|
|
|
+ statusContent := string(body)
|
|
|
+
|
|
|
+ // Match the active connection number
|
|
|
+ activeRe := regexp.MustCompile(`Active connections:\s+(\d+)`)
|
|
|
+ if matches := activeRe.FindStringSubmatch(statusContent); len(matches) > 1 {
|
|
|
+ result.Active, _ = strconv.Atoi(matches[1])
|
|
|
+ }
|
|
|
+
|
|
|
+ // Match the request statistics information
|
|
|
+ serverRe := regexp.MustCompile(`(\d+)\s+(\d+)\s+(\d+)`)
|
|
|
+ if matches := serverRe.FindStringSubmatch(statusContent); len(matches) > 3 {
|
|
|
+ result.Accepts, _ = strconv.Atoi(matches[1])
|
|
|
+ result.Handled, _ = strconv.Atoi(matches[2])
|
|
|
+ result.Requests, _ = strconv.Atoi(matches[3])
|
|
|
+ }
|
|
|
+
|
|
|
+ // Match the read and write waiting numbers
|
|
|
+ connRe := regexp.MustCompile(`Reading:\s+(\d+)\s+Writing:\s+(\d+)\s+Waiting:\s+(\d+)`)
|
|
|
+ if matches := connRe.FindStringSubmatch(statusContent); len(matches) > 3 {
|
|
|
+ result.Reading, _ = strconv.Atoi(matches[1])
|
|
|
+ result.Writing, _ = strconv.Atoi(matches[2])
|
|
|
+ result.Waiting, _ = strconv.Atoi(matches[3])
|
|
|
+ }
|
|
|
+
|
|
|
+ return enabled, result, nil
|
|
|
+}
|
|
|
+
|
|
|
+// GetStubStatus Get the stub_status module status
|
|
|
+func GetStubStatus() *StubStatusInfo {
|
|
|
+ enabled, statusURL := IsStubStatusEnabled()
|
|
|
+ return &StubStatusInfo{
|
|
|
+ Enabled: enabled,
|
|
|
+ URL: statusURL,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// IsStubStatusEnabled Check if the stub_status module is enabled and return the access address
|
|
|
+// Only check the stub_status_nginx-ui.conf configuration file
|
|
|
+func IsStubStatusEnabled() (bool, string) {
|
|
|
+ stubStatusConfPath := GetConfPath("conf.d", StubStatusConfigName)
|
|
|
+ if _, err := os.Stat(stubStatusConfPath); os.IsNotExist(err) {
|
|
|
+ return false, ""
|
|
|
+ }
|
|
|
+
|
|
|
+ ngxConfig, err := ParseNgxConfig(stubStatusConfPath)
|
|
|
+ if err != nil {
|
|
|
+ return false, ""
|
|
|
+ }
|
|
|
+
|
|
|
+ // Find the stub_status configuration
|
|
|
+ for _, server := range ngxConfig.Servers {
|
|
|
+ protocol := StubStatusProtocol
|
|
|
+ host := StubStatusHost
|
|
|
+ port := strconv.Itoa(StubStatusPort)
|
|
|
+
|
|
|
+ for _, location := range server.Locations {
|
|
|
+ // Check if the location content contains stub_status
|
|
|
+ if strings.Contains(location.Content, "stub_status") {
|
|
|
+ stubStatusURL := fmt.Sprintf("%s://%s:%s%s", protocol, host, port, StubStatusPath)
|
|
|
+ return true, stubStatusURL
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false, ""
|
|
|
+}
|
|
|
+
|
|
|
+// EnableStubStatus Enable stub_status module
|
|
|
+func EnableStubStatus() error {
|
|
|
+ enabled, _ := IsStubStatusEnabled()
|
|
|
+ if enabled {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ return CreateStubStatusConfig()
|
|
|
+}
|
|
|
+
|
|
|
+// DisableStubStatus Disable stub_status module
|
|
|
+func DisableStubStatus() error {
|
|
|
+ stubStatusConfPath := GetConfPath("conf.d", StubStatusConfigName)
|
|
|
+ if _, err := os.Stat(stubStatusConfPath); os.IsNotExist(err) {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ return os.Remove(stubStatusConfPath)
|
|
|
+}
|
|
|
+
|
|
|
+// CreateStubStatusConfig Create a new stub_status configuration file
|
|
|
+func CreateStubStatusConfig() error {
|
|
|
+ httpConfPath := GetConfPath("conf.d", StubStatusConfigName)
|
|
|
+
|
|
|
+ stubStatusConfig := `
|
|
|
+# DO NOT EDIT THIS FILE, IT IS AUTO GENERATED BY NGINX-UI
|
|
|
+# Nginx stub_status configuration for Nginx-UI
|
|
|
+# Modified at ` + time.Now().Format("2006-01-02 15:04:05") + `
|
|
|
+
|
|
|
+server {
|
|
|
+ listen 51828; # Use non-standard port to avoid conflicts
|
|
|
+ server_name localhost;
|
|
|
+
|
|
|
+ # Status monitoring interface
|
|
|
+ location /stub_status {
|
|
|
+ stub_status;
|
|
|
+ allow 127.0.0.1; # Only allow local access
|
|
|
+ deny all;
|
|
|
+ }
|
|
|
+}
|
|
|
+`
|
|
|
+ ngxConfig, err := ParseNgxConfigByContent(stubStatusConfig)
|
|
|
+ if err != nil {
|
|
|
+ return errors.Wrap(err, "failed to parse new nginx config")
|
|
|
+ }
|
|
|
+
|
|
|
+ ngxConfig.FileName = httpConfPath
|
|
|
+ configText, err := ngxConfig.BuildConfig()
|
|
|
+ if err != nil {
|
|
|
+ return errors.Wrap(err, "failed to build nginx config")
|
|
|
+ }
|
|
|
+
|
|
|
+ return os.WriteFile(httpConfPath, []byte(configText), 0644)
|
|
|
+}
|