ceremonyclient/go-libp2p/p2p/host/autorelay/autorelay.go
2024-10-12 11:55:17 -07:00

130 lines
2.8 KiB
Go

package autorelay
import (
"context"
"errors"
"sync"
"github.com/libp2p/go-libp2p/core/event"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/network"
basic "github.com/libp2p/go-libp2p/p2p/host/basic"
"github.com/libp2p/go-libp2p/p2p/host/eventbus"
logging "github.com/ipfs/go-log/v2"
ma "github.com/multiformats/go-multiaddr"
)
var log = logging.Logger("autorelay")
type AutoRelay struct {
refCount sync.WaitGroup
ctx context.Context
ctxCancel context.CancelFunc
conf *config
mx sync.Mutex
status network.Reachability
relayFinder *relayFinder
host host.Host
addrsF basic.AddrsFactory
metricsTracer MetricsTracer
}
func NewAutoRelay(bhost *basic.BasicHost, opts ...Option) (*AutoRelay, error) {
r := &AutoRelay{
host: bhost,
addrsF: bhost.AddrsFactory,
status: network.ReachabilityUnknown,
}
conf := defaultConfig
for _, opt := range opts {
if err := opt(&conf); err != nil {
return nil, err
}
}
r.ctx, r.ctxCancel = context.WithCancel(context.Background())
r.conf = &conf
r.relayFinder = newRelayFinder(bhost, conf.peerSource, &conf)
r.metricsTracer = &wrappedMetricsTracer{conf.metricsTracer}
bhost.AddrsFactory = r.hostAddrs
return r, nil
}
func (r *AutoRelay) Start() {
r.refCount.Add(1)
go func() {
r.background()
r.refCount.Done()
}()
}
func (r *AutoRelay) background() {
subReachability, err := r.host.EventBus().Subscribe(new(event.EvtLocalReachabilityChanged), eventbus.Name("autorelay (background)"))
if err != nil {
log.Debug("failed to subscribe to the EvtLocalReachabilityChanged")
return
}
for {
select {
case <-r.ctx.Done():
subReachability.Close()
return
case ev, ok := <-subReachability.Out():
if !ok {
subReachability.Close()
return
}
// TODO: push changed addresses
evt := ev.(event.EvtLocalReachabilityChanged)
switch evt.Reachability {
case network.ReachabilityPrivate, network.ReachabilityUnknown:
err := r.relayFinder.Start()
if errors.Is(err, errAlreadyRunning) {
log.Debug("tried to start already running relay finder")
} else if err != nil {
log.Errorw("failed to start relay finder", "error", err)
} else {
r.metricsTracer.RelayFinderStatus(true)
}
case network.ReachabilityPublic:
r.relayFinder.Stop()
r.metricsTracer.RelayFinderStatus(false)
}
r.mx.Lock()
r.status = evt.Reachability
r.mx.Unlock()
}
}
}
func (r *AutoRelay) hostAddrs(addrs []ma.Multiaddr) []ma.Multiaddr {
return r.relayAddrs(r.addrsF(addrs))
}
func (r *AutoRelay) relayAddrs(addrs []ma.Multiaddr) []ma.Multiaddr {
r.mx.Lock()
if r.status != network.ReachabilityPrivate {
r.mx.Unlock()
return addrs
}
a := r.relayFinder.relayAddrs(addrs)
r.mx.Unlock()
return a
}
func (r *AutoRelay) Close() error {
r.ctxCancel()
err := r.relayFinder.Stop()
r.refCount.Wait()
return err
}