fix: address network probe code quality issues

- Use shared http.Client in ProbeManager to avoid connection/transport leak
- Skip probe goroutine and agent request when system has no enabled probes
- Validate HTTP probe target URL scheme (http:// or https://) on creation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
xiaomiku01
2026-04-11 18:40:27 +08:00
parent 50f8548479
commit 9a5959b57e
5 changed files with 24 additions and 15 deletions

View File

@@ -16,6 +16,7 @@ import (
type ProbeManager struct { type ProbeManager struct {
mu sync.RWMutex mu sync.RWMutex
probes map[string]*probeTask // key = probe.Config.Key() probes map[string]*probeTask // key = probe.Config.Key()
httpClient *http.Client
} }
type probeTask struct { type probeTask struct {
@@ -33,6 +34,7 @@ type probeSample struct {
func newProbeManager() *ProbeManager { func newProbeManager() *ProbeManager {
return &ProbeManager{ return &ProbeManager{
probes: make(map[string]*probeTask), probes: make(map[string]*probeTask),
httpClient: &http.Client{Timeout: 10 * time.Second},
} }
} }
@@ -168,7 +170,7 @@ func (pm *ProbeManager) executeProbe(task *probeTask) {
case "tcp": case "tcp":
latencyMs = probeTCP(task.config.Target, task.config.Port) latencyMs = probeTCP(task.config.Target, task.config.Port)
case "http": case "http":
latencyMs = probeHTTP(task.config.Target) latencyMs = probeHTTP(pm.httpClient, task.config.Target)
default: default:
slog.Warn("unknown probe protocol", "protocol", task.config.Protocol) slog.Warn("unknown probe protocol", "protocol", task.config.Protocol)
return return
@@ -212,8 +214,7 @@ func probeTCP(target string, port uint16) float64 {
} }
// probeHTTP measures HTTP GET request latency. Returns -1 on failure. // probeHTTP measures HTTP GET request latency. Returns -1 on failure.
func probeHTTP(url string) float64 { func probeHTTP(client *http.Client, url string) float64 {
client := &http.Client{Timeout: 10 * time.Second}
start := time.Now() start := time.Now()
resp, err := client.Get(url) resp, err := client.Get(url)
if err != nil { if err != nil {

View File

@@ -3,6 +3,7 @@ package hub
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"strings"
"github.com/pocketbase/dbx" "github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/core"
@@ -75,6 +76,9 @@ func (h *Hub) createNetworkProbe(e *core.RequestEvent) error {
if req.Protocol != "icmp" && req.Protocol != "tcp" && req.Protocol != "http" { if req.Protocol != "icmp" && req.Protocol != "tcp" && req.Protocol != "http" {
return e.BadRequestError("protocol must be icmp, tcp, or http", nil) return e.BadRequestError("protocol must be icmp, tcp, or http", nil)
} }
if req.Protocol == "http" && !strings.HasPrefix(req.Target, "http://") && !strings.HasPrefix(req.Target, "https://") {
return e.BadRequestError("http probe target must start with http:// or https://", nil)
}
if req.Interval <= 0 { if req.Interval <= 0 {
req.Interval = 10 req.Interval = 10
} }

View File

@@ -169,7 +169,9 @@ func (sys *System) update() error {
} }
// Fetch and save network probe results // Fetch and save network probe results
if sys.hasEnabledProbes() {
go sys.fetchAndSaveProbeResults() go sys.fetchAndSaveProbeResults()
}
return err return err
} }

View File

@@ -27,17 +27,17 @@ func (sys *System) FetchNetworkProbeResults() (map[string]probe.Result, error) {
return results, err return results, err
} }
// hasEnabledProbes returns true if this system has any enabled network probes.
func (sys *System) hasEnabledProbes() bool {
count, err := sys.manager.hub.CountRecords("network_probes",
dbx.NewExp("system = {:system} AND enabled = true", dbx.Params{"system": sys.Id}))
return err == nil && count > 0
}
// fetchAndSaveProbeResults fetches probe results and saves them to the database. // fetchAndSaveProbeResults fetches probe results and saves them to the database.
func (sys *System) fetchAndSaveProbeResults() { func (sys *System) fetchAndSaveProbeResults() {
hub := sys.manager.hub hub := sys.manager.hub
// Check if this system has any probes
count, err := hub.CountRecords("network_probes",
dbx.NewExp("system = {:system} AND enabled = true", dbx.Params{"system": sys.Id}))
if err != nil || count == 0 {
return
}
results, err := sys.FetchNetworkProbeResults() results, err := sys.FetchNetworkProbeResults()
if err != nil || len(results) == 0 { if err != nil || len(results) == 0 {
return return

View File

@@ -168,9 +168,11 @@ func (sm *SystemManager) fetchRealtimeDataAndNotify() {
Containers: data.Containers, Containers: data.Containers,
} }
// Fetch network probe results (lightweight in-memory read on agent) // Fetch network probe results (lightweight in-memory read on agent)
if sys.hasEnabledProbes() {
if probes, err := sys.FetchNetworkProbeResults(); err == nil && len(probes) > 0 { if probes, err := sys.FetchNetworkProbeResults(); err == nil && len(probes) > 0 {
payload.Probes = probes payload.Probes = probes
} }
}
bytes, err := json.Marshal(payload) bytes, err := json.Marshal(payload)
if err == nil { if err == nil {
notify(sm.hub, info.subscription, bytes) notify(sm.hub, info.subscription, bytes)