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 }