various: reduce file I/O & pre-parse CIDRs

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I288c299d35fdc833c802e22682f14b8e6a6a6964
This commit is contained in:
raf 2026-03-07 12:12:37 +03:00
commit 0f38a062e9
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
2 changed files with 66 additions and 52 deletions

View file

@ -25,18 +25,23 @@ type versionInfo struct {
BuildDate string `json:"buildDate"` BuildDate string `json:"buildDate"`
} }
func getVersionInfo() versionInfo {
data, err := os.ReadFile("version.json")
if err != nil {
return versionInfo{}
}
var v versionInfo
if err := json.Unmarshal(data, &v); err != nil {
return versionInfo{}
}
return v
}
func getVersion() string { func getVersion() string {
if version != "" { if version != "" {
return version return version
} }
data, err := os.ReadFile("version.json") v := getVersionInfo()
if err != nil {
return "dev"
}
var v versionInfo
if err := json.Unmarshal(data, &v); err != nil {
return "dev"
}
if v.Version != "" { if v.Version != "" {
return v.Version return v.Version
} }
@ -47,14 +52,7 @@ func getCommit() string {
if commit != "" { if commit != "" {
return commit return commit
} }
data, err := os.ReadFile("version.json") v := getVersionInfo()
if err != nil {
return "none"
}
var v versionInfo
if err := json.Unmarshal(data, &v); err != nil {
return "none"
}
if v.Commit != "" { if v.Commit != "" {
return v.Commit return v.Commit
} }

View file

@ -5,6 +5,7 @@ import (
"net" "net"
"net/http" "net/http"
"strings" "strings"
"time"
"notashelf.dev/watchdog/internal/aggregate" "notashelf.dev/watchdog/internal/aggregate"
"notashelf.dev/watchdog/internal/config" "notashelf.dev/watchdog/internal/config"
@ -14,14 +15,16 @@ import (
// Handles incoming analytics events // Handles incoming analytics events
type IngestionHandler struct { type IngestionHandler struct {
cfg *config.Config cfg *config.Config
domainMap map[string]bool // O(1) domain validation domainMap map[string]bool // O(1) domain validation
pathNorm *normalize.PathNormalizer corsOriginMap map[string]bool // O(1) CORS origin validation
pathRegistry *aggregate.PathRegistry pathNorm *normalize.PathNormalizer
refRegistry *normalize.ReferrerRegistry pathRegistry *aggregate.PathRegistry
metricsAgg *aggregate.MetricsAggregator refRegistry *normalize.ReferrerRegistry
rateLimiter *ratelimit.TokenBucket metricsAgg *aggregate.MetricsAggregator
rng *rand.Rand rateLimiter *ratelimit.TokenBucket
rng *rand.Rand
trustedNetworks []*net.IPNet // Pre-parsed CIDR networks
} }
// Creates a new ingestion handler // Creates a new ingestion handler
@ -47,15 +50,40 @@ func NewIngestionHandler(
domainMap[domain] = true domainMap[domain] = true
} }
// Build CORS origin map for O(1) lookup
corsOriginMap := make(map[string]bool, len(cfg.Security.CORS.AllowedOrigins))
for _, origin := range cfg.Security.CORS.AllowedOrigins {
corsOriginMap[origin] = true
}
// Pre-parse trusted proxy CIDRs to avoid re-parsing on each request
trustedNetworks := make([]*net.IPNet, 0, len(cfg.Security.TrustedProxies))
for _, cidr := range cfg.Security.TrustedProxies {
if _, network, err := net.ParseCIDR(cidr); err == nil {
trustedNetworks = append(trustedNetworks, network)
} else if ip := net.ParseIP(cidr); ip != nil {
// Single IP - create a /32 or /128 network
var mask net.IPMask
if ip.To4() != nil {
mask = net.CIDRMask(32, 32)
} else {
mask = net.CIDRMask(128, 128)
}
trustedNetworks = append(trustedNetworks, &net.IPNet{IP: ip, Mask: mask})
}
}
return &IngestionHandler{ return &IngestionHandler{
cfg: cfg, cfg: cfg,
domainMap: domainMap, domainMap: domainMap,
pathNorm: pathNorm, corsOriginMap: corsOriginMap,
pathRegistry: pathRegistry, pathNorm: pathNorm,
refRegistry: refRegistry, pathRegistry: pathRegistry,
metricsAgg: metricsAgg, refRegistry: refRegistry,
rateLimiter: limiter, metricsAgg: metricsAgg,
rng: rand.New(rand.NewSource(42)), rateLimiter: limiter,
rng: rand.New(rand.NewSource(time.Now().UnixNano())),
trustedNetworks: trustedNetworks,
} }
} }
@ -213,13 +241,8 @@ func (h *IngestionHandler) extractIP(r *http.Request) string {
// Check if we should trust proxy headers // Check if we should trust proxy headers
trustProxy := false trustProxy := false
if len(h.cfg.Security.TrustedProxies) > 0 { if len(h.trustedNetworks) > 0 {
for _, trustedCIDR := range h.cfg.Security.TrustedProxies { trustProxy = h.ipInNetworks(remoteIP, h.trustedNetworks)
if h.ipInCIDR(remoteIP, trustedCIDR) {
trustProxy = true
break
}
}
} }
// If not trusting proxy, return direct IP // If not trusting proxy, return direct IP
@ -233,10 +256,9 @@ func (h *IngestionHandler) extractIP(r *http.Request) string {
ips := strings.Split(xff, ",") ips := strings.Split(xff, ",")
for i := len(ips) - 1; i >= 0; i-- { for i := len(ips) - 1; i >= 0; i-- {
ip := strings.TrimSpace(ips[i]) ip := strings.TrimSpace(ips[i])
if !h.ipInCIDR(ip, "0.0.0.0/0") { if testIP := net.ParseIP(ip); testIP != nil {
continue return ip
} }
return ip
} }
} }
@ -249,26 +271,20 @@ func (h *IngestionHandler) extractIP(r *http.Request) string {
return remoteIP return remoteIP
} }
// Checks if an IP address is within a CIDR range // Checks if an IP address is within any of the trusted networks
func (h *IngestionHandler) ipInCIDR(ip, cidr string) bool { func (h *IngestionHandler) ipInNetworks(ip string, networks []*net.IPNet) bool {
// Parse the IP address
testIP := net.ParseIP(ip) testIP := net.ParseIP(ip)
if testIP == nil { if testIP == nil {
return false return false
} }
// Parse the CIDR for _, network := range networks {
_, network, err := net.ParseCIDR(cidr) if network.Contains(testIP) {
if err != nil { return true
// If it's not a CIDR, try as a single IP
cidrIP := net.ParseIP(cidr)
if cidrIP == nil {
return false
} }
return testIP.Equal(cidrIP)
} }
return network.Contains(testIP) return false
} }
// Classifies device using both screen width and User-Agent parsing // Classifies device using both screen width and User-Agent parsing