package routedhost
import (
"context"
"fmt"
"time"
"github.com/libp2p/go-libp2p/core/connmgr"
"github.com/libp2p/go-libp2p/core/event"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/peerstore"
"github.com/libp2p/go-libp2p/core/protocol"
logging "github.com/ipfs/go-log/v2"
ma "github.com/multiformats/go-multiaddr"
)
var log = logging .Logger ("routedhost" )
const AddressTTL = time .Second * 10
type RoutedHost struct {
host host .Host
route Routing
}
type Routing interface {
FindPeer (context .Context , peer .ID ) (peer .AddrInfo , error )
}
func Wrap (h host .Host , r Routing ) *RoutedHost {
return &RoutedHost {h , r }
}
func (rh *RoutedHost ) Connect (ctx context .Context , pi peer .AddrInfo ) error {
forceDirect , _ := network .GetForceDirectDial (ctx )
canUseLimitedConn , _ := network .GetAllowLimitedConn (ctx )
if !forceDirect {
connectedness := rh .Network ().Connectedness (pi .ID )
if connectedness == network .Connected || (canUseLimitedConn && connectedness == network .Limited ) {
return nil
}
}
if len (pi .Addrs ) > 0 {
rh .Peerstore ().AddAddrs (pi .ID , pi .Addrs , peerstore .TempAddrTTL )
}
addrs := rh .Peerstore ().Addrs (pi .ID )
if len (addrs ) < 1 {
var err error
addrs , err = rh .findPeerAddrs (ctx , pi .ID )
if err != nil {
return err
}
}
for _ , addr := range addrs {
if _ , err := addr .ValueForProtocol (ma .P_CIRCUIT ); err != nil {
continue
}
if addr .Protocols ()[0 ].Code != ma .P_P2P {
continue
}
relay , _ := addr .ValueForProtocol (ma .P_P2P )
relayID , err := peer .Decode (relay )
if err != nil {
log .Debugf ("failed to parse relay ID in address %s: %s" , relay , err )
continue
}
if len (rh .Peerstore ().Addrs (relayID )) > 0 {
continue
}
relayAddrs , err := rh .findPeerAddrs (ctx , relayID )
if err != nil {
log .Debugf ("failed to find relay %s: %s" , relay , err )
continue
}
rh .Peerstore ().AddAddrs (relayID , relayAddrs , peerstore .TempAddrTTL )
}
pi .Addrs = addrs
if cerr := rh .host .Connect (ctx , pi ); cerr != nil {
newAddrs , err := rh .findPeerAddrs (ctx , pi .ID )
if err != nil {
log .Debugf ("failed to find more peer addresses %s: %s" , pi .ID , err )
return cerr
}
lookup := make (map [string ]struct {}, len (addrs ))
for _ , addr := range addrs {
lookup [string (addr .Bytes ())] = struct {}{}
}
for _ , newAddr := range newAddrs {
if _ , found := lookup [string (newAddr .Bytes ())]; found {
continue
}
pi .Addrs = newAddrs
return rh .host .Connect (ctx , pi )
}
return cerr
}
return nil
}
func (rh *RoutedHost ) findPeerAddrs (ctx context .Context , id peer .ID ) ([]ma .Multiaddr , error ) {
pi , err := rh .route .FindPeer (ctx , id )
if err != nil {
return nil , err
}
if pi .ID != id {
err = fmt .Errorf ("routing failure: provided addrs for different peer" )
log .Errorw ("got wrong peer" ,
"error" , err ,
"wantedPeer" , id ,
"gotPeer" , pi .ID ,
)
return nil , err
}
return pi .Addrs , nil
}
func (rh *RoutedHost ) ID () peer .ID {
return rh .host .ID ()
}
func (rh *RoutedHost ) Peerstore () peerstore .Peerstore {
return rh .host .Peerstore ()
}
func (rh *RoutedHost ) Addrs () []ma .Multiaddr {
return rh .host .Addrs ()
}
func (rh *RoutedHost ) Network () network .Network {
return rh .host .Network ()
}
func (rh *RoutedHost ) Mux () protocol .Switch {
return rh .host .Mux ()
}
func (rh *RoutedHost ) EventBus () event .Bus {
return rh .host .EventBus ()
}
func (rh *RoutedHost ) SetStreamHandler (pid protocol .ID , handler network .StreamHandler ) {
rh .host .SetStreamHandler (pid , handler )
}
func (rh *RoutedHost ) SetStreamHandlerMatch (pid protocol .ID , m func (protocol .ID ) bool , handler network .StreamHandler ) {
rh .host .SetStreamHandlerMatch (pid , m , handler )
}
func (rh *RoutedHost ) RemoveStreamHandler (pid protocol .ID ) {
rh .host .RemoveStreamHandler (pid )
}
func (rh *RoutedHost ) NewStream (ctx context .Context , p peer .ID , pids ...protocol .ID ) (network .Stream , error ) {
if nodial , _ := network .GetNoDial (ctx ); !nodial {
err := rh .Connect (ctx , peer .AddrInfo {ID : p })
if err != nil {
return nil , err
}
}
return rh .host .NewStream (ctx , p , pids ...)
}
func (rh *RoutedHost ) Close () error {
return rh .host .Close ()
}
func (rh *RoutedHost ) ConnManager () connmgr .ConnManager {
return rh .host .ConnManager ()
}
var _ (host .Host ) = (*RoutedHost )(nil )
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .