vppn/peer/hubpoller.go
J. David Lee 1d3cc1f959 refactor-for-testability (#3)
Co-authored-by: jdl <jdl@desktop>
Co-authored-by: jdl <jdl@crumpington.com>
Reviewed-on: #3
2025-03-01 20:02:27 +00:00

111 lines
2.1 KiB
Go

package peer
import (
"encoding/json"
"io"
"log"
"net/http"
"net/url"
"time"
"vppn/m"
)
type hubPoller struct {
client *http.Client
req *http.Request
versions [256]int64
localIP byte
netName string
handleControlMsg func(fromIP byte, msg any)
}
func newHubPoller(
localIP byte,
netName,
hubURL,
apiKey string,
handleControlMsg func(byte, any),
) (*hubPoller, error) {
u, err := url.Parse(hubURL)
if err != nil {
return nil, err
}
u.Path = "/peer/fetch-state/"
client := &http.Client{Timeout: 8 * time.Second}
req := &http.Request{
Method: http.MethodGet,
URL: u,
Header: http.Header{},
}
req.SetBasicAuth("", apiKey)
return &hubPoller{
client: client,
req: req,
localIP: localIP,
netName: netName,
handleControlMsg: handleControlMsg,
}, nil
}
func (hp *hubPoller) logf(s string, args ...any) {
log.Printf("[HubPoller] "+s, args...)
}
func (hp *hubPoller) Run() {
state, err := loadNetworkState(hp.netName)
if err != nil {
hp.logf("Failed to load network state: %v", err)
hp.logf("Polling hub...")
hp.pollHub()
} else {
hp.applyNetworkState(state)
}
for range time.Tick(64 * time.Second) {
hp.pollHub()
}
}
func (hp *hubPoller) pollHub() {
var state m.NetworkState
resp, err := hp.client.Do(hp.req)
if err != nil {
hp.logf("Failed to fetch peer state: %v", err)
return
}
body, err := io.ReadAll(resp.Body)
_ = resp.Body.Close()
if err != nil {
hp.logf("Failed to read body from hub: %v", err)
return
}
if err := json.Unmarshal(body, &state); err != nil {
hp.logf("Failed to unmarshal response from hub: %v\n%s", err, body)
return
}
hp.applyNetworkState(state)
if err := storeNetworkState(hp.netName, state); err != nil {
hp.logf("Failed to store network state: %v", err)
}
}
func (hp *hubPoller) applyNetworkState(state m.NetworkState) {
for i, peer := range state.Peers {
if i != int(hp.localIP) {
if peer == nil || peer.Version != hp.versions[i] {
hp.handleControlMsg(byte(i), peerUpdateMsg{Peer: state.Peers[i]})
if peer != nil {
hp.versions[i] = peer.Version
}
}
}
}
}