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"
"net"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
"github.com/fsnotify/fsnotify"
)
// 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,
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 {
watcher.lastMod = info.ModTime()
}
@ -379,65 +379,42 @@ func (w *ConfigWatcher) Start() {
return
}
w.mu.Lock()
if w.watcher != nil {
if w.started {
w.mu.Unlock()
return
}
fsWatcher, err := fsnotify.NewWatcher()
if err != nil {
w.mu.Unlock()
return
}
w.watcher = fsWatcher
w.started = true
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() {
defer func() {
_ = fsWatcher.Close()
w.mu.Lock()
if w.watcher == fsWatcher {
w.watcher = nil
}
w.mu.Unlock()
}()
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case event, ok := <-fsWatcher.Events:
if !ok {
return
}
if filepath.Clean(event.Name) != watchPath {
case <-ticker.C:
data, err := os.ReadFile(w.path)
if err != nil {
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
}
if info, err := os.Stat(w.path); err == nil {
w.mu.Lock()
w.lastMod = info.ModTime()
w.mu.Unlock()
}
config, result := LoadConfig(w.path)
if result.OK && config != nil {
if info, err := os.Stat(w.path); err == nil {
w.lastMod = info.ModTime()
}
w.onChange(config)
}
case _, ok := <-fsWatcher.Errors:
if !ok {
return
}
case <-w.done:
return
}
@ -451,10 +428,7 @@ func (w *ConfigWatcher) Stop() {
return
}
w.mu.Lock()
if w.watcher != nil {
_ = w.watcher.Close()
w.watcher = nil
}
w.started = false
w.mu.Unlock()
select {
case <-w.done:

4
go.mod
View file

@ -1,7 +1,3 @@
module dappco.re/go/proxy
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
import (
"github.com/fsnotify/fsnotify"
"net/http"
"sync"
"sync/atomic"
@ -120,9 +119,10 @@ type ConfigWatcher struct {
path string
onChange func(*Config)
lastMod time.Time
lastSum [32]byte
done chan struct{}
mu sync.Mutex
watcher *fsnotify.Watcher
started bool
}
// RateLimiter throttles new connections per source IP.