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 } } } } }