notif.go 3.06 KB
Newer Older
1 2 3
package dht

import (
4 5
	inet "github.com/libp2p/go-libp2p-net"
	ma "github.com/multiformats/go-multiaddr"
Jeromy's avatar
Jeromy committed
6
	mstream "github.com/multiformats/go-multistream"
7 8 9 10 11
)

// netNotifiee defines methods to be used with the IpfsDHT
type netNotifiee IpfsDHT

Steven Allen's avatar
Steven Allen committed
12 13
var dhtProtocols = []string{string(ProtocolDHT), string(ProtocolDHTOld)}

14 15 16 17 18 19 20
func (nn *netNotifiee) DHT() *IpfsDHT {
	return (*IpfsDHT)(nn)
}

func (nn *netNotifiee) Connected(n inet.Network, v inet.Conn) {
	dht := nn.DHT()
	select {
21
	case <-dht.Process().Closing():
22
		return
23
	default:
24
	}
25

Steven Allen's avatar
Steven Allen committed
26 27 28
	p := v.RemotePeer()
	protos, err := dht.peerstore.SupportsProtocols(p, dhtProtocols...)
	if err == nil && len(protos) != 0 {
29 30 31
		// We lock here for consistency with the lock in testConnection.
		// This probably isn't necessary because (dis)connect
		// notifications are serialized but it's nice to be consistent.
Steven Allen's avatar
Steven Allen committed
32 33 34 35 36
		dht.plk.Lock()
		defer dht.plk.Unlock()
		if dht.host.Network().Connectedness(p) == inet.Connected {
			dht.Update(dht.Context(), p)
		}
37 38 39
		return
	}

40
	// Note: Unfortunately, the peerstore may not yet know that this peer is
Steven Allen's avatar
Steven Allen committed
41 42 43
	// a DHT server. So, if it didn't return a positive response above, test
	// manually.
	go nn.testConnection(v)
Steven Allen's avatar
Steven Allen committed
44 45
}

Steven Allen's avatar
Steven Allen committed
46
func (nn *netNotifiee) testConnection(v inet.Conn) {
Steven Allen's avatar
Steven Allen committed
47
	dht := nn.DHT()
Steven Allen's avatar
Steven Allen committed
48 49 50 51 52 53 54 55
	p := v.RemotePeer()

	// Forcibly use *this* connection. Otherwise, if we have two connections, we could:
	// 1. Test it twice.
	// 2. Have it closed from under us leaving the second (open) connection untested.
	s, err := v.NewStream()
	if err != nil {
		// Connection error
Steven Allen's avatar
Steven Allen committed
56 57
		return
	}
Steven Allen's avatar
Steven Allen committed
58 59 60 61 62 63 64 65 66 67
	defer s.Close()

	selected, err := mstream.SelectOneOf(dhtProtocols, s)
	if err != nil {
		// Doesn't support the protocol
		return
	}
	// Remember this choice (makes subsequent negotiations faster)
	dht.peerstore.AddProtocols(p, selected)

68 69 70
	// We lock here as we race with disconnect. If we didn't lock, we could
	// finish processing a connect after handling the associated disconnect
	// event and add the peer to the routing table after removing it.
Steven Allen's avatar
Steven Allen committed
71 72 73 74 75
	dht.plk.Lock()
	defer dht.plk.Unlock()
	if dht.host.Network().Connectedness(p) == inet.Connected {
		dht.Update(dht.Context(), p)
	}
76 77 78 79 80
}

func (nn *netNotifiee) Disconnected(n inet.Network, v inet.Conn) {
	dht := nn.DHT()
	select {
81
	case <-dht.Process().Closing():
82
		return
83
	default:
84
	}
85

86
	p := v.RemotePeer()
87

88 89
	// Lock and check to see if we're still connected. We lock to make sure
	// we don't concurrently process a connect event.
90 91 92 93 94 95
	dht.plk.Lock()
	defer dht.plk.Unlock()
	if dht.host.Network().Connectedness(p) == inet.Connected {
		// We're still connected.
		return
	}
96

97
	dht.routingTable.Remove(p)
98 99 100 101

	dht.smlk.Lock()
	defer dht.smlk.Unlock()
	ms, ok := dht.strmap[p]
102 103 104
	if !ok {
		return
	}
105 106 107 108 109 110 111 112
	delete(dht.strmap, p)

	// Do this asynchronously as ms.lk can block for a while.
	go func() {
		ms.lk.Lock()
		defer ms.lk.Unlock()
		ms.invalidate()
	}()
113 114 115 116
}

func (nn *netNotifiee) OpenedStream(n inet.Network, v inet.Stream) {}
func (nn *netNotifiee) ClosedStream(n inet.Network, v inet.Stream) {}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
117 118
func (nn *netNotifiee) Listen(n inet.Network, a ma.Multiaddr)      {}
func (nn *netNotifiee) ListenClose(n inet.Network, a ma.Multiaddr) {}