github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/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  	"errors"
     7  	"fmt"
     8  	"strings"
     9  	"sync"
    10  
    11  	conn "github.com/jbenet/go-ipfs/net/conn"
    12  	msg "github.com/jbenet/go-ipfs/net/message"
    13  	peer "github.com/jbenet/go-ipfs/peer"
    14  	u "github.com/jbenet/go-ipfs/util"
    15  	ctxc "github.com/jbenet/go-ipfs/util/ctxcloser"
    16  
    17  	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
    18  	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
    19  )
    20  
    21  var log = u.Logger("swarm")
    22  
    23  // ErrAlreadyOpen signals that a connection to a peer is already open.
    24  var ErrAlreadyOpen = errors.New("Error: Connection to this peer already open.")
    25  
    26  // ListenErr contains a set of errors mapping to each of the swarms addresses.
    27  // Used to return multiple errors, as in listen.
    28  type ListenErr struct {
    29  	Errors []error
    30  }
    31  
    32  func (e *ListenErr) Error() string {
    33  	if e == nil {
    34  		return "<nil error>"
    35  	}
    36  	var out string
    37  	for i, v := range e.Errors {
    38  		if v != nil {
    39  			out += fmt.Sprintf("%d: %s\n", i, v)
    40  		}
    41  	}
    42  	return out
    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  type Swarm struct {
    50  
    51  	// local is the peer this swarm represents
    52  	local peer.Peer
    53  
    54  	// peers is a collection of peers for swarm to use
    55  	peers peer.Peerstore
    56  
    57  	// Swarm includes a Pipe object.
    58  	*msg.Pipe
    59  
    60  	// errChan is the channel of errors.
    61  	errChan chan error
    62  
    63  	// conns are the open connections the swarm is handling.
    64  	// these are MultiConns, which multiplex multiple separate underlying Conns.
    65  	conns     conn.MultiConnMap
    66  	connsLock sync.RWMutex
    67  
    68  	// listeners for each network address
    69  	listeners []conn.Listener
    70  
    71  	// ContextCloser
    72  	ctxc.ContextCloser
    73  }
    74  
    75  // NewSwarm constructs a Swarm, with a Chan.
    76  func NewSwarm(ctx context.Context, listenAddrs []ma.Multiaddr, local peer.Peer, ps peer.Peerstore) (*Swarm, error) {
    77  	s := &Swarm{
    78  		Pipe:    msg.NewPipe(10),
    79  		conns:   conn.MultiConnMap{},
    80  		local:   local,
    81  		peers:   ps,
    82  		errChan: make(chan error, 100),
    83  	}
    84  
    85  	// ContextCloser for proper child management.
    86  	s.ContextCloser = ctxc.NewContextCloser(ctx, s.close)
    87  
    88  	s.Children().Add(1)
    89  	go s.fanOut()
    90  	return s, s.listen(listenAddrs)
    91  }
    92  
    93  // close stops a swarm. It's the underlying function called by ContextCloser
    94  func (s *Swarm) close() error {
    95  	// close listeners
    96  	for _, list := range s.listeners {
    97  		list.Close()
    98  	}
    99  	// close connections
   100  	conn.CloseConns(s.Connections()...)
   101  	return nil
   102  }
   103  
   104  // Dial connects to a peer.
   105  //
   106  // The idea is that the client of Swarm does not need to know what network
   107  // the connection will happen over. Swarm can use whichever it choses.
   108  // This allows us to use various transport protocols, do NAT traversal/relay,
   109  // etc. to achive connection.
   110  //
   111  // For now, Dial uses only TCP. This will be extended.
   112  func (s *Swarm) Dial(peer peer.Peer) (conn.Conn, error) {
   113  	if peer.ID().Equal(s.local.ID()) {
   114  		return nil, errors.New("Attempted connection to self!")
   115  	}
   116  
   117  	// check if we already have an open connection first
   118  	c := s.GetConnection(peer.ID())
   119  	if c != nil {
   120  		return c, nil
   121  	}
   122  
   123  	// check if we don't have the peer in Peerstore
   124  	peer, err := s.peers.Add(peer)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  
   129  	// open connection to peer
   130  	d := &conn.Dialer{
   131  		LocalPeer: s.local,
   132  		Peerstore: s.peers,
   133  	}
   134  
   135  	// If we are attempting to connect to the zero addr, fail out early
   136  	raddr := peer.NetAddress("tcp")
   137  	if raddr == nil {
   138  		return nil, fmt.Errorf("No remote address for network tcp")
   139  	}
   140  
   141  	if strings.HasPrefix(raddr.String(), "/ip4/0.0.0.0") {
   142  		return nil, fmt.Errorf("Attempted to connect to loopback address: %s", raddr)
   143  	}
   144  
   145  	c, err = d.Dial(s.Context(), "tcp", peer)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	c, err = s.connSetup(c)
   151  	if err != nil {
   152  		c.Close()
   153  		return nil, err
   154  	}
   155  
   156  	return c, nil
   157  }
   158  
   159  // GetConnection returns the connection in the swarm to given peer.ID
   160  func (s *Swarm) GetConnection(pid peer.ID) conn.Conn {
   161  	s.connsLock.RLock()
   162  	c, found := s.conns[u.Key(pid)]
   163  	s.connsLock.RUnlock()
   164  
   165  	if !found {
   166  		return nil
   167  	}
   168  	return c
   169  }
   170  
   171  // Connections returns a slice of all connections.
   172  func (s *Swarm) Connections() []conn.Conn {
   173  	s.connsLock.RLock()
   174  
   175  	conns := make([]conn.Conn, 0, len(s.conns))
   176  	for _, c := range s.conns {
   177  		conns = append(conns, c)
   178  	}
   179  
   180  	s.connsLock.RUnlock()
   181  	return conns
   182  }
   183  
   184  // CloseConnection removes a given peer from swarm + closes the connection
   185  func (s *Swarm) CloseConnection(p peer.Peer) error {
   186  	c := s.GetConnection(p.ID())
   187  	if c == nil {
   188  		return u.ErrNotFound
   189  	}
   190  
   191  	s.connsLock.Lock()
   192  	delete(s.conns, u.Key(p.ID()))
   193  	s.connsLock.Unlock()
   194  
   195  	return c.Close()
   196  }
   197  
   198  func (s *Swarm) Error(e error) {
   199  	s.errChan <- e
   200  }
   201  
   202  // GetErrChan returns the errors chan.
   203  func (s *Swarm) GetErrChan() chan error {
   204  	return s.errChan
   205  }
   206  
   207  // GetPeerList returns a copy of the set of peers swarm is connected to.
   208  func (s *Swarm) GetPeerList() []peer.Peer {
   209  	var out []peer.Peer
   210  	s.connsLock.RLock()
   211  	for _, p := range s.conns {
   212  		out = append(out, p.RemotePeer())
   213  	}
   214  	s.connsLock.RUnlock()
   215  	return out
   216  }