github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/p2p/net/swarm/swarm.go (about)

     1  // package swarm implements a connection muxer with a pair of channels
     2  // to synchronize all network communication.
     3  package swarm
     4  
     5  import (
     6  	"fmt"
     7  	"sync"
     8  	"time"
     9  
    10  	metrics "github.com/ipfs/go-ipfs/metrics"
    11  	inet "github.com/ipfs/go-ipfs/p2p/net"
    12  	filter "github.com/ipfs/go-ipfs/p2p/net/filter"
    13  	addrutil "github.com/ipfs/go-ipfs/p2p/net/swarm/addr"
    14  	peer "github.com/ipfs/go-ipfs/p2p/peer"
    15  	eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
    16  
    17  	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
    18  	ps "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-peerstream"
    19  	pst "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-stream-muxer"
    20  	psy "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-stream-muxer/yamux"
    21  	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
    22  	goprocessctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context"
    23  	prom "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
    24  	mafilter "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/whyrusleeping/multiaddr-filter"
    25  	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
    26  )
    27  
    28  var log = eventlog.Logger("swarm2")
    29  
    30  var PSTransport pst.Transport
    31  
    32  var peersTotal = prom.NewGaugeVec(prom.GaugeOpts{
    33  	Namespace: "ipfs",
    34  	Subsystem: "p2p",
    35  	Name:      "peers_total",
    36  	Help:      "Number of connected peers",
    37  }, []string{"peer_id"})
    38  
    39  func init() {
    40  	tpt := *psy.DefaultTransport
    41  	tpt.MaxStreamWindowSize = 512 * 1024
    42  	PSTransport = &tpt
    43  }
    44  
    45  // Swarm is a connection muxer, allowing connections to other peers to
    46  // be opened and closed, while still using the same Chan for all
    47  // communication. The Chan sends/receives Messages, which note the
    48  // destination or source Peer.
    49  //
    50  // Uses peerstream.Swarm
    51  type Swarm struct {
    52  	swarm *ps.Swarm
    53  	local peer.ID
    54  	peers peer.Peerstore
    55  	connh ConnHandler
    56  
    57  	dsync dialsync
    58  	backf dialbackoff
    59  	dialT time.Duration // mainly for tests
    60  
    61  	notifmu sync.RWMutex
    62  	notifs  map[inet.Notifiee]ps.Notifiee
    63  
    64  	// filters for addresses that shouldnt be dialed
    65  	Filters *filter.Filters
    66  
    67  	proc goprocess.Process
    68  	ctx  context.Context
    69  	bwc  metrics.Reporter
    70  }
    71  
    72  // NewSwarm constructs a Swarm, with a Chan.
    73  func NewSwarm(ctx context.Context, listenAddrs []ma.Multiaddr,
    74  	local peer.ID, peers peer.Peerstore, bwc metrics.Reporter) (*Swarm, error) {
    75  
    76  	listenAddrs, err := filterAddrs(listenAddrs)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  
    81  	s := &Swarm{
    82  		swarm:   ps.NewSwarm(PSTransport),
    83  		local:   local,
    84  		peers:   peers,
    85  		ctx:     ctx,
    86  		dialT:   DialTimeout,
    87  		notifs:  make(map[inet.Notifiee]ps.Notifiee),
    88  		bwc:     bwc,
    89  		Filters: filter.NewFilters(),
    90  	}
    91  
    92  	// configure Swarm
    93  	s.proc = goprocessctx.WithContextAndTeardown(ctx, s.teardown)
    94  	s.SetConnHandler(nil) // make sure to setup our own conn handler.
    95  
    96  	// setup swarm metrics
    97  	prom.MustRegisterOrGet(peersTotal)
    98  	s.Notify((*metricsNotifiee)(s))
    99  
   100  	return s, s.listen(listenAddrs)
   101  }
   102  
   103  func (s *Swarm) teardown() error {
   104  	return s.swarm.Close()
   105  }
   106  
   107  func (s *Swarm) AddAddrFilter(f string) error {
   108  	m, err := mafilter.NewMask(f)
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	s.Filters.AddDialFilter(m)
   114  	return nil
   115  }
   116  func filterAddrs(listenAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) {
   117  	if len(listenAddrs) > 0 {
   118  		filtered := addrutil.FilterUsableAddrs(listenAddrs)
   119  		if len(filtered) < 1 {
   120  			return nil, fmt.Errorf("swarm cannot use any addr in: %s", listenAddrs)
   121  		}
   122  		listenAddrs = filtered
   123  	}
   124  	return listenAddrs, nil
   125  }
   126  
   127  func (s *Swarm) Listen(addrs ...ma.Multiaddr) error {
   128  	addrs, err := filterAddrs(addrs)
   129  	if err != nil {
   130  		return err
   131  	}
   132  
   133  	return s.listen(addrs)
   134  }
   135  
   136  // Process returns the Process of the swarm
   137  func (s *Swarm) Process() goprocess.Process {
   138  	return s.proc
   139  }
   140  
   141  // Context returns the context of the swarm
   142  func (s *Swarm) Context() context.Context {
   143  	return s.ctx
   144  }
   145  
   146  // Close stops the Swarm.
   147  func (s *Swarm) Close() error {
   148  	return s.proc.Close()
   149  }
   150  
   151  // StreamSwarm returns the underlying peerstream.Swarm
   152  func (s *Swarm) StreamSwarm() *ps.Swarm {
   153  	return s.swarm
   154  }
   155  
   156  // SetConnHandler assigns the handler for new connections.
   157  // See peerstream. You will rarely use this. See SetStreamHandler
   158  func (s *Swarm) SetConnHandler(handler ConnHandler) {
   159  
   160  	// handler is nil if user wants to clear the old handler.
   161  	if handler == nil {
   162  		s.swarm.SetConnHandler(func(psconn *ps.Conn) {
   163  			s.connHandler(psconn)
   164  		})
   165  		return
   166  	}
   167  
   168  	s.swarm.SetConnHandler(func(psconn *ps.Conn) {
   169  		// sc is nil if closed in our handler.
   170  		if sc := s.connHandler(psconn); sc != nil {
   171  			// call the user's handler. in a goroutine for sync safety.
   172  			go handler(sc)
   173  		}
   174  	})
   175  }
   176  
   177  // SetStreamHandler assigns the handler for new streams.
   178  // See peerstream.
   179  func (s *Swarm) SetStreamHandler(handler inet.StreamHandler) {
   180  	s.swarm.SetStreamHandler(func(s *ps.Stream) {
   181  		handler(wrapStream(s))
   182  	})
   183  }
   184  
   185  // NewStreamWithPeer creates a new stream on any available connection to p
   186  func (s *Swarm) NewStreamWithPeer(p peer.ID) (*Stream, error) {
   187  	// if we have no connections, try connecting.
   188  	if len(s.ConnectionsToPeer(p)) == 0 {
   189  		log.Debug("Swarm: NewStreamWithPeer no connections. Attempting to connect...")
   190  		if _, err := s.Dial(s.Context(), p); err != nil {
   191  			return nil, err
   192  		}
   193  	}
   194  	log.Debug("Swarm: NewStreamWithPeer...")
   195  
   196  	st, err := s.swarm.NewStreamWithGroup(p)
   197  	return wrapStream(st), err
   198  }
   199  
   200  // StreamsWithPeer returns all the live Streams to p
   201  func (s *Swarm) StreamsWithPeer(p peer.ID) []*Stream {
   202  	return wrapStreams(ps.StreamsWithGroup(p, s.swarm.Streams()))
   203  }
   204  
   205  // ConnectionsToPeer returns all the live connections to p
   206  func (s *Swarm) ConnectionsToPeer(p peer.ID) []*Conn {
   207  	return wrapConns(ps.ConnsWithGroup(p, s.swarm.Conns()))
   208  }
   209  
   210  // Connections returns a slice of all connections.
   211  func (s *Swarm) Connections() []*Conn {
   212  	return wrapConns(s.swarm.Conns())
   213  }
   214  
   215  // CloseConnection removes a given peer from swarm + closes the connection
   216  func (s *Swarm) CloseConnection(p peer.ID) error {
   217  	conns := s.swarm.ConnsWithGroup(p) // boom.
   218  	for _, c := range conns {
   219  		c.Close()
   220  	}
   221  	return nil
   222  }
   223  
   224  // Peers returns a copy of the set of peers swarm is connected to.
   225  func (s *Swarm) Peers() []peer.ID {
   226  	conns := s.Connections()
   227  
   228  	seen := make(map[peer.ID]struct{})
   229  	peers := make([]peer.ID, 0, len(conns))
   230  	for _, c := range conns {
   231  		p := c.RemotePeer()
   232  		if _, found := seen[p]; found {
   233  			continue
   234  		}
   235  
   236  		seen[p] = struct{}{}
   237  		peers = append(peers, p)
   238  	}
   239  	return peers
   240  }
   241  
   242  // LocalPeer returns the local peer swarm is associated to.
   243  func (s *Swarm) LocalPeer() peer.ID {
   244  	return s.local
   245  }
   246  
   247  // notifyAll sends a signal to all Notifiees
   248  func (s *Swarm) notifyAll(notify func(inet.Notifiee)) {
   249  	s.notifmu.RLock()
   250  	for f := range s.notifs {
   251  		go notify(f)
   252  	}
   253  	s.notifmu.RUnlock()
   254  }
   255  
   256  // Notify signs up Notifiee to receive signals when events happen
   257  func (s *Swarm) Notify(f inet.Notifiee) {
   258  	// wrap with our notifiee, to translate function calls
   259  	n := &ps2netNotifee{net: (*Network)(s), not: f}
   260  
   261  	s.notifmu.Lock()
   262  	s.notifs[f] = n
   263  	s.notifmu.Unlock()
   264  
   265  	// register for notifications in the peer swarm.
   266  	s.swarm.Notify(n)
   267  }
   268  
   269  // StopNotify unregisters Notifiee fromr receiving signals
   270  func (s *Swarm) StopNotify(f inet.Notifiee) {
   271  	s.notifmu.Lock()
   272  	n, found := s.notifs[f]
   273  	if found {
   274  		delete(s.notifs, f)
   275  	}
   276  	s.notifmu.Unlock()
   277  
   278  	if found {
   279  		s.swarm.StopNotify(n)
   280  	}
   281  }
   282  
   283  type ps2netNotifee struct {
   284  	net *Network
   285  	not inet.Notifiee
   286  }
   287  
   288  func (n *ps2netNotifee) Connected(c *ps.Conn) {
   289  	n.not.Connected(n.net, inet.Conn((*Conn)(c)))
   290  }
   291  
   292  func (n *ps2netNotifee) Disconnected(c *ps.Conn) {
   293  	n.not.Disconnected(n.net, inet.Conn((*Conn)(c)))
   294  }
   295  
   296  func (n *ps2netNotifee) OpenedStream(s *ps.Stream) {
   297  	n.not.OpenedStream(n.net, inet.Stream((*Stream)(s)))
   298  }
   299  
   300  func (n *ps2netNotifee) ClosedStream(s *ps.Stream) {
   301  	n.not.ClosedStream(n.net, inet.Stream((*Stream)(s)))
   302  }
   303  
   304  type metricsNotifiee Swarm
   305  
   306  func (nn *metricsNotifiee) Connected(n inet.Network, v inet.Conn) {
   307  	peersTotalGauge(n.LocalPeer()).Set(float64(len(n.Conns())))
   308  }
   309  
   310  func (nn *metricsNotifiee) Disconnected(n inet.Network, v inet.Conn) {
   311  	peersTotalGauge(n.LocalPeer()).Set(float64(len(n.Conns())))
   312  }
   313  
   314  func (nn *metricsNotifiee) OpenedStream(n inet.Network, v inet.Stream) {}
   315  func (nn *metricsNotifiee) ClosedStream(n inet.Network, v inet.Stream) {}
   316  func (nn *metricsNotifiee) Listen(n inet.Network, a ma.Multiaddr)      {}
   317  func (nn *metricsNotifiee) ListenClose(n inet.Network, a ma.Multiaddr) {}
   318  
   319  func peersTotalGauge(id peer.ID) prom.Gauge {
   320  	return peersTotal.With(prom.Labels{"peer_id": id.Pretty()})
   321  }