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 }