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

     1  package mocknet
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"sync"
     7  
     8  	inet "github.com/ipfs/go-ipfs/p2p/net"
     9  	peer "github.com/ipfs/go-ipfs/p2p/peer"
    10  
    11  	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
    12  	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
    13  	goprocessctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context"
    14  	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
    15  )
    16  
    17  // peernet implements inet.Network
    18  type peernet struct {
    19  	mocknet *mocknet // parent
    20  
    21  	peer peer.ID
    22  	ps   peer.Peerstore
    23  
    24  	// conns are actual live connections between peers.
    25  	// many conns could run over each link.
    26  	// **conns are NOT shared between peers**
    27  	connsByPeer map[peer.ID]map[*conn]struct{}
    28  	connsByLink map[*link]map[*conn]struct{}
    29  
    30  	// implement inet.Network
    31  	streamHandler inet.StreamHandler
    32  	connHandler   inet.ConnHandler
    33  
    34  	notifmu sync.RWMutex
    35  	notifs  map[inet.Notifiee]struct{}
    36  
    37  	proc goprocess.Process
    38  	sync.RWMutex
    39  }
    40  
    41  // newPeernet constructs a new peernet
    42  func newPeernet(ctx context.Context, m *mocknet, p peer.ID, ps peer.Peerstore) (*peernet, error) {
    43  
    44  	n := &peernet{
    45  		mocknet: m,
    46  		peer:    p,
    47  		ps:      ps,
    48  
    49  		connsByPeer: map[peer.ID]map[*conn]struct{}{},
    50  		connsByLink: map[*link]map[*conn]struct{}{},
    51  
    52  		notifs: make(map[inet.Notifiee]struct{}),
    53  	}
    54  
    55  	n.proc = goprocessctx.WithContextAndTeardown(ctx, n.teardown)
    56  	return n, nil
    57  }
    58  
    59  func (pn *peernet) teardown() error {
    60  
    61  	// close the connections
    62  	for _, c := range pn.allConns() {
    63  		c.Close()
    64  	}
    65  	return nil
    66  }
    67  
    68  // allConns returns all the connections between this peer and others
    69  func (pn *peernet) allConns() []*conn {
    70  	pn.RLock()
    71  	var cs []*conn
    72  	for _, csl := range pn.connsByPeer {
    73  		for c := range csl {
    74  			cs = append(cs, c)
    75  		}
    76  	}
    77  	pn.RUnlock()
    78  	return cs
    79  }
    80  
    81  // Close calls the ContextCloser func
    82  func (pn *peernet) Close() error {
    83  	return pn.proc.Close()
    84  }
    85  
    86  func (pn *peernet) Peerstore() peer.Peerstore {
    87  	return pn.ps
    88  }
    89  
    90  func (pn *peernet) String() string {
    91  	return fmt.Sprintf("<mock.peernet %s - %d conns>", pn.peer, len(pn.allConns()))
    92  }
    93  
    94  // handleNewStream is an internal function to trigger the client's handler
    95  func (pn *peernet) handleNewStream(s inet.Stream) {
    96  	pn.RLock()
    97  	handler := pn.streamHandler
    98  	pn.RUnlock()
    99  	if handler != nil {
   100  		go handler(s)
   101  	}
   102  }
   103  
   104  // handleNewConn is an internal function to trigger the client's handler
   105  func (pn *peernet) handleNewConn(c inet.Conn) {
   106  	pn.RLock()
   107  	handler := pn.connHandler
   108  	pn.RUnlock()
   109  	if handler != nil {
   110  		go handler(c)
   111  	}
   112  }
   113  
   114  // DialPeer attempts to establish a connection to a given peer.
   115  // Respects the context.
   116  func (pn *peernet) DialPeer(ctx context.Context, p peer.ID) (inet.Conn, error) {
   117  	return pn.connect(p)
   118  }
   119  
   120  func (pn *peernet) connect(p peer.ID) (*conn, error) {
   121  	// first, check if we already have live connections
   122  	pn.RLock()
   123  	cs, found := pn.connsByPeer[p]
   124  	if found && len(cs) > 0 {
   125  		var chosen *conn
   126  		for c := range cs { // because cs is a map
   127  			chosen = c // select first
   128  			break
   129  		}
   130  		pn.RUnlock()
   131  		return chosen, nil
   132  	}
   133  	pn.RUnlock()
   134  
   135  	log.Debugf("%s (newly) dialing %s", pn.peer, p)
   136  
   137  	// ok, must create a new connection. we need a link
   138  	links := pn.mocknet.LinksBetweenPeers(pn.peer, p)
   139  	if len(links) < 1 {
   140  		return nil, fmt.Errorf("%s cannot connect to %s", pn.peer, p)
   141  	}
   142  
   143  	// if many links found, how do we select? for now, randomly...
   144  	// this would be an interesting place to test logic that can measure
   145  	// links (network interfaces) and select properly
   146  	l := links[rand.Intn(len(links))]
   147  
   148  	log.Debugf("%s dialing %s openingConn", pn.peer, p)
   149  	// create a new connection with link
   150  	c := pn.openConn(p, l.(*link))
   151  	return c, nil
   152  }
   153  
   154  func (pn *peernet) openConn(r peer.ID, l *link) *conn {
   155  	lc, rc := l.newConnPair(pn)
   156  	log.Debugf("%s opening connection to %s", pn.LocalPeer(), lc.RemotePeer())
   157  	pn.addConn(lc)
   158  	pn.notifyAll(func(n inet.Notifiee) {
   159  		n.Connected(pn, lc)
   160  	})
   161  	rc.net.remoteOpenedConn(rc)
   162  	return lc
   163  }
   164  
   165  func (pn *peernet) remoteOpenedConn(c *conn) {
   166  	log.Debugf("%s accepting connection from %s", pn.LocalPeer(), c.RemotePeer())
   167  	pn.addConn(c)
   168  	pn.handleNewConn(c)
   169  	pn.notifyAll(func(n inet.Notifiee) {
   170  		n.Connected(pn, c)
   171  	})
   172  }
   173  
   174  // addConn constructs and adds a connection
   175  // to given remote peer over given link
   176  func (pn *peernet) addConn(c *conn) {
   177  	pn.Lock()
   178  	defer pn.Unlock()
   179  
   180  	cs, found := pn.connsByPeer[c.RemotePeer()]
   181  	if !found {
   182  		cs = map[*conn]struct{}{}
   183  		pn.connsByPeer[c.RemotePeer()] = cs
   184  	}
   185  	pn.connsByPeer[c.RemotePeer()][c] = struct{}{}
   186  
   187  	cs, found = pn.connsByLink[c.link]
   188  	if !found {
   189  		cs = map[*conn]struct{}{}
   190  		pn.connsByLink[c.link] = cs
   191  	}
   192  	pn.connsByLink[c.link][c] = struct{}{}
   193  }
   194  
   195  // removeConn removes a given conn
   196  func (pn *peernet) removeConn(c *conn) {
   197  	pn.Lock()
   198  	defer pn.Unlock()
   199  
   200  	cs, found := pn.connsByLink[c.link]
   201  	if !found || len(cs) < 1 {
   202  		panic(fmt.Sprintf("attempting to remove a conn that doesnt exist %p", c.link))
   203  	}
   204  	delete(cs, c)
   205  
   206  	cs, found = pn.connsByPeer[c.remote]
   207  	if !found {
   208  		panic(fmt.Sprintf("attempting to remove a conn that doesnt exist %p", c.remote))
   209  	}
   210  	delete(cs, c)
   211  }
   212  
   213  // Process returns the network's Process
   214  func (pn *peernet) Process() goprocess.Process {
   215  	return pn.proc
   216  }
   217  
   218  // LocalPeer the network's LocalPeer
   219  func (pn *peernet) LocalPeer() peer.ID {
   220  	return pn.peer
   221  }
   222  
   223  // Peers returns the connected peers
   224  func (pn *peernet) Peers() []peer.ID {
   225  	pn.RLock()
   226  	defer pn.RUnlock()
   227  
   228  	peers := make([]peer.ID, 0, len(pn.connsByPeer))
   229  	for _, cs := range pn.connsByPeer {
   230  		for c := range cs {
   231  			peers = append(peers, c.remote)
   232  			break
   233  		}
   234  	}
   235  	return peers
   236  }
   237  
   238  // Conns returns all the connections of this peer
   239  func (pn *peernet) Conns() []inet.Conn {
   240  	pn.RLock()
   241  	defer pn.RUnlock()
   242  
   243  	out := make([]inet.Conn, 0, len(pn.connsByPeer))
   244  	for _, cs := range pn.connsByPeer {
   245  		for c := range cs {
   246  			out = append(out, c)
   247  		}
   248  	}
   249  	return out
   250  }
   251  
   252  func (pn *peernet) ConnsToPeer(p peer.ID) []inet.Conn {
   253  	pn.RLock()
   254  	defer pn.RUnlock()
   255  
   256  	cs, found := pn.connsByPeer[p]
   257  	if !found || len(cs) == 0 {
   258  		return nil
   259  	}
   260  
   261  	var cs2 []inet.Conn
   262  	for c := range cs {
   263  		cs2 = append(cs2, c)
   264  	}
   265  	return cs2
   266  }
   267  
   268  // ClosePeer connections to peer
   269  func (pn *peernet) ClosePeer(p peer.ID) error {
   270  	pn.RLock()
   271  	cs, found := pn.connsByPeer[p]
   272  	if !found {
   273  		pn.RUnlock()
   274  		return nil
   275  	}
   276  
   277  	var conns []*conn
   278  	for c := range cs {
   279  		conns = append(conns, c)
   280  	}
   281  	pn.RUnlock()
   282  	for _, c := range conns {
   283  		c.Close()
   284  	}
   285  	return nil
   286  }
   287  
   288  // BandwidthTotals returns the total amount of bandwidth transferred
   289  func (pn *peernet) BandwidthTotals() (in uint64, out uint64) {
   290  	// need to implement this. probably best to do it in swarm this time.
   291  	// need a "metrics" object
   292  	return 0, 0
   293  }
   294  
   295  // Listen tells the network to start listening on given multiaddrs.
   296  func (pn *peernet) Listen(addrs ...ma.Multiaddr) error {
   297  	pn.Peerstore().AddAddrs(pn.LocalPeer(), addrs, peer.PermanentAddrTTL)
   298  	return nil
   299  }
   300  
   301  // ListenAddresses returns a list of addresses at which this network listens.
   302  func (pn *peernet) ListenAddresses() []ma.Multiaddr {
   303  	return pn.Peerstore().Addrs(pn.LocalPeer())
   304  }
   305  
   306  // InterfaceListenAddresses returns a list of addresses at which this network
   307  // listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to
   308  // use the known local interfaces.
   309  func (pn *peernet) InterfaceListenAddresses() ([]ma.Multiaddr, error) {
   310  	return pn.ListenAddresses(), nil
   311  }
   312  
   313  // Connectedness returns a state signaling connection capabilities
   314  // For now only returns Connecter || NotConnected. Expand into more later.
   315  func (pn *peernet) Connectedness(p peer.ID) inet.Connectedness {
   316  	pn.Lock()
   317  	defer pn.Unlock()
   318  
   319  	cs, found := pn.connsByPeer[p]
   320  	if found && len(cs) > 0 {
   321  		return inet.Connected
   322  	}
   323  	return inet.NotConnected
   324  }
   325  
   326  // NewStream returns a new stream to given peer p.
   327  // If there is no connection to p, attempts to create one.
   328  func (pn *peernet) NewStream(p peer.ID) (inet.Stream, error) {
   329  	pn.Lock()
   330  	cs, found := pn.connsByPeer[p]
   331  	if !found || len(cs) < 1 {
   332  		pn.Unlock()
   333  		return nil, fmt.Errorf("no connection to peer")
   334  	}
   335  
   336  	// if many conns are found, how do we select? for now, randomly...
   337  	// this would be an interesting place to test logic that can measure
   338  	// links (network interfaces) and select properly
   339  	n := rand.Intn(len(cs))
   340  	var c *conn
   341  	for c = range cs {
   342  		if n == 0 {
   343  			break
   344  		}
   345  		n--
   346  	}
   347  	pn.Unlock()
   348  
   349  	return c.NewStream()
   350  }
   351  
   352  // SetStreamHandler sets the new stream handler on the Network.
   353  // This operation is threadsafe.
   354  func (pn *peernet) SetStreamHandler(h inet.StreamHandler) {
   355  	pn.Lock()
   356  	pn.streamHandler = h
   357  	pn.Unlock()
   358  }
   359  
   360  // SetConnHandler sets the new conn handler on the Network.
   361  // This operation is threadsafe.
   362  func (pn *peernet) SetConnHandler(h inet.ConnHandler) {
   363  	pn.Lock()
   364  	pn.connHandler = h
   365  	pn.Unlock()
   366  }
   367  
   368  // Notify signs up Notifiee to receive signals when events happen
   369  func (pn *peernet) Notify(f inet.Notifiee) {
   370  	pn.notifmu.Lock()
   371  	pn.notifs[f] = struct{}{}
   372  	pn.notifmu.Unlock()
   373  }
   374  
   375  // StopNotify unregisters Notifiee fromr receiving signals
   376  func (pn *peernet) StopNotify(f inet.Notifiee) {
   377  	pn.notifmu.Lock()
   378  	delete(pn.notifs, f)
   379  	pn.notifmu.Unlock()
   380  }
   381  
   382  // notifyAll runs the notification function on all Notifiees
   383  func (pn *peernet) notifyAll(notification func(f inet.Notifiee)) {
   384  	pn.notifmu.RLock()
   385  	for n := range pn.notifs {
   386  		// make sure we dont block
   387  		// and they dont block each other.
   388  		go notification(n)
   389  	}
   390  	pn.notifmu.RUnlock()
   391  }