fix(proxy): align config watcher with RFC

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-05 01:59:28 +00:00
parent ce3b7a50cd
commit f2f7dfed75
4 changed files with 26 additions and 60 deletions

View file

@ -11,13 +11,10 @@ import (
"math" "math"
"net" "net"
"os" "os"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/fsnotify/fsnotify"
) )
// Result is the success/error carrier used by constructors and loaders. // Result is the success/error carrier used by constructors and loaders.
@ -367,6 +364,9 @@ func NewConfigWatcher(configPath string, onChange func(*Config)) *ConfigWatcher
onChange: onChange, onChange: onChange,
done: make(chan struct{}), done: make(chan struct{}),
} }
if data, err := os.ReadFile(configPath); err == nil {
watcher.lastSum = sha256.Sum256(data)
}
if info, err := os.Stat(configPath); err == nil { if info, err := os.Stat(configPath); err == nil {
watcher.lastMod = info.ModTime() watcher.lastMod = info.ModTime()
} }
@ -379,65 +379,42 @@ func (w *ConfigWatcher) Start() {
return return
} }
w.mu.Lock() w.mu.Lock()
if w.watcher != nil { if w.started {
w.mu.Unlock() w.mu.Unlock()
return return
} }
fsWatcher, err := fsnotify.NewWatcher() w.started = true
if err != nil {
w.mu.Unlock()
return
}
w.watcher = fsWatcher
w.mu.Unlock() w.mu.Unlock()
watchPath := filepath.Clean(w.path)
watchDir := filepath.Dir(watchPath)
if watchDir == "" {
watchDir = "."
}
if err := fsWatcher.Add(watchDir); err != nil {
_ = fsWatcher.Close()
w.mu.Lock()
if w.watcher == fsWatcher {
w.watcher = nil
}
w.mu.Unlock()
return
}
go func() { go func() {
defer func() { ticker := time.NewTicker(time.Second)
_ = fsWatcher.Close() defer ticker.Stop()
w.mu.Lock()
if w.watcher == fsWatcher {
w.watcher = nil
}
w.mu.Unlock()
}()
for { for {
select { select {
case event, ok := <-fsWatcher.Events: case <-ticker.C:
if !ok { data, err := os.ReadFile(w.path)
return if err != nil {
}
if filepath.Clean(event.Name) != watchPath {
continue continue
} }
if event.Op&(fsnotify.Write|fsnotify.Create|fsnotify.Rename|fsnotify.Remove|fsnotify.Chmod) == 0 { sum := sha256.Sum256(data)
w.mu.Lock()
changed := sum != w.lastSum
if changed {
w.lastSum = sum
}
w.mu.Unlock()
if !changed {
continue continue
} }
if info, err := os.Stat(w.path); err == nil {
w.mu.Lock()
w.lastMod = info.ModTime()
w.mu.Unlock()
}
config, result := LoadConfig(w.path) config, result := LoadConfig(w.path)
if result.OK && config != nil { if result.OK && config != nil {
if info, err := os.Stat(w.path); err == nil {
w.lastMod = info.ModTime()
}
w.onChange(config) w.onChange(config)
} }
case _, ok := <-fsWatcher.Errors:
if !ok {
return
}
case <-w.done: case <-w.done:
return return
} }
@ -451,10 +428,7 @@ func (w *ConfigWatcher) Stop() {
return return
} }
w.mu.Lock() w.mu.Lock()
if w.watcher != nil { w.started = false
_ = w.watcher.Close()
w.watcher = nil
}
w.mu.Unlock() w.mu.Unlock()
select { select {
case <-w.done: case <-w.done:

4
go.mod
View file

@ -1,7 +1,3 @@
module dappco.re/go/proxy module dappco.re/go/proxy
go 1.26.0 go 1.26.0
require github.com/fsnotify/fsnotify v1.7.0
require golang.org/x/sys v0.4.0 // indirect

4
go.sum
View file

@ -1,4 +0,0 @@
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View file

@ -8,7 +8,6 @@
package proxy package proxy
import ( import (
"github.com/fsnotify/fsnotify"
"net/http" "net/http"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -120,9 +119,10 @@ type ConfigWatcher struct {
path string path string
onChange func(*Config) onChange func(*Config)
lastMod time.Time lastMod time.Time
lastSum [32]byte
done chan struct{} done chan struct{}
mu sync.Mutex mu sync.Mutex
watcher *fsnotify.Watcher started bool
} }
// RateLimiter throttles new connections per source IP. // RateLimiter throttles new connections per source IP.