net2: separate protocols/services out.
using a placeholder net2 package so tests continue to pass. Will be swapped atomically into main code.
Juan Batiz-Benet committed
Jan 1, 2015 at 10:40 UTC
d322824874c663c582afdf606ded5dbfdfa436e7
29 files changed
+4092
p2p/net2/README.md
new
+17
@@ -0,0 +1,17 @@
1
+# Network
2
+
3
+The IPFS Network package handles all of the peer-to-peer networking. It connects to other hosts, it encrypts communications, it muxes messages between the network's client services and target hosts. It has multiple subcomponents:
4
+
5
+- `Conn` - a connection to a single Peer
6
+ - `MultiConn` - a set of connections to a single Peer
7
+ - `SecureConn` - an encrypted (tls-like) connection
8
+- `Swarm` - holds connections to Peers, multiplexes from/to each `MultiConn`
9
+- `Muxer` - multiplexes between `Services` and `Swarm`. Handles `Requet/Reply`.
10
+ - `Service` - connects between an outside client service and Network.
11
+ - `Handler` - the client service part that handles requests
12
+
13
+It looks a bit like this:
14
+
15
+<center>
16
+
17
+</center>
p2p/net2/conn/conn.go
new
+157
@@ -0,0 +1,157 @@
1
+package conn
2
+
3
+import (
4
+ "fmt"
5
+ "net"
6
+ "time"
7
+
8
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
9
+ msgio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-msgio"
10
+ mpool "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-msgio/mpool"
11
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
12
+ manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
13
+
14
+ ic "github.com/jbenet/go-ipfs/p2p/crypto"
15
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
16
+ u "github.com/jbenet/go-ipfs/util"
17
+ eventlog "github.com/jbenet/go-ipfs/util/eventlog"
18
+)
19
+
20
+var log = eventlog.Logger("conn")
21
+
22
+// ReleaseBuffer puts the given byte array back into the buffer pool,
23
+// first verifying that it is the correct size
24
+func ReleaseBuffer(b []byte) {
25
+ log.Debugf("Releasing buffer! (cap,size = %d, %d)", cap(b), len(b))
26
+ mpool.ByteSlicePool.Put(uint32(cap(b)), b)
27
+}
28
+
29
+// singleConn represents a single connection to another Peer (IPFS Node).
30
+type singleConn struct {
31
+ local peer.ID
32
+ remote peer.ID
33
+ maconn manet.Conn
34
+ msgrw msgio.ReadWriteCloser
35
+}
36
+
37
+// newConn constructs a new connection
38
+func newSingleConn(ctx context.Context, local, remote peer.ID, maconn manet.Conn) (Conn, error) {
39
+
40
+ conn := &singleConn{
41
+ local: local,
42
+ remote: remote,
43
+ maconn: maconn,
44
+ msgrw: msgio.NewReadWriter(maconn),
45
+ }
46
+
47
+ log.Debugf("newSingleConn %p: %v to %v", conn, local, remote)
48
+ return conn, nil
49
+}
50
+
51
+// close is the internal close function, called by ContextCloser.Close
52
+func (c *singleConn) Close() error {
53
+ log.Debugf("%s closing Conn with %s", c.local, c.remote)
54
+ // close underlying connection
55
+ return c.msgrw.Close()
56
+}
57
+
58
+// ID is an identifier unique to this connection.
59
+func (c *singleConn) ID() string {
60
+ return ID(c)
61
+}
62
+
63
+func (c *singleConn) String() string {
64
+ return String(c, "singleConn")
65
+}
66
+
67
+func (c *singleConn) LocalAddr() net.Addr {
68
+ return c.maconn.LocalAddr()
69
+}
70
+
71
+func (c *singleConn) RemoteAddr() net.Addr {
72
+ return c.maconn.RemoteAddr()
73
+}
74
+
75
+func (c *singleConn) LocalPrivateKey() ic.PrivKey {
76
+ return nil
77
+}
78
+
79
+func (c *singleConn) RemotePublicKey() ic.PubKey {
80
+ return nil
81
+}
82
+
83
+func (c *singleConn) SetDeadline(t time.Time) error {
84
+ return c.maconn.SetDeadline(t)
85
+}
86
+func (c *singleConn) SetReadDeadline(t time.Time) error {
87
+ return c.maconn.SetReadDeadline(t)
88
+}
89
+
90
+func (c *singleConn) SetWriteDeadline(t time.Time) error {
91
+ return c.maconn.SetWriteDeadline(t)
92
+}
93
+
94
+// LocalMultiaddr is the Multiaddr on this side
95
+func (c *singleConn) LocalMultiaddr() ma.Multiaddr {
96
+ return c.maconn.LocalMultiaddr()
97
+}
98
+
99
+// RemoteMultiaddr is the Multiaddr on the remote side
100
+func (c *singleConn) RemoteMultiaddr() ma.Multiaddr {
101
+ return c.maconn.RemoteMultiaddr()
102
+}
103
+
104
+// LocalPeer is the Peer on this side
105
+func (c *singleConn) LocalPeer() peer.ID {
106
+ return c.local
107
+}
108
+
109
+// RemotePeer is the Peer on the remote side
110
+func (c *singleConn) RemotePeer() peer.ID {
111
+ return c.remote
112
+}
113
+
114
+// Read reads data, net.Conn style
115
+func (c *singleConn) Read(buf []byte) (int, error) {
116
+ return c.msgrw.Read(buf)
117
+}
118
+
119
+// Write writes data, net.Conn style
120
+func (c *singleConn) Write(buf []byte) (int, error) {
121
+ return c.msgrw.Write(buf)
122
+}
123
+
124
+func (c *singleConn) NextMsgLen() (int, error) {
125
+ return c.msgrw.NextMsgLen()
126
+}
127
+
128
+// ReadMsg reads data, net.Conn style
129
+func (c *singleConn) ReadMsg() ([]byte, error) {
130
+ return c.msgrw.ReadMsg()
131
+}
132
+
133
+// WriteMsg writes data, net.Conn style
134
+func (c *singleConn) WriteMsg(buf []byte) error {
135
+ return c.msgrw.WriteMsg(buf)
136
+}
137
+
138
+// ReleaseMsg releases a buffer
139
+func (c *singleConn) ReleaseMsg(m []byte) {
140
+ c.msgrw.ReleaseMsg(m)
141
+}
142
+
143
+// ID returns the ID of a given Conn.
144
+func ID(c Conn) string {
145
+ l := fmt.Sprintf("%s/%s", c.LocalMultiaddr(), c.LocalPeer().Pretty())
146
+ r := fmt.Sprintf("%s/%s", c.RemoteMultiaddr(), c.RemotePeer().Pretty())
147
+ lh := u.Hash([]byte(l))
148
+ rh := u.Hash([]byte(r))
149
+ ch := u.XOR(lh, rh)
150
+ return u.Key(ch).Pretty()
151
+}
152
+
153
+// String returns the user-friendly String representation of a conn
154
+func String(c Conn, typ string) string {
155
+ return fmt.Sprintf("%s (%s) <-- %s %p --> (%s) %s",
156
+ c.LocalPeer(), c.LocalMultiaddr(), typ, c, c.RemoteMultiaddr(), c.RemotePeer())
157
+}
p2p/net2/conn/conn_test.go
new
+122
@@ -0,0 +1,122 @@
1
+package conn
2
+
3
+import (
4
+ "bytes"
5
+ "fmt"
6
+ "os"
7
+ "runtime"
8
+ "sync"
9
+ "testing"
10
+ "time"
11
+
12
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
13
+)
14
+
15
+func testOneSendRecv(t *testing.T, c1, c2 Conn) {
16
+ log.Debugf("testOneSendRecv from %s to %s", c1.LocalPeer(), c2.LocalPeer())
17
+ m1 := []byte("hello")
18
+ if err := c1.WriteMsg(m1); err != nil {
19
+ t.Fatal(err)
20
+ }
21
+ m2, err := c2.ReadMsg()
22
+ if err != nil {
23
+ t.Fatal(err)
24
+ }
25
+ if !bytes.Equal(m1, m2) {
26
+ t.Fatal("failed to send: %s %s", m1, m2)
27
+ }
28
+}
29
+
30
+func testNotOneSendRecv(t *testing.T, c1, c2 Conn) {
31
+ m1 := []byte("hello")
32
+ if err := c1.WriteMsg(m1); err == nil {
33
+ t.Fatal("write should have failed", err)
34
+ }
35
+ _, err := c2.ReadMsg()
36
+ if err == nil {
37
+ t.Fatal("read should have failed", err)
38
+ }
39
+}
40
+
41
+func TestClose(t *testing.T) {
42
+ // t.Skip("Skipping in favor of another test")
43
+
44
+ ctx, cancel := context.WithCancel(context.Background())
45
+ defer cancel()
46
+ c1, c2, _, _ := setupSingleConn(t, ctx)
47
+
48
+ testOneSendRecv(t, c1, c2)
49
+ testOneSendRecv(t, c2, c1)
50
+
51
+ c1.Close()
52
+ testNotOneSendRecv(t, c1, c2)
53
+
54
+ c2.Close()
55
+ testNotOneSendRecv(t, c2, c1)
56
+ testNotOneSendRecv(t, c1, c2)
57
+}
58
+
59
+func TestCloseLeak(t *testing.T) {
60
+ // t.Skip("Skipping in favor of another test")
61
+ if testing.Short() {
62
+ t.SkipNow()
63
+ }
64
+
65
+ if os.Getenv("TRAVIS") == "true" {
66
+ t.Skip("this doesn't work well on travis")
67
+ }
68
+
69
+ var wg sync.WaitGroup
70
+
71
+ runPair := func(num int) {
72
+ ctx, cancel := context.WithCancel(context.Background())
73
+ c1, c2, _, _ := setupSingleConn(t, ctx)
74
+
75
+ for i := 0; i < num; i++ {
76
+ b1 := []byte(fmt.Sprintf("beep%d", i))
77
+ c1.WriteMsg(b1)
78
+ b2, err := c2.ReadMsg()
79
+ if err != nil {
80
+ panic(err)
81
+ }
82
+ if !bytes.Equal(b1, b2) {
83
+ panic(fmt.Errorf("bytes not equal: %s != %s", b1, b2))
84
+ }
85
+
86
+ b2 = []byte(fmt.Sprintf("boop%d", i))
87
+ c2.WriteMsg(b2)
88
+ b1, err = c1.ReadMsg()
89
+ if err != nil {
90
+ panic(err)
91
+ }
92
+ if !bytes.Equal(b1, b2) {
93
+ panic(fmt.Errorf("bytes not equal: %s != %s", b1, b2))
94
+ }
95
+
96
+ <-time.After(time.Microsecond * 5)
97
+ }
98
+
99
+ c1.Close()
100
+ c2.Close()
101
+ cancel() // close the listener
102
+ wg.Done()
103
+ }
104
+
105
+ var cons = 5
106
+ var msgs = 50
107
+ log.Debugf("Running %d connections * %d msgs.\n", cons, msgs)
108
+ for i := 0; i < cons; i++ {
109
+ wg.Add(1)
110
+ go runPair(msgs)
111
+ }
112
+
113
+ log.Debugf("Waiting...\n")
114
+ wg.Wait()
115
+ // done!
116
+
117
+ <-time.After(time.Millisecond * 150)
118
+ if runtime.NumGoroutine() > 20 {
119
+ // panic("uncomment me to debug")
120
+ t.Fatal("leaking goroutines:", runtime.NumGoroutine())
121
+ }
122
+}
p2p/net2/conn/dial.go
new
+131
@@ -0,0 +1,131 @@
1
+package conn
2
+
3
+import (
4
+ "fmt"
5
+ "strings"
6
+
7
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
8
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
9
+ manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
10
+
11
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
12
+ debugerror "github.com/jbenet/go-ipfs/util/debugerror"
13
+)
14
+
15
+// String returns the string rep of d.
16
+func (d *Dialer) String() string {
17
+ return fmt.Sprintf("<Dialer %s %s ...>", d.LocalPeer, d.LocalAddrs[0])
18
+}
19
+
20
+// Dial connects to a peer over a particular address
21
+// Ensures raddr is part of peer.Addresses()
22
+// Example: d.DialAddr(ctx, peer.Addresses()[0], peer)
23
+func (d *Dialer) Dial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (Conn, error) {
24
+
25
+ network, _, err := manet.DialArgs(raddr)
26
+ if err != nil {
27
+ return nil, err
28
+ }
29
+
30
+ if strings.HasPrefix(raddr.String(), "/ip4/0.0.0.0") {
31
+ return nil, debugerror.Errorf("Attempted to connect to zero address: %s", raddr)
32
+ }
33
+
34
+ var laddr ma.Multiaddr
35
+ if len(d.LocalAddrs) > 0 {
36
+ // laddr := MultiaddrNetMatch(raddr, d.LocalAddrs)
37
+ laddr = NetAddress(network, d.LocalAddrs)
38
+ if laddr == nil {
39
+ return nil, debugerror.Errorf("No local address for network %s", network)
40
+ }
41
+ }
42
+
43
+ // TODO: try to get reusing addr/ports to work.
44
+ // madialer := manet.Dialer{LocalAddr: laddr}
45
+ madialer := manet.Dialer{}
46
+
47
+ log.Debugf("%s dialing %s %s", d.LocalPeer, remote, raddr)
48
+ maconn, err := madialer.Dial(raddr)
49
+ if err != nil {
50
+ return nil, err
51
+ }
52
+
53
+ var connOut Conn
54
+ var errOut error
55
+ done := make(chan struct{})
56
+
57
+ // do it async to ensure we respect don contexteone
58
+ go func() {
59
+ defer func() { done <- struct{}{} }()
60
+
61
+ c, err := newSingleConn(ctx, d.LocalPeer, remote, maconn)
62
+ if err != nil {
63
+ errOut = err
64
+ return
65
+ }
66
+
67
+ if d.PrivateKey == nil {
68
+ log.Warning("dialer %s dialing INSECURELY %s at %s!", d, remote, raddr)
69
+ connOut = c
70
+ return
71
+ }
72
+ c2, err := newSecureConn(ctx, d.PrivateKey, c)
73
+ if err != nil {
74
+ errOut = err
75
+ c.Close()
76
+ return
77
+ }
78
+
79
+ connOut = c2
80
+ }()
81
+
82
+ select {
83
+ case <-ctx.Done():
84
+ maconn.Close()
85
+ return nil, ctx.Err()
86
+ case <-done:
87
+ // whew, finished.
88
+ }
89
+
90
+ return connOut, errOut
91
+}
92
+
93
+// MultiaddrProtocolsMatch returns whether two multiaddrs match in protocol stacks.
94
+func MultiaddrProtocolsMatch(a, b ma.Multiaddr) bool {
95
+ ap := a.Protocols()
96
+ bp := b.Protocols()
97
+
98
+ if len(ap) != len(bp) {
99
+ return false
100
+ }
101
+
102
+ for i, api := range ap {
103
+ if api != bp[i] {
104
+ return false
105
+ }
106
+ }
107
+
108
+ return true
109
+}
110
+
111
+// MultiaddrNetMatch returns the first Multiaddr found to match network.
112
+func MultiaddrNetMatch(tgt ma.Multiaddr, srcs []ma.Multiaddr) ma.Multiaddr {
113
+ for _, a := range srcs {
114
+ if MultiaddrProtocolsMatch(tgt, a) {
115
+ return a
116
+ }
117
+ }
118
+ return nil
119
+}
120
+
121
+// NetAddress returns the first Multiaddr found for a given network.
122
+func NetAddress(n string, addrs []ma.Multiaddr) ma.Multiaddr {
123
+ for _, a := range addrs {
124
+ for _, p := range a.Protocols() {
125
+ if p.Name == n {
126
+ return a
127
+ }
128
+ }
129
+ }
130
+ return nil
131
+}
p2p/net2/conn/dial_test.go
new
+165
@@ -0,0 +1,165 @@
1
+package conn
2
+
3
+import (
4
+ "io"
5
+ "net"
6
+ "testing"
7
+ "time"
8
+
9
+ tu "github.com/jbenet/go-ipfs/util/testutil"
10
+
11
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
12
+)
13
+
14
+func echoListen(ctx context.Context, listener Listener) {
15
+ for {
16
+ c, err := listener.Accept()
17
+ if err != nil {
18
+
19
+ select {
20
+ case <-ctx.Done():
21
+ return
22
+ default:
23
+ }
24
+
25
+ if ne, ok := err.(net.Error); ok && ne.Temporary() {
26
+ <-time.After(time.Microsecond * 10)
27
+ continue
28
+ }
29
+
30
+ log.Debugf("echoListen: listener appears to be closing")
31
+ return
32
+ }
33
+
34
+ go echo(c.(Conn))
35
+ }
36
+}
37
+
38
+func echo(c Conn) {
39
+ io.Copy(c, c)
40
+}
41
+
42
+func setupSecureConn(t *testing.T, ctx context.Context) (a, b Conn, p1, p2 tu.PeerNetParams) {
43
+ return setupConn(t, ctx, true)
44
+}
45
+
46
+func setupSingleConn(t *testing.T, ctx context.Context) (a, b Conn, p1, p2 tu.PeerNetParams) {
47
+ return setupConn(t, ctx, false)
48
+}
49
+
50
+func setupConn(t *testing.T, ctx context.Context, secure bool) (a, b Conn, p1, p2 tu.PeerNetParams) {
51
+
52
+ p1 = tu.RandPeerNetParamsOrFatal(t)
53
+ p2 = tu.RandPeerNetParamsOrFatal(t)
54
+ laddr := p1.Addr
55
+
56
+ key1 := p1.PrivKey
57
+ key2 := p2.PrivKey
58
+ if !secure {
59
+ key1 = nil
60
+ key2 = nil
61
+ }
62
+ l1, err := Listen(ctx, laddr, p1.ID, key1)
63
+ if err != nil {
64
+ t.Fatal(err)
65
+ }
66
+
67
+ d2 := &Dialer{
68
+ LocalPeer: p2.ID,
69
+ PrivateKey: key2,
70
+ }
71
+
72
+ var c2 Conn
73
+
74
+ done := make(chan error)
75
+ go func() {
76
+ var err error
77
+ c2, err = d2.Dial(ctx, p1.Addr, p1.ID)
78
+ if err != nil {
79
+ done <- err
80
+ }
81
+ close(done)
82
+ }()
83
+
84
+ c1, err := l1.Accept()
85
+ if err != nil {
86
+ t.Fatal("failed to accept", err)
87
+ }
88
+ if err := <-done; err != nil {
89
+ t.Fatal(err)
90
+ }
91
+
92
+ return c1.(Conn), c2, p1, p2
93
+}
94
+
95
+func testDialer(t *testing.T, secure bool) {
96
+ // t.Skip("Skipping in favor of another test")
97
+
98
+ p1 := tu.RandPeerNetParamsOrFatal(t)
99
+ p2 := tu.RandPeerNetParamsOrFatal(t)
100
+
101
+ key1 := p1.PrivKey
102
+ key2 := p2.PrivKey
103
+ if !secure {
104
+ key1 = nil
105
+ key2 = nil
106
+ }
107
+
108
+ ctx, cancel := context.WithCancel(context.Background())
109
+ l1, err := Listen(ctx, p1.Addr, p1.ID, key1)
110
+ if err != nil {
111
+ t.Fatal(err)
112
+ }
113
+
114
+ d2 := &Dialer{
115
+ LocalPeer: p2.ID,
116
+ PrivateKey: key2,
117
+ }
118
+
119
+ go echoListen(ctx, l1)
120
+
121
+ c, err := d2.Dial(ctx, p1.Addr, p1.ID)
122
+ if err != nil {
123
+ t.Fatal("error dialing peer", err)
124
+ }
125
+
126
+ // fmt.Println("sending")
127
+ c.WriteMsg([]byte("beep"))
128
+ c.WriteMsg([]byte("boop"))
129
+
130
+ out, err := c.ReadMsg()
131
+ if err != nil {
132
+ t.Fatal(err)
133
+ }
134
+
135
+ // fmt.Println("recving", string(out))
136
+ data := string(out)
137
+ if data != "beep" {
138
+ t.Error("unexpected conn output", data)
139
+ }
140
+
141
+ out, err = c.ReadMsg()
142
+ if err != nil {
143
+ t.Fatal(err)
144
+ }
145
+
146
+ data = string(out)
147
+ if string(out) != "boop" {
148
+ t.Error("unexpected conn output", data)
149
+ }
150
+
151
+ // fmt.Println("closing")
152
+ c.Close()
153
+ l1.Close()
154
+ cancel()
155
+}
156
+
157
+func TestDialerInsecure(t *testing.T) {
158
+ // t.Skip("Skipping in favor of another test")
159
+ testDialer(t, false)
160
+}
161
+
162
+func TestDialerSecure(t *testing.T) {
163
+ // t.Skip("Skipping in favor of another test")
164
+ testDialer(t, true)
165
+}
p2p/net2/conn/interface.go
new
+84
@@ -0,0 +1,84 @@
1
+package conn
2
+
3
+import (
4
+ "io"
5
+ "net"
6
+ "time"
7
+
8
+ ic "github.com/jbenet/go-ipfs/p2p/crypto"
9
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
10
+ u "github.com/jbenet/go-ipfs/util"
11
+
12
+ msgio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-msgio"
13
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
14
+)
15
+
16
+// Map maps Keys (Peer.IDs) to Connections.
17
+type Map map[u.Key]Conn
18
+
19
+type PeerConn interface {
20
+ // LocalPeer (this side) ID, PrivateKey, and Address
21
+ LocalPeer() peer.ID
22
+ LocalPrivateKey() ic.PrivKey
23
+ LocalMultiaddr() ma.Multiaddr
24
+
25
+ // RemotePeer ID, PublicKey, and Address
26
+ RemotePeer() peer.ID
27
+ RemotePublicKey() ic.PubKey
28
+ RemoteMultiaddr() ma.Multiaddr
29
+}
30
+
31
+// Conn is a generic message-based Peer-to-Peer connection.
32
+type Conn interface {
33
+ PeerConn
34
+
35
+ // ID is an identifier unique to this connection.
36
+ ID() string
37
+
38
+ // can't just say "net.Conn" cause we have duplicate methods.
39
+ LocalAddr() net.Addr
40
+ RemoteAddr() net.Addr
41
+ SetDeadline(t time.Time) error
42
+ SetReadDeadline(t time.Time) error
43
+ SetWriteDeadline(t time.Time) error
44
+
45
+ msgio.Reader
46
+ msgio.Writer
47
+ io.Closer
48
+}
49
+
50
+// Dialer is an object that can open connections. We could have a "convenience"
51
+// Dial function as before, but it would have many arguments, as dialing is
52
+// no longer simple (need a peerstore, a local peer, a context, a network, etc)
53
+type Dialer struct {
54
+
55
+ // LocalPeer is the identity of the local Peer.
56
+ LocalPeer peer.ID
57
+
58
+ // LocalAddrs is a set of local addresses to use.
59
+ LocalAddrs []ma.Multiaddr
60
+
61
+ // PrivateKey used to initialize a secure connection.
62
+ // Warning: if PrivateKey is nil, connection will not be secured.
63
+ PrivateKey ic.PrivKey
64
+}
65
+
66
+// Listener is an object that can accept connections. It matches net.Listener
67
+type Listener interface {
68
+
69
+ // Accept waits for and returns the next connection to the listener.
70
+ Accept() (net.Conn, error)
71
+
72
+ // Addr is the local address
73
+ Addr() net.Addr
74
+
75
+ // Multiaddr is the local multiaddr address
76
+ Multiaddr() ma.Multiaddr
77
+
78
+ // LocalPeer is the identity of the local Peer.
79
+ LocalPeer() peer.ID
80
+
81
+ // Close closes the listener.
82
+ // Any blocked Accept operations will be unblocked and return errors.
83
+ Close() error
84
+}
p2p/net2/conn/listen.go
new
+115
@@ -0,0 +1,115 @@
1
+package conn
2
+
3
+import (
4
+ "fmt"
5
+ "net"
6
+
7
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
8
+ ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
9
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
10
+ manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
11
+
12
+ ic "github.com/jbenet/go-ipfs/p2p/crypto"
13
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
14
+)
15
+
16
+// listener is an object that can accept connections. It implements Listener
17
+type listener struct {
18
+ manet.Listener
19
+
20
+ maddr ma.Multiaddr // Local multiaddr to listen on
21
+ local peer.ID // LocalPeer is the identity of the local Peer
22
+ privk ic.PrivKey // private key to use to initialize secure conns
23
+
24
+ cg ctxgroup.ContextGroup
25
+}
26
+
27
+func (l *listener) teardown() error {
28
+ defer log.Debugf("listener closed: %s %s", l.local, l.maddr)
29
+ return l.Listener.Close()
30
+}
31
+
32
+func (l *listener) Close() error {
33
+ log.Debugf("listener closing: %s %s", l.local, l.maddr)
34
+ return l.cg.Close()
35
+}
36
+
37
+func (l *listener) String() string {
38
+ return fmt.Sprintf("<Listener %s %s>", l.local, l.maddr)
39
+}
40
+
41
+// Accept waits for and returns the next connection to the listener.
42
+// Note that unfortunately this
43
+func (l *listener) Accept() (net.Conn, error) {
44
+
45
+ // listeners dont have contexts. given changes dont make sense here anymore
46
+ // note that the parent of listener will Close, which will interrupt all io.
47
+ // Contexts and io don't mix.
48
+ ctx := context.Background()
49
+
50
+ maconn, err := l.Listener.Accept()
51
+ if err != nil {
52
+ return nil, err
53
+ }
54
+
55
+ c, err := newSingleConn(ctx, l.local, "", maconn)
56
+ if err != nil {
57
+ return nil, fmt.Errorf("Error accepting connection: %v", err)
58
+ }
59
+
60
+ if l.privk == nil {
61
+ log.Warning("listener %s listening INSECURELY!", l)
62
+ return c, nil
63
+ }
64
+ sc, err := newSecureConn(ctx, l.privk, c)
65
+ if err != nil {
66
+ return nil, fmt.Errorf("Error securing connection: %v", err)
67
+ }
68
+ return sc, nil
69
+}
70
+
71
+func (l *listener) Addr() net.Addr {
72
+ return l.Listener.Addr()
73
+}
74
+
75
+// Multiaddr is the identity of the local Peer.
76
+func (l *listener) Multiaddr() ma.Multiaddr {
77
+ return l.maddr
78
+}
79
+
80
+// LocalPeer is the identity of the local Peer.
81
+func (l *listener) LocalPeer() peer.ID {
82
+ return l.local
83
+}
84
+
85
+func (l *listener) Loggable() map[string]interface{} {
86
+ return map[string]interface{}{
87
+ "listener": map[string]interface{}{
88
+ "peer": l.LocalPeer(),
89
+ "address": l.Multiaddr(),
90
+ "secure": (l.privk != nil),
91
+ },
92
+ }
93
+}
94
+
95
+// Listen listens on the particular multiaddr, with given peer and peerstore.
96
+func Listen(ctx context.Context, addr ma.Multiaddr, local peer.ID, sk ic.PrivKey) (Listener, error) {
97
+
98
+ ml, err := manet.Listen(addr)
99
+ if err != nil {
100
+ return nil, fmt.Errorf("Failed to listen on %s: %s", addr, err)
101
+ }
102
+
103
+ l := &listener{
104
+ Listener: ml,
105
+ maddr: addr,
106
+ local: local,
107
+ privk: sk,
108
+ cg: ctxgroup.WithContext(ctx),
109
+ }
110
+ l.cg.SetTeardown(l.teardown)
111
+
112
+ log.Infof("swarm listening on %s", l.Multiaddr())
113
+ log.Event(ctx, "swarmListen", l)
114
+ return l, nil
115
+}
p2p/net2/conn/secure_conn.go
new
+154
@@ -0,0 +1,154 @@
1
+package conn
2
+
3
+import (
4
+ "net"
5
+ "time"
6
+
7
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
8
+ msgio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-msgio"
9
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
10
+
11
+ ic "github.com/jbenet/go-ipfs/p2p/crypto"
12
+ secio "github.com/jbenet/go-ipfs/p2p/crypto/secio"
13
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
14
+ errors "github.com/jbenet/go-ipfs/util/debugerror"
15
+)
16
+
17
+// secureConn wraps another Conn object with an encrypted channel.
18
+type secureConn struct {
19
+
20
+ // the wrapped conn
21
+ insecure Conn
22
+
23
+ // secure io (wrapping insecure)
24
+ secure msgio.ReadWriteCloser
25
+
26
+ // secure Session
27
+ session secio.Session
28
+}
29
+
30
+// newConn constructs a new connection
31
+func newSecureConn(ctx context.Context, sk ic.PrivKey, insecure Conn) (Conn, error) {
32
+
33
+ if insecure == nil {
34
+ return nil, errors.New("insecure is nil")
35
+ }
36
+ if insecure.LocalPeer() == "" {
37
+ return nil, errors.New("insecure.LocalPeer() is nil")
38
+ }
39
+ if sk == nil {
40
+ panic("way")
41
+ return nil, errors.New("private key is nil")
42
+ }
43
+
44
+ // NewSession performs the secure handshake, which takes multiple RTT
45
+ sessgen := secio.SessionGenerator{LocalID: insecure.LocalPeer(), PrivateKey: sk}
46
+ session, err := sessgen.NewSession(ctx, insecure)
47
+ if err != nil {
48
+ return nil, err
49
+ }
50
+
51
+ conn := &secureConn{
52
+ insecure: insecure,
53
+ session: session,
54
+ secure: session.ReadWriter(),
55
+ }
56
+ log.Debugf("newSecureConn: %v to %v handshake success!", conn.LocalPeer(), conn.RemotePeer())
57
+ return conn, nil
58
+}
59
+
60
+func (c *secureConn) Close() error {
61
+ if err := c.secure.Close(); err != nil {
62
+ c.insecure.Close()
63
+ return err
64
+ }
65
+ return c.insecure.Close()
66
+}
67
+
68
+// ID is an identifier unique to this connection.
69
+func (c *secureConn) ID() string {
70
+ return ID(c)
71
+}
72
+
73
+func (c *secureConn) String() string {
74
+ return String(c, "secureConn")
75
+}
76
+
77
+func (c *secureConn) LocalAddr() net.Addr {
78
+ return c.insecure.LocalAddr()
79
+}
80
+
81
+func (c *secureConn) RemoteAddr() net.Addr {
82
+ return c.insecure.RemoteAddr()
83
+}
84
+
85
+func (c *secureConn) SetDeadline(t time.Time) error {
86
+ return c.insecure.SetDeadline(t)
87
+}
88
+
89
+func (c *secureConn) SetReadDeadline(t time.Time) error {
90
+ return c.insecure.SetReadDeadline(t)
91
+}
92
+
93
+func (c *secureConn) SetWriteDeadline(t time.Time) error {
94
+ return c.insecure.SetWriteDeadline(t)
95
+}
96
+
97
+// LocalMultiaddr is the Multiaddr on this side
98
+func (c *secureConn) LocalMultiaddr() ma.Multiaddr {
99
+ return c.insecure.LocalMultiaddr()
100
+}
101
+
102
+// RemoteMultiaddr is the Multiaddr on the remote side
103
+func (c *secureConn) RemoteMultiaddr() ma.Multiaddr {
104
+ return c.insecure.RemoteMultiaddr()
105
+}
106
+
107
+// LocalPeer is the Peer on this side
108
+func (c *secureConn) LocalPeer() peer.ID {
109
+ return c.session.LocalPeer()
110
+}
111
+
112
+// RemotePeer is the Peer on the remote side
113
+func (c *secureConn) RemotePeer() peer.ID {
114
+ return c.session.RemotePeer()
115
+}
116
+
117
+// LocalPrivateKey is the public key of the peer on this side
118
+func (c *secureConn) LocalPrivateKey() ic.PrivKey {
119
+ return c.session.LocalPrivateKey()
120
+}
121
+
122
+// RemotePubKey is the public key of the peer on the remote side
123
+func (c *secureConn) RemotePublicKey() ic.PubKey {
124
+ return c.session.RemotePublicKey()
125
+}
126
+
127
+// Read reads data, net.Conn style
128
+func (c *secureConn) Read(buf []byte) (int, error) {
129
+ return c.secure.Read(buf)
130
+}
131
+
132
+// Write writes data, net.Conn style
133
+func (c *secureConn) Write(buf []byte) (int, error) {
134
+ return c.secure.Write(buf)
135
+}
136
+
137
+func (c *secureConn) NextMsgLen() (int, error) {
138
+ return c.secure.NextMsgLen()
139
+}
140
+
141
+// ReadMsg reads data, net.Conn style
142
+func (c *secureConn) ReadMsg() ([]byte, error) {
143
+ return c.secure.ReadMsg()
144
+}
145
+
146
+// WriteMsg writes data, net.Conn style
147
+func (c *secureConn) WriteMsg(buf []byte) error {
148
+ return c.secure.WriteMsg(buf)
149
+}
150
+
151
+// ReleaseMsg releases a buffer
152
+func (c *secureConn) ReleaseMsg(m []byte) {
153
+ c.secure.ReleaseMsg(m)
154
+}
p2p/net2/conn/secure_conn_test.go
new
+199
@@ -0,0 +1,199 @@
1
+package conn
2
+
3
+import (
4
+ "bytes"
5
+ "os"
6
+ "runtime"
7
+ "sync"
8
+ "testing"
9
+ "time"
10
+
11
+ ic "github.com/jbenet/go-ipfs/p2p/crypto"
12
+
13
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
14
+)
15
+
16
+func upgradeToSecureConn(t *testing.T, ctx context.Context, sk ic.PrivKey, c Conn) (Conn, error) {
17
+ if c, ok := c.(*secureConn); ok {
18
+ return c, nil
19
+ }
20
+
21
+ // shouldn't happen, because dial + listen already return secure conns.
22
+ s, err := newSecureConn(ctx, sk, c)
23
+ if err != nil {
24
+ return nil, err
25
+ }
26
+ return s, nil
27
+}
28
+
29
+func secureHandshake(t *testing.T, ctx context.Context, sk ic.PrivKey, c Conn, done chan error) {
30
+ _, err := upgradeToSecureConn(t, ctx, sk, c)
31
+ done <- err
32
+}
33
+
34
+func TestSecureSimple(t *testing.T) {
35
+ // t.Skip("Skipping in favor of another test")
36
+
37
+ numMsgs := 100
38
+ if testing.Short() {
39
+ numMsgs = 10
40
+ }
41
+
42
+ ctx := context.Background()
43
+ c1, c2, p1, p2 := setupSingleConn(t, ctx)
44
+
45
+ done := make(chan error)
46
+ go secureHandshake(t, ctx, p1.PrivKey, c1, done)
47
+ go secureHandshake(t, ctx, p2.PrivKey, c2, done)
48
+
49
+ for i := 0; i < 2; i++ {
50
+ if err := <-done; err != nil {
51
+ t.Fatal(err)
52
+ }
53
+ }
54
+
55
+ for i := 0; i < numMsgs; i++ {
56
+ testOneSendRecv(t, c1, c2)
57
+ testOneSendRecv(t, c2, c1)
58
+ }
59
+
60
+ c1.Close()
61
+ c2.Close()
62
+}
63
+
64
+func TestSecureClose(t *testing.T) {
65
+ // t.Skip("Skipping in favor of another test")
66
+
67
+ ctx := context.Background()
68
+ c1, c2, p1, p2 := setupSingleConn(t, ctx)
69
+
70
+ done := make(chan error)
71
+ go secureHandshake(t, ctx, p1.PrivKey, c1, done)
72
+ go secureHandshake(t, ctx, p2.PrivKey, c2, done)
73
+
74
+ for i := 0; i < 2; i++ {
75
+ if err := <-done; err != nil {
76
+ t.Fatal(err)
77
+ }
78
+ }
79
+
80
+ testOneSendRecv(t, c1, c2)
81
+
82
+ c1.Close()
83
+ testNotOneSendRecv(t, c1, c2)
84
+
85
+ c2.Close()
86
+ testNotOneSendRecv(t, c1, c2)
87
+ testNotOneSendRecv(t, c2, c1)
88
+
89
+}
90
+
91
+func TestSecureCancelHandshake(t *testing.T) {
92
+ // t.Skip("Skipping in favor of another test")
93
+
94
+ ctx, cancel := context.WithCancel(context.Background())
95
+ c1, c2, p1, p2 := setupSingleConn(t, ctx)
96
+
97
+ done := make(chan error)
98
+ go secureHandshake(t, ctx, p1.PrivKey, c1, done)
99
+ <-time.After(time.Millisecond)
100
+ cancel() // cancel ctx
101
+ go secureHandshake(t, ctx, p2.PrivKey, c2, done)
102
+
103
+ for i := 0; i < 2; i++ {
104
+ if err := <-done; err == nil {
105
+ t.Error("cancel should've errored out")
106
+ }
107
+ }
108
+}
109
+
110
+func TestSecureHandshakeFailsWithWrongKeys(t *testing.T) {
111
+ // t.Skip("Skipping in favor of another test")
112
+
113
+ ctx, cancel := context.WithCancel(context.Background())
114
+ defer cancel()
115
+ c1, c2, p1, p2 := setupSingleConn(t, ctx)
116
+
117
+ done := make(chan error)
118
+ go secureHandshake(t, ctx, p2.PrivKey, c1, done)
119
+ go secureHandshake(t, ctx, p1.PrivKey, c2, done)
120
+
121
+ for i := 0; i < 2; i++ {
122
+ if err := <-done; err == nil {
123
+ t.Fatal("wrong keys should've errored out.")
124
+ }
125
+ }
126
+}
127
+
128
+func TestSecureCloseLeak(t *testing.T) {
129
+ // t.Skip("Skipping in favor of another test")
130
+
131
+ if testing.Short() {
132
+ t.SkipNow()
133
+ }
134
+ if os.Getenv("TRAVIS") == "true" {
135
+ t.Skip("this doesn't work well on travis")
136
+ }
137
+
138
+ runPair := func(c1, c2 Conn, num int) {
139
+ log.Debugf("runPair %d", num)
140
+
141
+ for i := 0; i < num; i++ {
142
+ log.Debugf("runPair iteration %d", i)
143
+ b1 := []byte("beep")
144
+ c1.WriteMsg(b1)
145
+ b2, err := c2.ReadMsg()
146
+ if err != nil {
147
+ panic(err)
148
+ }
149
+ if !bytes.Equal(b1, b2) {
150
+ panic("bytes not equal")
151
+ }
152
+
153
+ b2 = []byte("beep")
154
+ c2.WriteMsg(b2)
155
+ b1, err = c1.ReadMsg()
156
+ if err != nil {
157
+ panic(err)
158
+ }
159
+ if !bytes.Equal(b1, b2) {
160
+ panic("bytes not equal")
161
+ }
162
+
163
+ <-time.After(time.Microsecond * 5)
164
+ }
165
+ }
166
+
167
+ var cons = 5
168
+ var msgs = 50
169
+ log.Debugf("Running %d connections * %d msgs.\n", cons, msgs)
170
+
171
+ var wg sync.WaitGroup
172
+ for i := 0; i < cons; i++ {
173
+ wg.Add(1)
174
+
175
+ ctx, cancel := context.WithCancel(context.Background())
176
+ c1, c2, _, _ := setupSecureConn(t, ctx)
177
+ go func(c1, c2 Conn) {
178
+
179
+ defer func() {
180
+ c1.Close()
181
+ c2.Close()
182
+ cancel()
183
+ wg.Done()
184
+ }()
185
+
186
+ runPair(c1, c2, msgs)
187
+ }(c1, c2)
188
+ }
189
+
190
+ log.Debugf("Waiting...\n")
191
+ wg.Wait()
192
+ // done!
193
+
194
+ <-time.After(time.Millisecond * 150)
195
+ if runtime.NumGoroutine() > 20 {
196
+ // panic("uncomment me to debug")
197
+ t.Fatal("leaking goroutines:", runtime.NumGoroutine())
198
+ }
199
+}
p2p/net2/interface.go
new
+133
@@ -0,0 +1,133 @@
1
+package net
2
+
3
+import (
4
+ "io"
5
+
6
+ conn "github.com/jbenet/go-ipfs/p2p/net2/conn"
7
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
8
+
9
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
10
+ ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
11
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
12
+)
13
+
14
+// MessageSizeMax is a soft (recommended) maximum for network messages.
15
+// One can write more, as the interface is a stream. But it is useful
16
+// to bunch it up into multiple read/writes when the whole message is
17
+// a single, large serialized object.
18
+const MessageSizeMax = 2 << 22 // 4MB
19
+
20
+// Stream represents a bidirectional channel between two agents in
21
+// the IPFS network. "agent" is as granular as desired, potentially
22
+// being a "request -> reply" pair, or whole protocols.
23
+// Streams are backed by SPDY streams underneath the hood.
24
+type Stream interface {
25
+ io.Reader
26
+ io.Writer
27
+ io.Closer
28
+
29
+ // Conn returns the connection this stream is part of.
30
+ Conn() Conn
31
+}
32
+
33
+// StreamHandler is the type of function used to listen for
34
+// streams opened by the remote side.
35
+type StreamHandler func(Stream)
36
+
37
+// Conn is a connection to a remote peer. It multiplexes streams.
38
+// Usually there is no need to use a Conn directly, but it may
39
+// be useful to get information about the peer on the other side:
40
+// stream.Conn().RemotePeer()
41
+type Conn interface {
42
+ conn.PeerConn
43
+
44
+ // NewStream constructs a new Stream over this conn.
45
+ NewStream() (Stream, error)
46
+}
47
+
48
+// ConnHandler is the type of function used to listen for
49
+// connections opened by the remote side.
50
+type ConnHandler func(Conn)
51
+
52
+// Network is the interface used to connect to the outside world.
53
+// It dials and listens for connections. it uses a Swarm to pool
54
+// connnections (see swarm pkg, and peerstream.Swarm). Connections
55
+// are encrypted with a TLS-like protocol.
56
+type Network interface {
57
+ Dialer
58
+ io.Closer
59
+
60
+ // SetStreamHandler sets the handler for new streams opened by the
61
+ // remote side. This operation is threadsafe.
62
+ SetStreamHandler(StreamHandler)
63
+
64
+ // SetConnHandler sets the handler for new connections opened by the
65
+ // remote side. This operation is threadsafe.
66
+ SetConnHandler(ConnHandler)
67
+
68
+ // NewStream returns a new stream to given peer p.
69
+ // If there is no connection to p, attempts to create one.
70
+ NewStream(peer.ID) (Stream, error)
71
+
72
+ // ListenAddresses returns a list of addresses at which this network listens.
73
+ ListenAddresses() []ma.Multiaddr
74
+
75
+ // InterfaceListenAddresses returns a list of addresses at which this network
76
+ // listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to
77
+ // use the known local interfaces.
78
+ InterfaceListenAddresses() ([]ma.Multiaddr, error)
79
+
80
+ // CtxGroup returns the network's contextGroup
81
+ CtxGroup() ctxgroup.ContextGroup
82
+}
83
+
84
+// Dialer represents a service that can dial out to peers
85
+// (this is usually just a Network, but other services may not need the whole
86
+// stack, and thus it becomes easier to mock)
87
+type Dialer interface {
88
+
89
+ // Peerstore returns the internal peerstore
90
+ // This is useful to tell the dialer about a new address for a peer.
91
+ // Or use one of the public keys found out over the network.
92
+ Peerstore() peer.Peerstore
93
+
94
+ // LocalPeer returns the local peer associated with this network
95
+ LocalPeer() peer.ID
96
+
97
+ // DialPeer establishes a connection to a given peer
98
+ DialPeer(context.Context, peer.ID) (Conn, error)
99
+
100
+ // ClosePeer closes the connection to a given peer
101
+ ClosePeer(peer.ID) error
102
+
103
+ // Connectedness returns a state signaling connection capabilities
104
+ Connectedness(peer.ID) Connectedness
105
+
106
+ // Peers returns the peers connected
107
+ Peers() []peer.ID
108
+
109
+ // Conns returns the connections in this Netowrk
110
+ Conns() []Conn
111
+
112
+ // ConnsToPeer returns the connections in this Netowrk for given peer.
113
+ ConnsToPeer(p peer.ID) []Conn
114
+}
115
+
116
+// Connectedness signals the capacity for a connection with a given node.
117
+// It is used to signal to services and other peers whether a node is reachable.
118
+type Connectedness int
119
+
120
+const (
121
+ // NotConnected means no connection to peer, and no extra information (default)
122
+ NotConnected Connectedness = iota
123
+
124
+ // Connected means has an open, live connection to peer
125
+ Connected
126
+
127
+ // CanConnect means recently connected to peer, terminated gracefully
128
+ CanConnect
129
+
130
+ // CannotConnect means recently attempted connecting but failed to connect.
131
+ // (should signal "made effort, failed")
132
+ CannotConnect
133
+)
p2p/net2/mock/interface.go
new
+98
@@ -0,0 +1,98 @@
1
+// Package mocknet provides a mock net.Network to test with.
2
+//
3
+// - a Mocknet has many inet.Networks
4
+// - a Mocknet has many Links
5
+// - a Link joins two inet.Networks
6
+// - inet.Conns and inet.Streams are created by inet.Networks
7
+package mocknet
8
+
9
+import (
10
+ "io"
11
+ "time"
12
+
13
+ ic "github.com/jbenet/go-ipfs/p2p/crypto"
14
+ inet "github.com/jbenet/go-ipfs/p2p/net2"
15
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
16
+
17
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
18
+)
19
+
20
+type Mocknet interface {
21
+
22
+ // GenPeer generates a peer and its inet.Network in the Mocknet
23
+ GenPeer() (inet.Network, error)
24
+
25
+ // AddPeer adds an existing peer. we need both a privkey and addr.
26
+ // ID is derived from PrivKey
27
+ AddPeer(ic.PrivKey, ma.Multiaddr) (inet.Network, error)
28
+
29
+ // retrieve things (with randomized iteration order)
30
+ Peers() []peer.ID
31
+ Net(peer.ID) inet.Network
32
+ Nets() []inet.Network
33
+ Links() LinkMap
34
+ LinksBetweenPeers(a, b peer.ID) []Link
35
+ LinksBetweenNets(a, b inet.Network) []Link
36
+
37
+ // Links are the **ability to connect**.
38
+ // think of Links as the physical medium.
39
+ // For p1 and p2 to connect, a link must exist between them.
40
+ // (this makes it possible to test dial failures, and
41
+ // things like relaying traffic)
42
+ LinkPeers(peer.ID, peer.ID) (Link, error)
43
+ LinkNets(inet.Network, inet.Network) (Link, error)
44
+ Unlink(Link) error
45
+ UnlinkPeers(peer.ID, peer.ID) error
46
+ UnlinkNets(inet.Network, inet.Network) error
47
+
48
+ // LinkDefaults are the default options that govern links
49
+ // if they do not have thier own option set.
50
+ SetLinkDefaults(LinkOptions)
51
+ LinkDefaults() LinkOptions
52
+
53
+ // Connections are the usual. Connecting means Dialing.
54
+ // **to succeed, peers must be linked beforehand**
55
+ ConnectPeers(peer.ID, peer.ID) (inet.Conn, error)
56
+ ConnectNets(inet.Network, inet.Network) (inet.Conn, error)
57
+ DisconnectPeers(peer.ID, peer.ID) error
58
+ DisconnectNets(inet.Network, inet.Network) error
59
+}
60
+
61
+// LinkOptions are used to change aspects of the links.
62
+// Sorry but they dont work yet :(
63
+type LinkOptions struct {
64
+ Latency time.Duration
65
+ Bandwidth int // in bytes-per-second
66
+ // we can make these values distributions down the road.
67
+}
68
+
69
+// Link represents the **possibility** of a connection between
70
+// two peers. Think of it like physical network links. Without
71
+// them, the peers can try and try but they won't be able to
72
+// connect. This allows constructing topologies where specific
73
+// nodes cannot talk to each other directly. :)
74
+type Link interface {
75
+ Networks() []inet.Network
76
+ Peers() []peer.ID
77
+
78
+ SetOptions(LinkOptions)
79
+ Options() LinkOptions
80
+
81
+ // Metrics() Metrics
82
+}
83
+
84
+// LinkMap is a 3D map to give us an easy way to track links.
85
+// (wow, much map. so data structure. how compose. ahhh pointer)
86
+type LinkMap map[string]map[string]map[Link]struct{}
87
+
88
+// Printer lets you inspect things :)
89
+type Printer interface {
90
+ // MocknetLinks shows the entire Mocknet's link table :)
91
+ MocknetLinks(mn Mocknet)
92
+ NetworkConns(ni inet.Network)
93
+}
94
+
95
+// PrinterTo returns a Printer ready to write to w.
96
+func PrinterTo(w io.Writer) Printer {
97
+ return &printer{w}
98
+}
p2p/net2/mock/mock.go
new
+63
@@ -0,0 +1,63 @@
1
+package mocknet
2
+
3
+import (
4
+ eventlog "github.com/jbenet/go-ipfs/util/eventlog"
5
+
6
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
7
+)
8
+
9
+var log = eventlog.Logger("mocknet")
10
+
11
+// WithNPeers constructs a Mocknet with N peers.
12
+func WithNPeers(ctx context.Context, n int) (Mocknet, error) {
13
+ m := New(ctx)
14
+ for i := 0; i < n; i++ {
15
+ if _, err := m.GenPeer(); err != nil {
16
+ return nil, err
17
+ }
18
+ }
19
+ return m, nil
20
+}
21
+
22
+// FullMeshLinked constructs a Mocknet with full mesh of Links.
23
+// This means that all the peers **can** connect to each other
24
+// (not that they already are connected. you can use m.ConnectAll())
25
+func FullMeshLinked(ctx context.Context, n int) (Mocknet, error) {
26
+ m, err := WithNPeers(ctx, n)
27
+ if err != nil {
28
+ return nil, err
29
+ }
30
+
31
+ nets := m.Nets()
32
+ for _, n1 := range nets {
33
+ for _, n2 := range nets {
34
+ // yes, even self.
35
+ if _, err := m.LinkNets(n1, n2); err != nil {
36
+ return nil, err
37
+ }
38
+ }
39
+ }
40
+
41
+ return m, nil
42
+}
43
+
44
+// FullMeshConnected constructs a Mocknet with full mesh of Connections.
45
+// This means that all the peers have dialed and are ready to talk to
46
+// each other.
47
+func FullMeshConnected(ctx context.Context, n int) (Mocknet, error) {
48
+ m, err := FullMeshLinked(ctx, n)
49
+ if err != nil {
50
+ return nil, err
51
+ }
52
+
53
+ nets := m.Nets()
54
+ for _, n1 := range nets {
55
+ for _, n2 := range nets {
56
+ if _, err := m.ConnectNets(n1, n2); err != nil {
57
+ return nil, err
58
+ }
59
+ }
60
+ }
61
+
62
+ return m, nil
63
+}
p2p/net2/mock/mock_conn.go
new
+120
@@ -0,0 +1,120 @@
1
+package mocknet
2
+
3
+import (
4
+ "container/list"
5
+ "sync"
6
+
7
+ ic "github.com/jbenet/go-ipfs/p2p/crypto"
8
+ inet "github.com/jbenet/go-ipfs/p2p/net2"
9
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
10
+
11
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
12
+)
13
+
14
+// conn represents one side's perspective of a
15
+// live connection between two peers.
16
+// it goes over a particular link.
17
+type conn struct {
18
+ local peer.ID
19
+ remote peer.ID
20
+
21
+ localAddr ma.Multiaddr
22
+ remoteAddr ma.Multiaddr
23
+
24
+ localPrivKey ic.PrivKey
25
+ remotePubKey ic.PubKey
26
+
27
+ net *peernet
28
+ link *link
29
+ rconn *conn // counterpart
30
+ streams list.List
31
+
32
+ sync.RWMutex
33
+}
34
+
35
+func (c *conn) Close() error {
36
+ for _, s := range c.allStreams() {
37
+ s.Close()
38
+ }
39
+ c.net.removeConn(c)
40
+ return nil
41
+}
42
+
43
+func (c *conn) addStream(s *stream) {
44
+ c.Lock()
45
+ s.conn = c
46
+ c.streams.PushBack(s)
47
+ c.Unlock()
48
+}
49
+
50
+func (c *conn) removeStream(s *stream) {
51
+ c.Lock()
52
+ defer c.Unlock()
53
+ for e := c.streams.Front(); e != nil; e = e.Next() {
54
+ if s == e.Value {
55
+ c.streams.Remove(e)
56
+ return
57
+ }
58
+ }
59
+}
60
+
61
+func (c *conn) allStreams() []inet.Stream {
62
+ c.RLock()
63
+ defer c.RUnlock()
64
+
65
+ strs := make([]inet.Stream, 0, c.streams.Len())
66
+ for e := c.streams.Front(); e != nil; e = e.Next() {
67
+ s := e.Value.(*stream)
68
+ strs = append(strs, s)
69
+ }
70
+ return strs
71
+}
72
+
73
+func (c *conn) remoteOpenedStream(s *stream) {
74
+ c.addStream(s)
75
+ c.net.handleNewStream(s)
76
+}
77
+
78
+func (c *conn) openStream() *stream {
79
+ sl, sr := c.link.newStreamPair()
80
+ c.addStream(sl)
81
+ c.rconn.remoteOpenedStream(sr)
82
+ return sl
83
+}
84
+
85
+func (c *conn) NewStream() (inet.Stream, error) {
86
+ log.Debugf("Conn.NewStreamWithProtocol: %s --> %s", c.local, c.remote)
87
+
88
+ s := c.openStream()
89
+ return s, nil
90
+}
91
+
92
+// LocalMultiaddr is the Multiaddr on this side
93
+func (c *conn) LocalMultiaddr() ma.Multiaddr {
94
+ return c.localAddr
95
+}
96
+
97
+// LocalPeer is the Peer on our side of the connection
98
+func (c *conn) LocalPeer() peer.ID {
99
+ return c.local
100
+}
101
+
102
+// LocalPrivateKey is the private key of the peer on our side.
103
+func (c *conn) LocalPrivateKey() ic.PrivKey {
104
+ return c.localPrivKey
105
+}
106
+
107
+// RemoteMultiaddr is the Multiaddr on the remote side
108
+func (c *conn) RemoteMultiaddr() ma.Multiaddr {
109
+ return c.remoteAddr
110
+}
111
+
112
+// RemotePeer is the Peer on the remote side
113
+func (c *conn) RemotePeer() peer.ID {
114
+ return c.remote
115
+}
116
+
117
+// RemotePublicKey is the private key of the peer on our side.
118
+func (c *conn) RemotePublicKey() ic.PubKey {
119
+ return c.remotePubKey
120
+}
p2p/net2/mock/mock_link.go
new
+93
@@ -0,0 +1,93 @@
1
+package mocknet
2
+
3
+import (
4
+ "io"
5
+ "sync"
6
+
7
+ inet "github.com/jbenet/go-ipfs/p2p/net2"
8
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
9
+)
10
+
11
+// link implements mocknet.Link
12
+// and, for simplicity, inet.Conn
13
+type link struct {
14
+ mock *mocknet
15
+ nets []*peernet
16
+ opts LinkOptions
17
+
18
+ // this could have addresses on both sides.
19
+
20
+ sync.RWMutex
21
+}
22
+
23
+func newLink(mn *mocknet, opts LinkOptions) *link {
24
+ return &link{mock: mn, opts: opts}
25
+}
26
+
27
+func (l *link) newConnPair(dialer *peernet) (*conn, *conn) {
28
+ l.RLock()
29
+ defer l.RUnlock()
30
+
31
+ mkconn := func(ln, rn *peernet) *conn {
32
+ c := &conn{net: ln, link: l}
33
+ c.local = ln.peer
34
+ c.remote = rn.peer
35
+
36
+ c.localAddr = ln.ps.Addresses(ln.peer)[0]
37
+ c.remoteAddr = rn.ps.Addresses(rn.peer)[0]
38
+
39
+ c.localPrivKey = ln.ps.PrivKey(ln.peer)
40
+ c.remotePubKey = rn.ps.PubKey(rn.peer)
41
+
42
+ return c
43
+ }
44
+
45
+ c1 := mkconn(l.nets[0], l.nets[1])
46
+ c2 := mkconn(l.nets[1], l.nets[0])
47
+ c1.rconn = c2
48
+ c2.rconn = c1
49
+
50
+ if dialer == c1.net {
51
+ return c1, c2
52
+ }
53
+ return c2, c1
54
+}
55
+
56
+func (l *link) newStreamPair() (*stream, *stream) {
57
+ r1, w1 := io.Pipe()
58
+ r2, w2 := io.Pipe()
59
+
60
+ s1 := &stream{Reader: r1, Writer: w2}
61
+ s2 := &stream{Reader: r2, Writer: w1}
62
+ return s1, s2
63
+}
64
+
65
+func (l *link) Networks() []inet.Network {
66
+ l.RLock()
67
+ defer l.RUnlock()
68
+
69
+ cp := make([]inet.Network, len(l.nets))
70
+ for i, n := range l.nets {
71
+ cp[i] = n
72
+ }
73
+ return cp
74
+}
75
+
76
+func (l *link) Peers() []peer.ID {
77
+ l.RLock()
78
+ defer l.RUnlock()
79
+
80
+ cp := make([]peer.ID, len(l.nets))
81
+ for i, n := range l.nets {
82
+ cp[i] = n.peer
83
+ }
84
+ return cp
85
+}
86
+
87
+func (l *link) SetOptions(o LinkOptions) {
88
+ l.opts = o
89
+}
90
+
91
+func (l *link) Options() LinkOptions {
92
+ return l.opts
93
+}
p2p/net2/mock/mock_net.go
new
+322
@@ -0,0 +1,322 @@
1
+package mocknet
2
+
3
+import (
4
+ "fmt"
5
+ "sync"
6
+
7
+ ic "github.com/jbenet/go-ipfs/p2p/crypto"
8
+ inet "github.com/jbenet/go-ipfs/p2p/net2"
9
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
10
+ testutil "github.com/jbenet/go-ipfs/util/testutil"
11
+
12
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
13
+ ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
14
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
15
+)
16
+
17
+// mocknet implements mocknet.Mocknet
18
+type mocknet struct {
19
+ // must map on peer.ID (instead of peer.ID) because
20
+ // each inet.Network has different peerstore
21
+ nets map[peer.ID]*peernet
22
+
23
+ // links make it possible to connect two peers.
24
+ // think of links as the physical medium.
25
+ // usually only one, but there could be multiple
26
+ // **links are shared between peers**
27
+ links map[peer.ID]map[peer.ID]map[*link]struct{}
28
+
29
+ linkDefaults LinkOptions
30
+
31
+ cg ctxgroup.ContextGroup // for Context closing
32
+ sync.RWMutex
33
+}
34
+
35
+func New(ctx context.Context) Mocknet {
36
+ return &mocknet{
37
+ nets: map[peer.ID]*peernet{},
38
+ links: map[peer.ID]map[peer.ID]map[*link]struct{}{},
39
+ cg: ctxgroup.WithContext(ctx),
40
+ }
41
+}
42
+
43
+func (mn *mocknet) GenPeer() (inet.Network, error) {
44
+ sk, _, err := testutil.RandKeyPair(512)
45
+ if err != nil {
46
+ return nil, err
47
+ }
48
+
49
+ a := testutil.RandLocalTCPAddress()
50
+
51
+ n, err := mn.AddPeer(sk, a)
52
+ if err != nil {
53
+ return nil, err
54
+ }
55
+
56
+ return n, nil
57
+}
58
+
59
+func (mn *mocknet) AddPeer(k ic.PrivKey, a ma.Multiaddr) (inet.Network, error) {
60
+ n, err := newPeernet(mn.cg.Context(), mn, k, a)
61
+ if err != nil {
62
+ return nil, err
63
+ }
64
+
65
+ // make sure to add listening address!
66
+ // this makes debugging things simpler as remembering to register
67
+ // an address may cause unexpected failure.
68
+ n.Peerstore().AddAddress(n.LocalPeer(), a)
69
+ log.Debugf("mocknet added listen addr for peer: %s -- %s", n.LocalPeer(), a)
70
+
71
+ mn.cg.AddChildGroup(n.cg)
72
+
73
+ mn.Lock()
74
+ mn.nets[n.peer] = n
75
+ mn.Unlock()
76
+ return n, nil
77
+}
78
+
79
+func (mn *mocknet) Peers() []peer.ID {
80
+ mn.RLock()
81
+ defer mn.RUnlock()
82
+
83
+ cp := make([]peer.ID, 0, len(mn.nets))
84
+ for _, n := range mn.nets {
85
+ cp = append(cp, n.peer)
86
+ }
87
+ return cp
88
+}
89
+
90
+func (mn *mocknet) Net(pid peer.ID) inet.Network {
91
+ mn.RLock()
92
+ defer mn.RUnlock()
93
+
94
+ for _, n := range mn.nets {
95
+ if n.peer == pid {
96
+ return n
97
+ }
98
+ }
99
+ return nil
100
+}
101
+
102
+func (mn *mocknet) Nets() []inet.Network {
103
+ mn.RLock()
104
+ defer mn.RUnlock()
105
+
106
+ cp := make([]inet.Network, 0, len(mn.nets))
107
+ for _, n := range mn.nets {
108
+ cp = append(cp, n)
109
+ }
110
+ return cp
111
+}
112
+
113
+// Links returns a copy of the internal link state map.
114
+// (wow, much map. so data structure. how compose. ahhh pointer)
115
+func (mn *mocknet) Links() LinkMap {
116
+ mn.RLock()
117
+ defer mn.RUnlock()
118
+
119
+ links := map[string]map[string]map[Link]struct{}{}
120
+ for p1, lm := range mn.links {
121
+ sp1 := string(p1)
122
+ links[sp1] = map[string]map[Link]struct{}{}
123
+ for p2, ls := range lm {
124
+ sp2 := string(p2)
125
+ links[sp1][sp2] = map[Link]struct{}{}
126
+ for l := range ls {
127
+ links[sp1][sp2][l] = struct{}{}
128
+ }
129
+ }
130
+ }
131
+ return links
132
+}
133
+
134
+func (mn *mocknet) LinkAll() error {
135
+ nets := mn.Nets()
136
+ for _, n1 := range nets {
137
+ for _, n2 := range nets {
138
+ if _, err := mn.LinkNets(n1, n2); err != nil {
139
+ return err
140
+ }
141
+ }
142
+ }
143
+ return nil
144
+}
145
+
146
+func (mn *mocknet) LinkPeers(p1, p2 peer.ID) (Link, error) {
147
+ mn.RLock()
148
+ n1 := mn.nets[p1]
149
+ n2 := mn.nets[p2]
150
+ mn.RUnlock()
151
+
152
+ if n1 == nil {
153
+ return nil, fmt.Errorf("network for p1 not in mocknet")
154
+ }
155
+
156
+ if n2 == nil {
157
+ return nil, fmt.Errorf("network for p2 not in mocknet")
158
+ }
159
+
160
+ return mn.LinkNets(n1, n2)
161
+}
162
+
163
+func (mn *mocknet) validate(n inet.Network) (*peernet, error) {
164
+ // WARNING: assumes locks acquired
165
+
166
+ nr, ok := n.(*peernet)
167
+ if !ok {
168
+ return nil, fmt.Errorf("Network not supported (use mock package nets only)")
169
+ }
170
+
171
+ if _, found := mn.nets[nr.peer]; !found {
172
+ return nil, fmt.Errorf("Network not on mocknet. is it from another mocknet?")
173
+ }
174
+
175
+ return nr, nil
176
+}
177
+
178
+func (mn *mocknet) LinkNets(n1, n2 inet.Network) (Link, error) {
179
+ mn.RLock()
180
+ n1r, err1 := mn.validate(n1)
181
+ n2r, err2 := mn.validate(n2)
182
+ ld := mn.linkDefaults
183
+ mn.RUnlock()
184
+
185
+ if err1 != nil {
186
+ return nil, err1
187
+ }
188
+ if err2 != nil {
189
+ return nil, err2
190
+ }
191
+
192
+ l := newLink(mn, ld)
193
+ l.nets = append(l.nets, n1r, n2r)
194
+ mn.addLink(l)
195
+ return l, nil
196
+}
197
+
198
+func (mn *mocknet) Unlink(l2 Link) error {
199
+
200
+ l, ok := l2.(*link)
201
+ if !ok {
202
+ return fmt.Errorf("only links from mocknet are supported")
203
+ }
204
+
205
+ mn.removeLink(l)
206
+ return nil
207
+}
208
+
209
+func (mn *mocknet) UnlinkPeers(p1, p2 peer.ID) error {
210
+ ls := mn.LinksBetweenPeers(p1, p2)
211
+ if ls == nil {
212
+ return fmt.Errorf("no link between p1 and p2")
213
+ }
214
+
215
+ for _, l := range ls {
216
+ if err := mn.Unlink(l); err != nil {
217
+ return err
218
+ }
219
+ }
220
+ return nil
221
+}
222
+
223
+func (mn *mocknet) UnlinkNets(n1, n2 inet.Network) error {
224
+ return mn.UnlinkPeers(n1.LocalPeer(), n2.LocalPeer())
225
+}
226
+
227
+// get from the links map. and lazily contruct.
228
+func (mn *mocknet) linksMapGet(p1, p2 peer.ID) *map[*link]struct{} {
229
+
230
+ l1, found := mn.links[p1]
231
+ if !found {
232
+ mn.links[p1] = map[peer.ID]map[*link]struct{}{}
233
+ l1 = mn.links[p1] // so we make sure it's there.
234
+ }
235
+
236
+ l2, found := l1[p2]
237
+ if !found {
238
+ m := map[*link]struct{}{}
239
+ l1[p2] = m
240
+ l2 = l1[p2]
241
+ }
242
+
243
+ return &l2
244
+}
245
+
246
+func (mn *mocknet) addLink(l *link) {
247
+ mn.Lock()
248
+ defer mn.Unlock()
249
+
250
+ n1, n2 := l.nets[0], l.nets[1]
251
+ (*mn.linksMapGet(n1.peer, n2.peer))[l] = struct{}{}
252
+ (*mn.linksMapGet(n2.peer, n1.peer))[l] = struct{}{}
253
+}
254
+
255
+func (mn *mocknet) removeLink(l *link) {
256
+ mn.Lock()
257
+ defer mn.Unlock()
258
+
259
+ n1, n2 := l.nets[0], l.nets[1]
260
+ delete(*mn.linksMapGet(n1.peer, n2.peer), l)
261
+ delete(*mn.linksMapGet(n2.peer, n1.peer), l)
262
+}
263
+
264
+func (mn *mocknet) ConnectAll() error {
265
+ nets := mn.Nets()
266
+ for _, n1 := range nets {
267
+ for _, n2 := range nets {
268
+ if n1 == n2 {
269
+ continue
270
+ }
271
+
272
+ if _, err := mn.ConnectNets(n1, n2); err != nil {
273
+ return err
274
+ }
275
+ }
276
+ }
277
+ return nil
278
+}
279
+
280
+func (mn *mocknet) ConnectPeers(a, b peer.ID) (inet.Conn, error) {
281
+ return mn.Net(a).DialPeer(mn.cg.Context(), b)
282
+}
283
+
284
+func (mn *mocknet) ConnectNets(a, b inet.Network) (inet.Conn, error) {
285
+ return a.DialPeer(mn.cg.Context(), b.LocalPeer())
286
+}
287
+
288
+func (mn *mocknet) DisconnectPeers(p1, p2 peer.ID) error {
289
+ return mn.Net(p1).ClosePeer(p2)
290
+}
291
+
292
+func (mn *mocknet) DisconnectNets(n1, n2 inet.Network) error {
293
+ return n1.ClosePeer(n2.LocalPeer())
294
+}
295
+
296
+func (mn *mocknet) LinksBetweenPeers(p1, p2 peer.ID) []Link {
297
+ mn.RLock()
298
+ defer mn.RUnlock()
299
+
300
+ ls2 := *mn.linksMapGet(p1, p2)
301
+ cp := make([]Link, 0, len(ls2))
302
+ for l := range ls2 {
303
+ cp = append(cp, l)
304
+ }
305
+ return cp
306
+}
307
+
308
+func (mn *mocknet) LinksBetweenNets(n1, n2 inet.Network) []Link {
309
+ return mn.LinksBetweenPeers(n1.LocalPeer(), n2.LocalPeer())
310
+}
311
+
312
+func (mn *mocknet) SetLinkDefaults(o LinkOptions) {
313
+ mn.Lock()
314
+ mn.linkDefaults = o
315
+ mn.Unlock()
316
+}
317
+
318
+func (mn *mocknet) LinkDefaults() LinkOptions {
319
+ mn.RLock()
320
+ defer mn.RUnlock()
321
+ return mn.linkDefaults
322
+}
p2p/net2/mock/mock_peernet.go
new
+353
@@ -0,0 +1,353 @@
1
+package mocknet
2
+
3
+import (
4
+ "fmt"
5
+ "math/rand"
6
+ "sync"
7
+
8
+ ic "github.com/jbenet/go-ipfs/p2p/crypto"
9
+ inet "github.com/jbenet/go-ipfs/p2p/net2"
10
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
11
+
12
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
13
+ ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
14
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
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
+ cg ctxgroup.ContextGroup
35
+ sync.RWMutex
36
+}
37
+
38
+// newPeernet constructs a new peernet
39
+func newPeernet(ctx context.Context, m *mocknet, k ic.PrivKey,
40
+ a ma.Multiaddr) (*peernet, error) {
41
+
42
+ p, err := peer.IDFromPublicKey(k.GetPublic())
43
+ if err != nil {
44
+ return nil, err
45
+ }
46
+
47
+ // create our own entirely, so that peers knowledge doesn't get shared
48
+ ps := peer.NewPeerstore()
49
+ ps.AddAddress(p, a)
50
+ ps.AddPrivKey(p, k)
51
+ ps.AddPubKey(p, k.GetPublic())
52
+
53
+ n := &peernet{
54
+ mocknet: m,
55
+ peer: p,
56
+ ps: ps,
57
+ cg: ctxgroup.WithContext(ctx),
58
+
59
+ connsByPeer: map[peer.ID]map[*conn]struct{}{},
60
+ connsByLink: map[*link]map[*conn]struct{}{},
61
+ }
62
+
63
+ n.cg.SetTeardown(n.teardown)
64
+ return n, nil
65
+}
66
+
67
+func (pn *peernet) teardown() error {
68
+
69
+ // close the connections
70
+ for _, c := range pn.allConns() {
71
+ c.Close()
72
+ }
73
+ return nil
74
+}
75
+
76
+// allConns returns all the connections between this peer and others
77
+func (pn *peernet) allConns() []*conn {
78
+ pn.RLock()
79
+ var cs []*conn
80
+ for _, csl := range pn.connsByPeer {
81
+ for c := range csl {
82
+ cs = append(cs, c)
83
+ }
84
+ }
85
+ pn.RUnlock()
86
+ return cs
87
+}
88
+
89
+// Close calls the ContextCloser func
90
+func (pn *peernet) Close() error {
91
+ return pn.cg.Close()
92
+}
93
+
94
+func (pn *peernet) Peerstore() peer.Peerstore {
95
+ return pn.ps
96
+}
97
+
98
+func (pn *peernet) String() string {
99
+ return fmt.Sprintf("<mock.peernet %s - %d conns>", pn.peer, len(pn.allConns()))
100
+}
101
+
102
+// handleNewStream is an internal function to trigger the client's handler
103
+func (pn *peernet) handleNewStream(s inet.Stream) {
104
+ pn.RLock()
105
+ handler := pn.streamHandler
106
+ pn.RUnlock()
107
+ if handler != nil {
108
+ go handler(s)
109
+ }
110
+}
111
+
112
+// handleNewConn is an internal function to trigger the client's handler
113
+func (pn *peernet) handleNewConn(c inet.Conn) {
114
+ pn.RLock()
115
+ handler := pn.connHandler
116
+ pn.RUnlock()
117
+ if handler != nil {
118
+ go handler(c)
119
+ }
120
+}
121
+
122
+// DialPeer attempts to establish a connection to a given peer.
123
+// Respects the context.
124
+func (pn *peernet) DialPeer(ctx context.Context, p peer.ID) (inet.Conn, error) {
125
+ return pn.connect(p)
126
+}
127
+
128
+func (pn *peernet) connect(p peer.ID) (*conn, error) {
129
+ // first, check if we already have live connections
130
+ pn.RLock()
131
+ cs, found := pn.connsByPeer[p]
132
+ pn.RUnlock()
133
+ if found && len(cs) > 0 {
134
+ for c := range cs {
135
+ return c, nil
136
+ }
137
+ }
138
+
139
+ log.Debugf("%s (newly) dialing %s", pn.peer, p)
140
+
141
+ // ok, must create a new connection. we need a link
142
+ links := pn.mocknet.LinksBetweenPeers(pn.peer, p)
143
+ if len(links) < 1 {
144
+ return nil, fmt.Errorf("%s cannot connect to %s", pn.peer, p)
145
+ }
146
+
147
+ // if many links found, how do we select? for now, randomly...
148
+ // this would be an interesting place to test logic that can measure
149
+ // links (network interfaces) and select properly
150
+ l := links[rand.Intn(len(links))]
151
+
152
+ log.Debugf("%s dialing %s openingConn", pn.peer, p)
153
+ // create a new connection with link
154
+ c := pn.openConn(p, l.(*link))
155
+ return c, nil
156
+}
157
+
158
+func (pn *peernet) openConn(r peer.ID, l *link) *conn {
159
+ lc, rc := l.newConnPair(pn)
160
+ log.Debugf("%s opening connection to %s", pn.LocalPeer(), lc.RemotePeer())
161
+ pn.addConn(lc)
162
+ rc.net.remoteOpenedConn(rc)
163
+ return lc
164
+}
165
+
166
+func (pn *peernet) remoteOpenedConn(c *conn) {
167
+ log.Debugf("%s accepting connection from %s", pn.LocalPeer(), c.RemotePeer())
168
+ pn.addConn(c)
169
+ pn.handleNewConn(c)
170
+}
171
+
172
+// addConn constructs and adds a connection
173
+// to given remote peer over given link
174
+func (pn *peernet) addConn(c *conn) {
175
+ pn.Lock()
176
+ defer pn.Unlock()
177
+
178
+ cs, found := pn.connsByPeer[c.RemotePeer()]
179
+ if !found {
180
+ cs = map[*conn]struct{}{}
181
+ pn.connsByPeer[c.RemotePeer()] = cs
182
+ }
183
+ pn.connsByPeer[c.RemotePeer()][c] = struct{}{}
184
+
185
+ cs, found = pn.connsByLink[c.link]
186
+ if !found {
187
+ cs = map[*conn]struct{}{}
188
+ pn.connsByLink[c.link] = cs
189
+ }
190
+ pn.connsByLink[c.link][c] = struct{}{}
191
+}
192
+
193
+// removeConn removes a given conn
194
+func (pn *peernet) removeConn(c *conn) {
195
+ pn.Lock()
196
+ defer pn.Unlock()
197
+
198
+ cs, found := pn.connsByLink[c.link]
199
+ if !found || len(cs) < 1 {
200
+ panic("attempting to remove a conn that doesnt exist")
201
+ }
202
+ delete(cs, c)
203
+
204
+ cs, found = pn.connsByPeer[c.remote]
205
+ if !found {
206
+ panic("attempting to remove a conn that doesnt exist")
207
+ }
208
+ delete(cs, c)
209
+}
210
+
211
+// CtxGroup returns the network's ContextGroup
212
+func (pn *peernet) CtxGroup() ctxgroup.ContextGroup {
213
+ return pn.cg
214
+}
215
+
216
+// LocalPeer the network's LocalPeer
217
+func (pn *peernet) LocalPeer() peer.ID {
218
+ return pn.peer
219
+}
220
+
221
+// Peers returns the connected peers
222
+func (pn *peernet) Peers() []peer.ID {
223
+ pn.RLock()
224
+ defer pn.RUnlock()
225
+
226
+ peers := make([]peer.ID, 0, len(pn.connsByPeer))
227
+ for _, cs := range pn.connsByPeer {
228
+ for c := range cs {
229
+ peers = append(peers, c.remote)
230
+ break
231
+ }
232
+ }
233
+ return peers
234
+}
235
+
236
+// Conns returns all the connections of this peer
237
+func (pn *peernet) Conns() []inet.Conn {
238
+ pn.RLock()
239
+ defer pn.RUnlock()
240
+
241
+ out := make([]inet.Conn, 0, len(pn.connsByPeer))
242
+ for _, cs := range pn.connsByPeer {
243
+ for c := range cs {
244
+ out = append(out, c)
245
+ }
246
+ }
247
+ return out
248
+}
249
+
250
+func (pn *peernet) ConnsToPeer(p peer.ID) []inet.Conn {
251
+ pn.RLock()
252
+ defer pn.RUnlock()
253
+
254
+ cs, found := pn.connsByPeer[p]
255
+ if !found || len(cs) == 0 {
256
+ return nil
257
+ }
258
+
259
+ var cs2 []inet.Conn
260
+ for c := range cs {
261
+ cs2 = append(cs2, c)
262
+ }
263
+ return cs2
264
+}
265
+
266
+// ClosePeer connections to peer
267
+func (pn *peernet) ClosePeer(p peer.ID) error {
268
+ pn.RLock()
269
+ cs, found := pn.connsByPeer[p]
270
+ pn.RUnlock()
271
+ if !found {
272
+ return nil
273
+ }
274
+
275
+ for c := range cs {
276
+ c.Close()
277
+ }
278
+ return nil
279
+}
280
+
281
+// BandwidthTotals returns the total amount of bandwidth transferred
282
+func (pn *peernet) BandwidthTotals() (in uint64, out uint64) {
283
+ // need to implement this. probably best to do it in swarm this time.
284
+ // need a "metrics" object
285
+ return 0, 0
286
+}
287
+
288
+// ListenAddresses returns a list of addresses at which this network listens.
289
+func (pn *peernet) ListenAddresses() []ma.Multiaddr {
290
+ return pn.Peerstore().Addresses(pn.LocalPeer())
291
+}
292
+
293
+// InterfaceListenAddresses returns a list of addresses at which this network
294
+// listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to
295
+// use the known local interfaces.
296
+func (pn *peernet) InterfaceListenAddresses() ([]ma.Multiaddr, error) {
297
+ return pn.ListenAddresses(), nil
298
+}
299
+
300
+// Connectedness returns a state signaling connection capabilities
301
+// For now only returns Connecter || NotConnected. Expand into more later.
302
+func (pn *peernet) Connectedness(p peer.ID) inet.Connectedness {
303
+ pn.Lock()
304
+ defer pn.Unlock()
305
+
306
+ cs, found := pn.connsByPeer[p]
307
+ if found && len(cs) > 0 {
308
+ return inet.Connected
309
+ }
310
+ return inet.NotConnected
311
+}
312
+
313
+// NewStream returns a new stream to given peer p.
314
+// If there is no connection to p, attempts to create one.
315
+func (pn *peernet) NewStream(p peer.ID) (inet.Stream, error) {
316
+ pn.Lock()
317
+ cs, found := pn.connsByPeer[p]
318
+ if !found || len(cs) < 1 {
319
+ pn.Unlock()
320
+ return nil, fmt.Errorf("no connection to peer")
321
+ }
322
+ pn.Unlock()
323
+
324
+ // if many conns are found, how do we select? for now, randomly...
325
+ // this would be an interesting place to test logic that can measure
326
+ // links (network interfaces) and select properly
327
+ n := rand.Intn(len(cs))
328
+ var c *conn
329
+ for c = range cs {
330
+ if n == 0 {
331
+ break
332
+ }
333
+ n--
334
+ }
335
+
336
+ return c.NewStream()
337
+}
338
+
339
+// SetStreamHandler sets the new stream handler on the Network.
340
+// This operation is threadsafe.
341
+func (pn *peernet) SetStreamHandler(h inet.StreamHandler) {
342
+ pn.Lock()
343
+ pn.streamHandler = h
344
+ pn.Unlock()
345
+}
346
+
347
+// SetConnHandler sets the new conn handler on the Network.
348
+// This operation is threadsafe.
349
+func (pn *peernet) SetConnHandler(h inet.ConnHandler) {
350
+ pn.Lock()
351
+ pn.connHandler = h
352
+ pn.Unlock()
353
+}
p2p/net2/mock/mock_printer.go
new
+36
@@ -0,0 +1,36 @@
1
+package mocknet
2
+
3
+import (
4
+ "fmt"
5
+ "io"
6
+
7
+ inet "github.com/jbenet/go-ipfs/p2p/net2"
8
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
9
+)
10
+
11
+// separate object so our interfaces are separate :)
12
+type printer struct {
13
+ w io.Writer
14
+}
15
+
16
+func (p *printer) MocknetLinks(mn Mocknet) {
17
+ links := mn.Links()
18
+
19
+ fmt.Fprintf(p.w, "Mocknet link map:\n")
20
+ for p1, lm := range links {
21
+ fmt.Fprintf(p.w, "\t%s linked to:\n", peer.ID(p1))
22
+ for p2, l := range lm {
23
+ fmt.Fprintf(p.w, "\t\t%s (%d links)\n", peer.ID(p2), len(l))
24
+ }
25
+ }
26
+ fmt.Fprintf(p.w, "\n")
27
+}
28
+
29
+func (p *printer) NetworkConns(ni inet.Network) {
30
+
31
+ fmt.Fprintf(p.w, "%s connected to:\n", ni.LocalPeer())
32
+ for _, c := range ni.Conns() {
33
+ fmt.Fprintf(p.w, "\t%s (addr: %s)\n", c.RemotePeer(), c.RemoteMultiaddr())
34
+ }
35
+ fmt.Fprintf(p.w, "\n")
36
+}
p2p/net2/mock/mock_stream.go
new
+29
@@ -0,0 +1,29 @@
1
+package mocknet
2
+
3
+import (
4
+ "io"
5
+
6
+ inet "github.com/jbenet/go-ipfs/p2p/net2"
7
+)
8
+
9
+// stream implements inet.Stream
10
+type stream struct {
11
+ io.Reader
12
+ io.Writer
13
+ conn *conn
14
+}
15
+
16
+func (s *stream) Close() error {
17
+ s.conn.removeStream(s)
18
+ if r, ok := (s.Reader).(io.Closer); ok {
19
+ r.Close()
20
+ }
21
+ if w, ok := (s.Writer).(io.Closer); ok {
22
+ return w.Close()
23
+ }
24
+ return nil
25
+}
26
+
27
+func (s *stream) Conn() inet.Conn {
28
+ return s.conn
29
+}
p2p/net2/mock/mock_test.go
new
+460
@@ -0,0 +1,460 @@
1
+package mocknet
2
+
3
+import (
4
+ "bytes"
5
+ "io"
6
+ "math/rand"
7
+ "sync"
8
+ "testing"
9
+
10
+ inet "github.com/jbenet/go-ipfs/p2p/net2"
11
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
12
+ testutil "github.com/jbenet/go-ipfs/util/testutil"
13
+
14
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
15
+)
16
+
17
+func randPeer(t *testing.T) peer.ID {
18
+ p, err := testutil.RandPeerID()
19
+ if err != nil {
20
+ t.Fatal(err)
21
+ }
22
+ return p
23
+}
24
+
25
+func TestNetworkSetup(t *testing.T) {
26
+
27
+ ctx := context.Background()
28
+ sk1, _, err := testutil.RandKeyPair(512)
29
+ if err != nil {
30
+ t.Fatal(t)
31
+ }
32
+ sk2, _, err := testutil.RandKeyPair(512)
33
+ if err != nil {
34
+ t.Fatal(t)
35
+ }
36
+ sk3, _, err := testutil.RandKeyPair(512)
37
+ if err != nil {
38
+ t.Fatal(t)
39
+ }
40
+ mn := New(ctx)
41
+ // peers := []peer.ID{p1, p2, p3}
42
+
43
+ // add peers to mock net
44
+
45
+ a1 := testutil.RandLocalTCPAddress()
46
+ a2 := testutil.RandLocalTCPAddress()
47
+ a3 := testutil.RandLocalTCPAddress()
48
+
49
+ n1, err := mn.AddPeer(sk1, a1)
50
+ if err != nil {
51
+ t.Fatal(err)
52
+ }
53
+ p1 := n1.LocalPeer()
54
+
55
+ n2, err := mn.AddPeer(sk2, a2)
56
+ if err != nil {
57
+ t.Fatal(err)
58
+ }
59
+ p2 := n2.LocalPeer()
60
+
61
+ n3, err := mn.AddPeer(sk3, a3)
62
+ if err != nil {
63
+ t.Fatal(err)
64
+ }
65
+ p3 := n3.LocalPeer()
66
+
67
+ // check peers and net
68
+ if mn.Net(p1) != n1 {
69
+ t.Error("net for p1.ID != n1")
70
+ }
71
+ if mn.Net(p2) != n2 {
72
+ t.Error("net for p2.ID != n1")
73
+ }
74
+ if mn.Net(p3) != n3 {
75
+ t.Error("net for p3.ID != n1")
76
+ }
77
+
78
+ // link p1<-->p2, p1<-->p1, p2<-->p3, p3<-->p2
79
+
80
+ l12, err := mn.LinkPeers(p1, p2)
81
+ if err != nil {
82
+ t.Fatal(err)
83
+ }
84
+ if !(l12.Networks()[0] == n1 && l12.Networks()[1] == n2) &&
85
+ !(l12.Networks()[0] == n2 && l12.Networks()[1] == n1) {
86
+ t.Error("l12 networks incorrect")
87
+ }
88
+
89
+ l11, err := mn.LinkPeers(p1, p1)
90
+ if err != nil {
91
+ t.Fatal(err)
92
+ }
93
+ if !(l11.Networks()[0] == n1 && l11.Networks()[1] == n1) {
94
+ t.Error("l11 networks incorrect")
95
+ }
96
+
97
+ l23, err := mn.LinkPeers(p2, p3)
98
+ if err != nil {
99
+ t.Fatal(err)
100
+ }
101
+ if !(l23.Networks()[0] == n2 && l23.Networks()[1] == n3) &&
102
+ !(l23.Networks()[0] == n3 && l23.Networks()[1] == n2) {
103
+ t.Error("l23 networks incorrect")
104
+ }
105
+
106
+ l32, err := mn.LinkPeers(p3, p2)
107
+ if err != nil {
108
+ t.Fatal(err)
109
+ }
110
+ if !(l32.Networks()[0] == n2 && l32.Networks()[1] == n3) &&
111
+ !(l32.Networks()[0] == n3 && l32.Networks()[1] == n2) {
112
+ t.Error("l32 networks incorrect")
113
+ }
114
+
115
+ // check things
116
+
117
+ links12 := mn.LinksBetweenPeers(p1, p2)
118
+ if len(links12) != 1 {
119
+ t.Errorf("should be 1 link bt. p1 and p2 (found %d)", len(links12))
120
+ }
121
+ if links12[0] != l12 {
122
+ t.Error("links 1-2 should be l12.")
123
+ }
124
+
125
+ links11 := mn.LinksBetweenPeers(p1, p1)
126
+ if len(links11) != 1 {
127
+ t.Errorf("should be 1 link bt. p1 and p1 (found %d)", len(links11))
128
+ }
129
+ if links11[0] != l11 {
130
+ t.Error("links 1-1 should be l11.")
131
+ }
132
+
133
+ links23 := mn.LinksBetweenPeers(p2, p3)
134
+ if len(links23) != 2 {
135
+ t.Errorf("should be 2 link bt. p2 and p3 (found %d)", len(links23))
136
+ }
137
+ if !((links23[0] == l23 && links23[1] == l32) ||
138
+ (links23[0] == l32 && links23[1] == l23)) {
139
+ t.Error("links 2-3 should be l23 and l32.")
140
+ }
141
+
142
+ // unlinking
143
+
144
+ if err := mn.UnlinkPeers(p2, p1); err != nil {
145
+ t.Error(err)
146
+ }
147
+
148
+ // check only one link affected:
149
+
150
+ links12 = mn.LinksBetweenPeers(p1, p2)
151
+ if len(links12) != 0 {
152
+ t.Errorf("should be 0 now...", len(links12))
153
+ }
154
+
155
+ links11 = mn.LinksBetweenPeers(p1, p1)
156
+ if len(links11) != 1 {
157
+ t.Errorf("should be 1 link bt. p1 and p1 (found %d)", len(links11))
158
+ }
159
+ if links11[0] != l11 {
160
+ t.Error("links 1-1 should be l11.")
161
+ }
162
+
163
+ links23 = mn.LinksBetweenPeers(p2, p3)
164
+ if len(links23) != 2 {
165
+ t.Errorf("should be 2 link bt. p2 and p3 (found %d)", len(links23))
166
+ }
167
+ if !((links23[0] == l23 && links23[1] == l32) ||
168
+ (links23[0] == l32 && links23[1] == l23)) {
169
+ t.Error("links 2-3 should be l23 and l32.")
170
+ }
171
+
172
+ // check connecting
173
+
174
+ // first, no conns
175
+ if len(n2.Conns()) > 0 || len(n3.Conns()) > 0 {
176
+ t.Error("should have 0 conn. Got: (%d, %d)", len(n2.Conns()), len(n3.Conns()))
177
+ }
178
+
179
+ // connect p2->p3
180
+ if _, err := n2.DialPeer(ctx, p3); err != nil {
181
+ t.Error(err)
182
+ }
183
+
184
+ if len(n2.Conns()) != 1 || len(n3.Conns()) != 1 {
185
+ t.Errorf("should have (1,1) conn. Got: (%d, %d)", len(n2.Conns()), len(n3.Conns()))
186
+ }
187
+
188
+ // p := PrinterTo(os.Stdout)
189
+ // p.NetworkConns(n1)
190
+ // p.NetworkConns(n2)
191
+ // p.NetworkConns(n3)
192
+
193
+ // can create a stream 2->3, 3->2,
194
+ if _, err := n2.NewStream(p3); err != nil {
195
+ t.Error(err)
196
+ }
197
+ if _, err := n3.NewStream(p2); err != nil {
198
+ t.Error(err)
199
+ }
200
+
201
+ // but not 1->2 nor 2->2 (not linked), nor 1->1 (not connected)
202
+ if _, err := n1.NewStream(p2); err == nil {
203
+ t.Error("should not be able to connect")
204
+ }
205
+ if _, err := n2.NewStream(p2); err == nil {
206
+ t.Error("should not be able to connect")
207
+ }
208
+ if _, err := n1.NewStream(p1); err == nil {
209
+ t.Error("should not be able to connect")
210
+ }
211
+
212
+ // connect p1->p1 (should work)
213
+ if _, err := n1.DialPeer(ctx, p1); err != nil {
214
+ t.Error("p1 should be able to dial self.", err)
215
+ }
216
+
217
+ // and a stream too
218
+ if _, err := n1.NewStream(p1); err != nil {
219
+ t.Error(err)
220
+ }
221
+
222
+ // connect p1->p2
223
+ if _, err := n1.DialPeer(ctx, p2); err == nil {
224
+ t.Error("p1 should not be able to dial p2, not connected...")
225
+ }
226
+
227
+ // connect p3->p1
228
+ if _, err := n3.DialPeer(ctx, p1); err == nil {
229
+ t.Error("p3 should not be able to dial p1, not connected...")
230
+ }
231
+
232
+ // relink p1->p2
233
+
234
+ l12, err = mn.LinkPeers(p1, p2)
235
+ if err != nil {
236
+ t.Fatal(err)
237
+ }
238
+ if !(l12.Networks()[0] == n1 && l12.Networks()[1] == n2) &&
239
+ !(l12.Networks()[0] == n2 && l12.Networks()[1] == n1) {
240
+ t.Error("l12 networks incorrect")
241
+ }
242
+
243
+ // should now be able to connect
244
+
245
+ // connect p1->p2
246
+ if _, err := n1.DialPeer(ctx, p2); err != nil {
247
+ t.Error(err)
248
+ }
249
+
250
+ // and a stream should work now too :)
251
+ if _, err := n2.NewStream(p3); err != nil {
252
+ t.Error(err)
253
+ }
254
+
255
+}
256
+
257
+func TestStreams(t *testing.T) {
258
+
259
+ mn, err := FullMeshConnected(context.Background(), 3)
260
+ if err != nil {
261
+ t.Fatal(err)
262
+ }
263
+
264
+ handler := func(s inet.Stream) {
265
+ b := make([]byte, 4)
266
+ if _, err := io.ReadFull(s, b); err != nil {
267
+ panic(err)
268
+ }
269
+ if !bytes.Equal(b, []byte("beep")) {
270
+ panic("bytes mismatch")
271
+ }
272
+ if _, err := s.Write([]byte("boop")); err != nil {
273
+ panic(err)
274
+ }
275
+ s.Close()
276
+ }
277
+
278
+ nets := mn.Nets()
279
+ for _, n := range nets {
280
+ n.SetStreamHandler(handler)
281
+ }
282
+
283
+ s, err := nets[0].NewStream(nets[1].LocalPeer())
284
+ if err != nil {
285
+ t.Fatal(err)
286
+ }
287
+
288
+ if _, err := s.Write([]byte("beep")); err != nil {
289
+ panic(err)
290
+ }
291
+ b := make([]byte, 4)
292
+ if _, err := io.ReadFull(s, b); err != nil {
293
+ panic(err)
294
+ }
295
+ if !bytes.Equal(b, []byte("boop")) {
296
+ panic("bytes mismatch 2")
297
+ }
298
+
299
+}
300
+
301
+func makePinger(st string, n int) func(inet.Stream) {
302
+ return func(s inet.Stream) {
303
+ go func() {
304
+ defer s.Close()
305
+
306
+ for i := 0; i < n; i++ {
307
+ b := make([]byte, 4+len(st))
308
+ if _, err := s.Write([]byte("ping" + st)); err != nil {
309
+ panic(err)
310
+ }
311
+ if _, err := io.ReadFull(s, b); err != nil {
312
+ panic(err)
313
+ }
314
+ if !bytes.Equal(b, []byte("pong"+st)) {
315
+ panic("bytes mismatch")
316
+ }
317
+ }
318
+ }()
319
+ }
320
+}
321
+
322
+func makePonger(st string) func(inet.Stream) {
323
+ return func(s inet.Stream) {
324
+ go func() {
325
+ defer s.Close()
326
+
327
+ for {
328
+ b := make([]byte, 4+len(st))
329
+ if _, err := io.ReadFull(s, b); err != nil {
330
+ if err == io.EOF {
331
+ return
332
+ }
333
+ panic(err)
334
+ }
335
+ if !bytes.Equal(b, []byte("ping"+st)) {
336
+ panic("bytes mismatch")
337
+ }
338
+ if _, err := s.Write([]byte("pong" + st)); err != nil {
339
+ panic(err)
340
+ }
341
+ }
342
+ }()
343
+ }
344
+}
345
+
346
+func TestStreamsStress(t *testing.T) {
347
+
348
+ mn, err := FullMeshConnected(context.Background(), 100)
349
+ if err != nil {
350
+ t.Fatal(err)
351
+ }
352
+
353
+ nets := mn.Nets()
354
+ for _, n := range nets {
355
+ n.SetStreamHandler(makePonger("pingpong"))
356
+ }
357
+
358
+ var wg sync.WaitGroup
359
+ for i := 0; i < 1000; i++ {
360
+ wg.Add(1)
361
+ go func(i int) {
362
+ defer wg.Done()
363
+ from := rand.Intn(len(nets))
364
+ to := rand.Intn(len(nets))
365
+ s, err := nets[from].NewStream(nets[to].LocalPeer())
366
+ if err != nil {
367
+ log.Debugf("%d (%s) %d (%s)", from, nets[from], to, nets[to])
368
+ panic(err)
369
+ }
370
+
371
+ log.Infof("%d start pinging", i)
372
+ makePinger("pingpong", rand.Intn(100))(s)
373
+ log.Infof("%d done pinging", i)
374
+ }(i)
375
+ }
376
+
377
+ wg.Wait()
378
+}
379
+
380
+func TestAdding(t *testing.T) {
381
+
382
+ mn := New(context.Background())
383
+
384
+ peers := []peer.ID{}
385
+ for i := 0; i < 3; i++ {
386
+ sk, _, err := testutil.RandKeyPair(512)
387
+ if err != nil {
388
+ t.Fatal(err)
389
+ }
390
+
391
+ a := testutil.RandLocalTCPAddress()
392
+ n, err := mn.AddPeer(sk, a)
393
+ if err != nil {
394
+ t.Fatal(err)
395
+ }
396
+
397
+ peers = append(peers, n.LocalPeer())
398
+ }
399
+
400
+ p1 := peers[0]
401
+ p2 := peers[1]
402
+
403
+ // link them
404
+ for _, p1 := range peers {
405
+ for _, p2 := range peers {
406
+ if _, err := mn.LinkPeers(p1, p2); err != nil {
407
+ t.Error(err)
408
+ }
409
+ }
410
+ }
411
+
412
+ // set the new stream handler on p2
413
+ n2 := mn.Net(p2)
414
+ if n2 == nil {
415
+ t.Fatalf("no network for %s", p2)
416
+ }
417
+ n2.SetStreamHandler(func(s inet.Stream) {
418
+ defer s.Close()
419
+
420
+ b := make([]byte, 4)
421
+ if _, err := io.ReadFull(s, b); err != nil {
422
+ panic(err)
423
+ }
424
+ if string(b) != "beep" {
425
+ panic("did not beep!")
426
+ }
427
+
428
+ if _, err := s.Write([]byte("boop")); err != nil {
429
+ panic(err)
430
+ }
431
+ })
432
+
433
+ // connect p1 to p2
434
+ if _, err := mn.ConnectPeers(p1, p2); err != nil {
435
+ t.Fatal(err)
436
+ }
437
+
438
+ // talk to p2
439
+ n1 := mn.Net(p1)
440
+ if n1 == nil {
441
+ t.Fatalf("no network for %s", p1)
442
+ }
443
+
444
+ s, err := n1.NewStream(p2)
445
+ if err != nil {
446
+ t.Fatal(err)
447
+ }
448
+
449
+ if _, err := s.Write([]byte("beep")); err != nil {
450
+ t.Error(err)
451
+ }
452
+ b := make([]byte, 4)
453
+ if _, err := io.ReadFull(s, b); err != nil {
454
+ t.Error(err)
455
+ }
456
+ if !bytes.Equal(b, []byte("boop")) {
457
+ t.Error("bytes mismatch 2")
458
+ }
459
+
460
+}
p2p/net2/swarm/addr.go
new
+124
@@ -0,0 +1,124 @@
1
+package swarm
2
+
3
+import (
4
+ conn "github.com/jbenet/go-ipfs/p2p/net/conn"
5
+ eventlog "github.com/jbenet/go-ipfs/util/eventlog"
6
+
7
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
8
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
9
+ manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
10
+)
11
+
12
+// ListenAddresses returns a list of addresses at which this swarm listens.
13
+func (s *Swarm) ListenAddresses() []ma.Multiaddr {
14
+ listeners := s.swarm.Listeners()
15
+ addrs := make([]ma.Multiaddr, 0, len(listeners))
16
+ for _, l := range listeners {
17
+ if l2, ok := l.NetListener().(conn.Listener); ok {
18
+ addrs = append(addrs, l2.Multiaddr())
19
+ }
20
+ }
21
+ return addrs
22
+}
23
+
24
+// InterfaceListenAddresses returns a list of addresses at which this swarm
25
+// listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to
26
+// use the known local interfaces.
27
+func InterfaceListenAddresses(s *Swarm) ([]ma.Multiaddr, error) {
28
+ return resolveUnspecifiedAddresses(s.ListenAddresses())
29
+}
30
+
31
+// resolveUnspecifiedAddresses expands unspecified ip addresses (/ip4/0.0.0.0, /ip6/::) to
32
+// use the known local interfaces.
33
+func resolveUnspecifiedAddresses(unspecifiedAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) {
34
+ var outputAddrs []ma.Multiaddr
35
+
36
+ // todo optimize: only fetch these if we have a "any" addr.
37
+ ifaceAddrs, err := interfaceAddresses()
38
+ if err != nil {
39
+ return nil, err
40
+ }
41
+
42
+ for _, a := range unspecifiedAddrs {
43
+
44
+ // split address into its components
45
+ split := ma.Split(a)
46
+
47
+ // if first component (ip) is not unspecified, use it as is.
48
+ if !manet.IsIPUnspecified(split[0]) {
49
+ outputAddrs = append(outputAddrs, a)
50
+ continue
51
+ }
52
+
53
+ // unspecified? add one address per interface.
54
+ for _, ia := range ifaceAddrs {
55
+ split[0] = ia
56
+ joined := ma.Join(split...)
57
+ outputAddrs = append(outputAddrs, joined)
58
+ }
59
+ }
60
+
61
+ log.Event(context.TODO(), "interfaceListenAddresses", func() eventlog.Loggable {
62
+ var addrs []string
63
+ for _, addr := range outputAddrs {
64
+ addrs = append(addrs, addr.String())
65
+ }
66
+ return eventlog.Metadata{"addresses": addrs}
67
+ }())
68
+ log.Debug("InterfaceListenAddresses:", outputAddrs)
69
+ return outputAddrs, nil
70
+}
71
+
72
+// interfaceAddresses returns a list of addresses associated with local machine
73
+func interfaceAddresses() ([]ma.Multiaddr, error) {
74
+ maddrs, err := manet.InterfaceMultiaddrs()
75
+ if err != nil {
76
+ return nil, err
77
+ }
78
+
79
+ var nonLoopback []ma.Multiaddr
80
+ for _, a := range maddrs {
81
+ if !manet.IsIPLoopback(a) {
82
+ nonLoopback = append(nonLoopback, a)
83
+ }
84
+ }
85
+
86
+ return nonLoopback, nil
87
+}
88
+
89
+// addrInList returns whether or not an address is part of a list.
90
+// this is useful to check if NAT is happening (or other bugs?)
91
+func addrInList(addr ma.Multiaddr, list []ma.Multiaddr) bool {
92
+ for _, addr2 := range list {
93
+ if addr.Equal(addr2) {
94
+ return true
95
+ }
96
+ }
97
+ return false
98
+}
99
+
100
+// checkNATWarning checks if our observed addresses differ. if so,
101
+// informs the user that certain things might not work yet
102
+func checkNATWarning(s *Swarm, observed ma.Multiaddr, expected ma.Multiaddr) {
103
+ if observed.Equal(expected) {
104
+ return
105
+ }
106
+
107
+ listen, err := InterfaceListenAddresses(s)
108
+ if err != nil {
109
+ log.Errorf("Error retrieving swarm.InterfaceListenAddresses: %s", err)
110
+ return
111
+ }
112
+
113
+ if !addrInList(observed, listen) { // probably a nat
114
+ log.Warningf(natWarning, observed, listen)
115
+ }
116
+}
117
+
118
+const natWarning = `Remote peer observed our address to be: %s
119
+The local addresses are: %s
120
+Thus, connection is going through NAT, and other connections may fail.
121
+
122
+IPFS NAT traversal is still under development. Please bug us on github or irc to fix this.
123
+Baby steps: http://jbenet.static.s3.amazonaws.com/271dfcf/baby-steps.gif
124
+`
p2p/net2/swarm/simul_test.go
new
+66
@@ -0,0 +1,66 @@
1
+package swarm
2
+
3
+import (
4
+ "sync"
5
+ "testing"
6
+ "time"
7
+
8
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
9
+
10
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
11
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
12
+)
13
+
14
+func TestSimultOpen(t *testing.T) {
15
+ // t.Skip("skipping for another test")
16
+
17
+ ctx := context.Background()
18
+ swarms, peers := makeSwarms(ctx, t, 2)
19
+
20
+ // connect everyone
21
+ {
22
+ var wg sync.WaitGroup
23
+ connect := func(s *Swarm, dst peer.ID, addr ma.Multiaddr) {
24
+ // copy for other peer
25
+ s.peers.AddAddress(dst, addr)
26
+ if _, err := s.Dial(ctx, dst); err != nil {
27
+ t.Fatal("error swarm dialing to peer", err)
28
+ }
29
+ wg.Done()
30
+ }
31
+
32
+ log.Info("Connecting swarms simultaneously.")
33
+ wg.Add(2)
34
+ go connect(swarms[0], swarms[1].local, peers[1].Addr)
35
+ go connect(swarms[1], swarms[0].local, peers[0].Addr)
36
+ wg.Wait()
37
+ }
38
+
39
+ for _, s := range swarms {
40
+ s.Close()
41
+ }
42
+}
43
+
44
+func TestSimultOpenMany(t *testing.T) {
45
+ // t.Skip("very very slow")
46
+
47
+ addrs := 20
48
+ SubtestSwarm(t, addrs, 10)
49
+}
50
+
51
+func TestSimultOpenFewStress(t *testing.T) {
52
+ if testing.Short() {
53
+ t.SkipNow()
54
+ }
55
+ // t.Skip("skipping for another test")
56
+
57
+ msgs := 40
58
+ swarms := 2
59
+ rounds := 10
60
+ // rounds := 100
61
+
62
+ for i := 0; i < rounds; i++ {
63
+ SubtestSwarm(t, swarms, msgs)
64
+ <-time.After(10 * time.Millisecond)
65
+ }
66
+}
p2p/net2/swarm/swarm.go
new
+158
@@ -0,0 +1,158 @@
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
+ inet "github.com/jbenet/go-ipfs/p2p/net2"
7
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
8
+ eventlog "github.com/jbenet/go-ipfs/util/eventlog"
9
+
10
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
11
+ ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
12
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
13
+ ps "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-peerstream"
14
+)
15
+
16
+var log = eventlog.Logger("swarm2")
17
+
18
+// Swarm is a connection muxer, allowing connections to other peers to
19
+// be opened and closed, while still using the same Chan for all
20
+// communication. The Chan sends/receives Messages, which note the
21
+// destination or source Peer.
22
+//
23
+// Uses peerstream.Swarm
24
+type Swarm struct {
25
+ swarm *ps.Swarm
26
+ local peer.ID
27
+ peers peer.Peerstore
28
+ connh ConnHandler
29
+
30
+ cg ctxgroup.ContextGroup
31
+}
32
+
33
+// NewSwarm constructs a Swarm, with a Chan.
34
+func NewSwarm(ctx context.Context, listenAddrs []ma.Multiaddr,
35
+ local peer.ID, peers peer.Peerstore) (*Swarm, error) {
36
+
37
+ s := &Swarm{
38
+ swarm: ps.NewSwarm(),
39
+ local: local,
40
+ peers: peers,
41
+ cg: ctxgroup.WithContext(ctx),
42
+ }
43
+
44
+ // configure Swarm
45
+ s.cg.SetTeardown(s.teardown)
46
+ s.SetConnHandler(nil) // make sure to setup our own conn handler.
47
+
48
+ return s, s.listen(listenAddrs)
49
+}
50
+
51
+func (s *Swarm) teardown() error {
52
+ return s.swarm.Close()
53
+}
54
+
55
+// CtxGroup returns the Context Group of the swarm
56
+func (s *Swarm) CtxGroup() ctxgroup.ContextGroup {
57
+ return s.cg
58
+}
59
+
60
+// Close stops the Swarm.
61
+func (s *Swarm) Close() error {
62
+ return s.cg.Close()
63
+}
64
+
65
+// StreamSwarm returns the underlying peerstream.Swarm
66
+func (s *Swarm) StreamSwarm() *ps.Swarm {
67
+ return s.swarm
68
+}
69
+
70
+// SetConnHandler assigns the handler for new connections.
71
+// See peerstream. You will rarely use this. See SetStreamHandler
72
+func (s *Swarm) SetConnHandler(handler ConnHandler) {
73
+
74
+ // handler is nil if user wants to clear the old handler.
75
+ if handler == nil {
76
+ s.swarm.SetConnHandler(func(psconn *ps.Conn) {
77
+ s.connHandler(psconn)
78
+ })
79
+ return
80
+ }
81
+
82
+ s.swarm.SetConnHandler(func(psconn *ps.Conn) {
83
+ // sc is nil if closed in our handler.
84
+ if sc := s.connHandler(psconn); sc != nil {
85
+ // call the user's handler. in a goroutine for sync safety.
86
+ go handler(sc)
87
+ }
88
+ })
89
+}
90
+
91
+// SetStreamHandler assigns the handler for new streams.
92
+// See peerstream.
93
+func (s *Swarm) SetStreamHandler(handler inet.StreamHandler) {
94
+ s.swarm.SetStreamHandler(func(s *ps.Stream) {
95
+ handler(wrapStream(s))
96
+ })
97
+}
98
+
99
+// NewStreamWithPeer creates a new stream on any available connection to p
100
+func (s *Swarm) NewStreamWithPeer(p peer.ID) (*Stream, error) {
101
+ // if we have no connections, try connecting.
102
+ if len(s.ConnectionsToPeer(p)) == 0 {
103
+ log.Debug("Swarm: NewStreamWithPeer no connections. Attempting to connect...")
104
+ if _, err := s.Dial(context.Background(), p); err != nil {
105
+ return nil, err
106
+ }
107
+ }
108
+ log.Debug("Swarm: NewStreamWithPeer...")
109
+
110
+ st, err := s.swarm.NewStreamWithGroup(p)
111
+ return wrapStream(st), err
112
+}
113
+
114
+// StreamsWithPeer returns all the live Streams to p
115
+func (s *Swarm) StreamsWithPeer(p peer.ID) []*Stream {
116
+ return wrapStreams(ps.StreamsWithGroup(p, s.swarm.Streams()))
117
+}
118
+
119
+// ConnectionsToPeer returns all the live connections to p
120
+func (s *Swarm) ConnectionsToPeer(p peer.ID) []*Conn {
121
+ return wrapConns(ps.ConnsWithGroup(p, s.swarm.Conns()))
122
+}
123
+
124
+// Connections returns a slice of all connections.
125
+func (s *Swarm) Connections() []*Conn {
126
+ return wrapConns(s.swarm.Conns())
127
+}
128
+
129
+// CloseConnection removes a given peer from swarm + closes the connection
130
+func (s *Swarm) CloseConnection(p peer.ID) error {
131
+ conns := s.swarm.ConnsWithGroup(p) // boom.
132
+ for _, c := range conns {
133
+ c.Close()
134
+ }
135
+ return nil
136
+}
137
+
138
+// Peers returns a copy of the set of peers swarm is connected to.
139
+func (s *Swarm) Peers() []peer.ID {
140
+ conns := s.Connections()
141
+
142
+ seen := make(map[peer.ID]struct{})
143
+ peers := make([]peer.ID, 0, len(conns))
144
+ for _, c := range conns {
145
+ p := c.RemotePeer()
146
+ if _, found := seen[p]; found {
147
+ continue
148
+ }
149
+
150
+ peers = append(peers, p)
151
+ }
152
+ return peers
153
+}
154
+
155
+// LocalPeer returns the local peer swarm is associated to.
156
+func (s *Swarm) LocalPeer() peer.ID {
157
+ return s.local
158
+}
p2p/net2/swarm/swarm_conn.go
new
+141
@@ -0,0 +1,141 @@
1
+package swarm
2
+
3
+import (
4
+ "fmt"
5
+
6
+ ic "github.com/jbenet/go-ipfs/p2p/crypto"
7
+ conn "github.com/jbenet/go-ipfs/p2p/net/conn"
8
+ inet "github.com/jbenet/go-ipfs/p2p/net2"
9
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
10
+
11
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
12
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
13
+ ps "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-peerstream"
14
+)
15
+
16
+// a Conn is a simple wrapper around a ps.Conn that also exposes
17
+// some of the methods from the underlying conn.Conn.
18
+// There's **five** "layers" to each connection:
19
+// * 0. the net.Conn - underlying net.Conn (TCP/UDP/UTP/etc)
20
+// * 1. the manet.Conn - provides multiaddr friendly Conn
21
+// * 2. the conn.Conn - provides Peer friendly Conn (inc Secure channel)
22
+// * 3. the peerstream.Conn - provides peerstream / spdysptream happiness
23
+// * 4. the Conn - abstracts everyting out, exposing only key parts of underlying layers
24
+// (I know, this is kinda crazy. it's more historical than a good design. though the
25
+// layers do build up pieces of functionality. and they're all just io.RW :) )
26
+type Conn ps.Conn
27
+
28
+// ConnHandler is called when new conns are opened from remote peers.
29
+// See peerstream.ConnHandler
30
+type ConnHandler func(*Conn)
31
+
32
+func (c *Conn) StreamConn() *ps.Conn {
33
+ return (*ps.Conn)(c)
34
+}
35
+
36
+func (c *Conn) RawConn() conn.Conn {
37
+ // righly panic if these things aren't true. it is an expected
38
+ // invariant that these Conns are all of the typewe expect:
39
+ // ps.Conn wrapping a conn.Conn
40
+ // if we get something else it is programmer error.
41
+ return (*ps.Conn)(c).NetConn().(conn.Conn)
42
+}
43
+
44
+func (c *Conn) String() string {
45
+ return fmt.Sprintf("<SwarmConn %s>", c.RawConn())
46
+}
47
+
48
+// LocalMultiaddr is the Multiaddr on this side
49
+func (c *Conn) LocalMultiaddr() ma.Multiaddr {
50
+ return c.RawConn().LocalMultiaddr()
51
+}
52
+
53
+// LocalPeer is the Peer on our side of the connection
54
+func (c *Conn) LocalPeer() peer.ID {
55
+ return c.RawConn().LocalPeer()
56
+}
57
+
58
+// RemoteMultiaddr is the Multiaddr on the remote side
59
+func (c *Conn) RemoteMultiaddr() ma.Multiaddr {
60
+ return c.RawConn().RemoteMultiaddr()
61
+}
62
+
63
+// RemotePeer is the Peer on the remote side
64
+func (c *Conn) RemotePeer() peer.ID {
65
+ return c.RawConn().RemotePeer()
66
+}
67
+
68
+// LocalPrivateKey is the public key of the peer on this side
69
+func (c *Conn) LocalPrivateKey() ic.PrivKey {
70
+ return c.RawConn().LocalPrivateKey()
71
+}
72
+
73
+// RemotePublicKey is the public key of the peer on the remote side
74
+func (c *Conn) RemotePublicKey() ic.PubKey {
75
+ return c.RawConn().RemotePublicKey()
76
+}
77
+
78
+// NewSwarmStream returns a new Stream from this connection
79
+func (c *Conn) NewSwarmStream() (*Stream, error) {
80
+ s, err := c.StreamConn().NewStream()
81
+ return wrapStream(s), err
82
+}
83
+
84
+// NewStream returns a new Stream from this connection
85
+func (c *Conn) NewStream() (inet.Stream, error) {
86
+ s, err := c.NewSwarmStream()
87
+ return inet.Stream(s), err
88
+}
89
+
90
+func (c *Conn) Close() error {
91
+ return c.StreamConn().Close()
92
+}
93
+
94
+func wrapConn(psc *ps.Conn) (*Conn, error) {
95
+ // grab the underlying connection.
96
+ if _, ok := psc.NetConn().(conn.Conn); !ok {
97
+ // this should never happen. if we see it ocurring it means that we added
98
+ // a Listener to the ps.Swarm that is NOT one of our net/conn.Listener.
99
+ return nil, fmt.Errorf("swarm connHandler: invalid conn (not a conn.Conn): %s", psc)
100
+ }
101
+ return (*Conn)(psc), nil
102
+}
103
+
104
+// wrapConns returns a *Conn for all these ps.Conns
105
+func wrapConns(conns1 []*ps.Conn) []*Conn {
106
+ conns2 := make([]*Conn, len(conns1))
107
+ for i, c1 := range conns1 {
108
+ if c2, err := wrapConn(c1); err == nil {
109
+ conns2[i] = c2
110
+ }
111
+ }
112
+ return conns2
113
+}
114
+
115
+// newConnSetup does the swarm's "setup" for a connection. returns the underlying
116
+// conn.Conn this method is used by both swarm.Dial and ps.Swarm connHandler
117
+func (s *Swarm) newConnSetup(ctx context.Context, psConn *ps.Conn) (*Conn, error) {
118
+
119
+ // wrap with a Conn
120
+ sc, err := wrapConn(psConn)
121
+ if err != nil {
122
+ return nil, err
123
+ }
124
+
125
+ // if we have a public key, make sure we add it to our peerstore!
126
+ // This is an important detail. Otherwise we must fetch the public
127
+ // key from the DHT or some other system.
128
+ if pk := sc.RemotePublicKey(); pk != nil {
129
+ s.peers.AddPubKey(sc.RemotePeer(), pk)
130
+ }
131
+
132
+ // ok great! we can use it. add it to our group.
133
+
134
+ // set the RemotePeer as a group on the conn. this lets us group
135
+ // connections in the StreamSwarm by peer, and get a streams from
136
+ // any available connection in the group (better multiconn):
137
+ // swarm.StreamSwarm().NewStreamWithGroup(remotePeer)
138
+ psConn.AddGroup(sc.RemotePeer())
139
+
140
+ return sc, nil
141
+}
p2p/net2/swarm/swarm_dial.go
new
+104
@@ -0,0 +1,104 @@
1
+package swarm
2
+
3
+import (
4
+ "errors"
5
+ "fmt"
6
+
7
+ conn "github.com/jbenet/go-ipfs/p2p/net/conn"
8
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
9
+ lgbl "github.com/jbenet/go-ipfs/util/eventlog/loggables"
10
+
11
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
12
+)
13
+
14
+// Dial connects to a peer.
15
+//
16
+// The idea is that the client of Swarm does not need to know what network
17
+// the connection will happen over. Swarm can use whichever it choses.
18
+// This allows us to use various transport protocols, do NAT traversal/relay,
19
+// etc. to achive connection.
20
+func (s *Swarm) Dial(ctx context.Context, p peer.ID) (*Conn, error) {
21
+
22
+ if p == s.local {
23
+ return nil, errors.New("Attempted connection to self!")
24
+ }
25
+
26
+ // check if we already have an open connection first
27
+ cs := s.ConnectionsToPeer(p)
28
+ for _, c := range cs {
29
+ if c != nil { // dump out the first one we find
30
+ return c, nil
31
+ }
32
+ }
33
+
34
+ sk := s.peers.PrivKey(s.local)
35
+ if sk == nil {
36
+ // may be fine for sk to be nil, just log a warning.
37
+ log.Warning("Dial not given PrivateKey, so WILL NOT SECURE conn.")
38
+ }
39
+
40
+ remoteAddrs := s.peers.Addresses(p)
41
+ if len(remoteAddrs) == 0 {
42
+ return nil, errors.New("peer has no addresses")
43
+ }
44
+ localAddrs := s.peers.Addresses(s.local)
45
+ if len(localAddrs) == 0 {
46
+ log.Debug("Dialing out with no local addresses.")
47
+ }
48
+
49
+ // open connection to peer
50
+ d := &conn.Dialer{
51
+ LocalPeer: s.local,
52
+ LocalAddrs: localAddrs,
53
+ PrivateKey: sk,
54
+ }
55
+
56
+ // try to connect to one of the peer's known addresses.
57
+ // for simplicity, we do this sequentially.
58
+ // A future commit will do this asynchronously.
59
+ var connC conn.Conn
60
+ var err error
61
+ for _, addr := range remoteAddrs {
62
+ connC, err = d.Dial(ctx, addr, p)
63
+ if err == nil {
64
+ break
65
+ }
66
+ }
67
+ if err != nil {
68
+ return nil, err
69
+ }
70
+
71
+ // ok try to setup the new connection.
72
+ swarmC, err := dialConnSetup(ctx, s, connC)
73
+ if err != nil {
74
+ log.Error("Dial newConnSetup failed. disconnecting.")
75
+ log.Event(ctx, "dialFailureDisconnect", lgbl.NetConn(connC), lgbl.Error(err))
76
+ swarmC.Close() // close the connection. didn't work out :(
77
+ return nil, err
78
+ }
79
+
80
+ log.Event(ctx, "dial", p)
81
+ return swarmC, nil
82
+}
83
+
84
+// dialConnSetup is the setup logic for a connection from the dial side. it
85
+// needs to add the Conn to the StreamSwarm, then run newConnSetup
86
+func dialConnSetup(ctx context.Context, s *Swarm, connC conn.Conn) (*Conn, error) {
87
+
88
+ psC, err := s.swarm.AddConn(connC)
89
+ if err != nil {
90
+ // connC is closed by caller if we fail.
91
+ return nil, fmt.Errorf("failed to add conn to ps.Swarm: %s", err)
92
+ }
93
+
94
+ // ok try to setup the new connection. (newConnSetup will add to group)
95
+ swarmC, err := s.newConnSetup(ctx, psC)
96
+ if err != nil {
97
+ log.Error("Dial newConnSetup failed. disconnecting.")
98
+ log.Event(ctx, "dialFailureDisconnect", lgbl.NetConn(connC), lgbl.Error(err))
99
+ swarmC.Close() // we need to call this to make sure psC is Closed.
100
+ return nil, err
101
+ }
102
+
103
+ return swarmC, err
104
+}
p2p/net2/swarm/swarm_listen.go
new
+86
@@ -0,0 +1,86 @@
1
+package swarm
2
+
3
+import (
4
+ conn "github.com/jbenet/go-ipfs/p2p/net/conn"
5
+ lgbl "github.com/jbenet/go-ipfs/util/eventlog/loggables"
6
+
7
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
8
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
9
+ ps "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-peerstream"
10
+ multierr "github.com/jbenet/go-ipfs/util/multierr"
11
+)
12
+
13
+// Open listeners for each network the swarm should listen on
14
+func (s *Swarm) listen(addrs []ma.Multiaddr) error {
15
+ retErr := multierr.New()
16
+
17
+ // listen on every address
18
+ for i, addr := range addrs {
19
+ err := s.setupListener(addr)
20
+ if err != nil {
21
+ if retErr.Errors == nil {
22
+ retErr.Errors = make([]error, len(addrs))
23
+ }
24
+ retErr.Errors[i] = err
25
+ log.Errorf("Failed to listen on: %s - %s", addr, err)
26
+ }
27
+ }
28
+
29
+ if retErr.Errors != nil {
30
+ return retErr
31
+ }
32
+ return nil
33
+}
34
+
35
+// Listen for new connections on the given multiaddr
36
+func (s *Swarm) setupListener(maddr ma.Multiaddr) error {
37
+
38
+ // TODO rethink how this has to work. (jbenet)
39
+ //
40
+ // resolved, err := resolveUnspecifiedAddresses([]ma.Multiaddr{maddr})
41
+ // if err != nil {
42
+ // return err
43
+ // }
44
+ // for _, a := range resolved {
45
+ // s.peers.AddAddress(s.local, a)
46
+ // }
47
+
48
+ sk := s.peers.PrivKey(s.local)
49
+ if sk == nil {
50
+ // may be fine for sk to be nil, just log a warning.
51
+ log.Warning("Listener not given PrivateKey, so WILL NOT SECURE conns.")
52
+ }
53
+ list, err := conn.Listen(s.cg.Context(), maddr, s.local, sk)
54
+ if err != nil {
55
+ return err
56
+ }
57
+
58
+ // AddListener to the peerstream Listener. this will begin accepting connections
59
+ // and streams!
60
+ _, err = s.swarm.AddListener(list)
61
+ return err
62
+}
63
+
64
+// connHandler is called by the StreamSwarm whenever a new connection is added
65
+// here we configure it slightly. Note that this is sequential, so if anything
66
+// will take a while do it in a goroutine.
67
+// See https://godoc.org/github.com/jbenet/go-peerstream for more information
68
+func (s *Swarm) connHandler(c *ps.Conn) *Conn {
69
+ ctx := context.Background()
70
+ // this context is for running the handshake, which -- when receiveing connections
71
+ // -- we have no bound on beyond what the transport protocol bounds it at.
72
+ // note that setup + the handshake are bounded by underlying io.
73
+ // (i.e. if TCP or UDP disconnects (or the swarm closes), we're done.
74
+ // Q: why not have a shorter handshake? think about an HTTP server on really slow conns.
75
+ // as long as the conn is live (TCP says its online), it tries its best. we follow suit.)
76
+
77
+ sc, err := s.newConnSetup(ctx, c)
78
+ if err != nil {
79
+ log.Error(err)
80
+ log.Event(ctx, "newConnHandlerDisconnect", lgbl.NetConn(c.NetConn()), lgbl.Error(err))
81
+ c.Close() // boom. close it.
82
+ return nil
83
+ }
84
+
85
+ return sc
86
+}
p2p/net2/swarm/swarm_net.go
new
+156
@@ -0,0 +1,156 @@
1
+package swarm
2
+
3
+import (
4
+ "fmt"
5
+
6
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
7
+
8
+ inet "github.com/jbenet/go-ipfs/p2p/net2"
9
+
10
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
11
+ ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
12
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
13
+)
14
+
15
+// Network implements the inet.Network interface.
16
+// It is simply a swarm, with a few different functions
17
+// to implement inet.Network.
18
+type Network Swarm
19
+
20
+// NewNetwork constructs a new network and starts listening on given addresses.
21
+func NewNetwork(ctx context.Context, listen []ma.Multiaddr, local peer.ID,
22
+ peers peer.Peerstore) (*Network, error) {
23
+
24
+ s, err := NewSwarm(ctx, listen, local, peers)
25
+ if err != nil {
26
+ return nil, err
27
+ }
28
+
29
+ return (*Network)(s), nil
30
+}
31
+
32
+// DialPeer attempts to establish a connection to a given peer.
33
+// Respects the context.
34
+func (n *Network) DialPeer(ctx context.Context, p peer.ID) (inet.Conn, error) {
35
+ log.Debugf("[%s] network dialing peer [%s]", n.local, p)
36
+ sc, err := n.Swarm().Dial(ctx, p)
37
+ if err != nil {
38
+ return nil, err
39
+ }
40
+
41
+ log.Debugf("network for %s finished dialing %s", n.local, p)
42
+ return inet.Conn(sc), nil
43
+}
44
+
45
+// CtxGroup returns the network's ContextGroup
46
+func (n *Network) CtxGroup() ctxgroup.ContextGroup {
47
+ return n.cg
48
+}
49
+
50
+// Swarm returns the network's peerstream.Swarm
51
+func (n *Network) Swarm() *Swarm {
52
+ return (*Swarm)(n)
53
+}
54
+
55
+// LocalPeer the network's LocalPeer
56
+func (n *Network) LocalPeer() peer.ID {
57
+ return n.Swarm().LocalPeer()
58
+}
59
+
60
+// Peers returns the connected peers
61
+func (n *Network) Peers() []peer.ID {
62
+ return n.Swarm().Peers()
63
+}
64
+
65
+// Peers returns the connected peers
66
+func (n *Network) Peerstore() peer.Peerstore {
67
+ return n.Swarm().peers
68
+}
69
+
70
+// Conns returns the connected peers
71
+func (n *Network) Conns() []inet.Conn {
72
+ conns1 := n.Swarm().Connections()
73
+ out := make([]inet.Conn, len(conns1))
74
+ for i, c := range conns1 {
75
+ out[i] = inet.Conn(c)
76
+ }
77
+ return out
78
+}
79
+
80
+// ConnsToPeer returns the connections in this Netowrk for given peer.
81
+func (n *Network) ConnsToPeer(p peer.ID) []inet.Conn {
82
+ conns1 := n.Swarm().ConnectionsToPeer(p)
83
+ out := make([]inet.Conn, len(conns1))
84
+ for i, c := range conns1 {
85
+ out[i] = inet.Conn(c)
86
+ }
87
+ return out
88
+}
89
+
90
+// ClosePeer connection to peer
91
+func (n *Network) ClosePeer(p peer.ID) error {
92
+ return n.Swarm().CloseConnection(p)
93
+}
94
+
95
+// close is the real teardown function
96
+func (n *Network) close() error {
97
+ return n.Swarm().Close()
98
+}
99
+
100
+// Close calls the ContextCloser func
101
+func (n *Network) Close() error {
102
+ return n.Swarm().cg.Close()
103
+}
104
+
105
+// ListenAddresses returns a list of addresses at which this network listens.
106
+func (n *Network) ListenAddresses() []ma.Multiaddr {
107
+ return n.Swarm().ListenAddresses()
108
+}
109
+
110
+// InterfaceListenAddresses returns a list of addresses at which this network
111
+// listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to
112
+// use the known local interfaces.
113
+func (n *Network) InterfaceListenAddresses() ([]ma.Multiaddr, error) {
114
+ return InterfaceListenAddresses(n.Swarm())
115
+}
116
+
117
+// Connectedness returns a state signaling connection capabilities
118
+// For now only returns Connected || NotConnected. Expand into more later.
119
+func (n *Network) Connectedness(p peer.ID) inet.Connectedness {
120
+ c := n.Swarm().ConnectionsToPeer(p)
121
+ if c != nil && len(c) > 0 {
122
+ return inet.Connected
123
+ }
124
+ return inet.NotConnected
125
+}
126
+
127
+// NewStream returns a new stream to given peer p.
128
+// If there is no connection to p, attempts to create one.
129
+func (n *Network) NewStream(p peer.ID) (inet.Stream, error) {
130
+ log.Debugf("[%s] network opening stream to peer [%s]", n.local, p)
131
+ s, err := n.Swarm().NewStreamWithPeer(p)
132
+ if err != nil {
133
+ return nil, err
134
+ }
135
+
136
+ return inet.Stream(s), nil
137
+}
138
+
139
+// SetHandler sets the protocol handler on the Network's Muxer.
140
+// This operation is threadsafe.
141
+func (n *Network) SetStreamHandler(h inet.StreamHandler) {
142
+ n.Swarm().SetStreamHandler(h)
143
+}
144
+
145
+// SetConnHandler sets the conn handler on the Network.
146
+// This operation is threadsafe.
147
+func (n *Network) SetConnHandler(h inet.ConnHandler) {
148
+ n.Swarm().SetConnHandler(func(c *Conn) {
149
+ h(inet.Conn(c))
150
+ })
151
+}
152
+
153
+// String returns a string representation of Network.
154
+func (n *Network) String() string {
155
+ return fmt.Sprintf("<Network %s>", n.LocalPeer())
156
+}
p2p/net2/swarm/swarm_net_test.go
new
+78
@@ -0,0 +1,78 @@
1
+package swarm_test
2
+
3
+import (
4
+ "fmt"
5
+ "testing"
6
+ "time"
7
+
8
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
9
+
10
+ inet "github.com/jbenet/go-ipfs/p2p/net"
11
+ netutil "github.com/jbenet/go-ipfs/p2p/net/swarmnet/util"
12
+)
13
+
14
+// TestConnectednessCorrect starts a few networks, connects a few
15
+// and tests Connectedness value is correct.
16
+func TestConnectednessCorrect(t *testing.T) {
17
+
18
+ ctx := context.Background()
19
+
20
+ nets := make([]inet.Network, 4)
21
+ for i := 0; i < 4; i++ {
22
+ nets[i] = netutil.GenNetwork(t, ctx)
23
+ }
24
+
25
+ // connect 0-1, 0-2, 0-3, 1-2, 2-3
26
+
27
+ dial := func(a, b inet.Network) {
28
+ netutil.DivulgeAddresses(b, a)
29
+ if err := a.DialPeer(ctx, b.LocalPeer()); err != nil {
30
+ t.Fatalf("Failed to dial: %s", err)
31
+ }
32
+ }
33
+
34
+ dial(nets[0], nets[1])
35
+ dial(nets[0], nets[3])
36
+ dial(nets[1], nets[2])
37
+ dial(nets[3], nets[2])
38
+
39
+ // there's something wrong with dial, i think. it's not finishing
40
+ // completely. there must be some async stuff.
41
+ <-time.After(100 * time.Millisecond)
42
+
43
+ // test those connected show up correctly
44
+
45
+ // test connected
46
+ expectConnectedness(t, nets[0], nets[1], inet.Connected)
47
+ expectConnectedness(t, nets[0], nets[3], inet.Connected)
48
+ expectConnectedness(t, nets[1], nets[2], inet.Connected)
49
+ expectConnectedness(t, nets[3], nets[2], inet.Connected)
50
+
51
+ // test not connected
52
+ expectConnectedness(t, nets[0], nets[2], inet.NotConnected)
53
+ expectConnectedness(t, nets[1], nets[3], inet.NotConnected)
54
+
55
+ for _, n := range nets {
56
+ n.Close()
57
+ }
58
+}
59
+
60
+func expectConnectedness(t *testing.T, a, b inet.Network, expected inet.Connectedness) {
61
+ es := "%s is connected to %s, but Connectedness incorrect. %s %s"
62
+ if a.Connectedness(b.LocalPeer()) != expected {
63
+ t.Errorf(es, a, b, printConns(a), printConns(b))
64
+ }
65
+
66
+ // test symmetric case
67
+ if b.Connectedness(a.LocalPeer()) != expected {
68
+ t.Errorf(es, b, a, printConns(b), printConns(a))
69
+ }
70
+}
71
+
72
+func printConns(n inet.Network) string {
73
+ s := fmt.Sprintf("Connections in %s:\n", n)
74
+ for _, c := range n.Conns() {
75
+ s = s + fmt.Sprintf("- %s\n", c)
76
+ }
77
+ return s
78
+}
p2p/net2/swarm/swarm_stream.go
new
+59
@@ -0,0 +1,59 @@
1
+package swarm
2
+
3
+import (
4
+ inet "github.com/jbenet/go-ipfs/p2p/net2"
5
+
6
+ ps "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-peerstream"
7
+)
8
+
9
+// a Stream is a wrapper around a ps.Stream that exposes a way to get
10
+// our Conn and Swarm (instead of just the ps.Conn and ps.Swarm)
11
+type Stream ps.Stream
12
+
13
+// Stream returns the underlying peerstream.Stream
14
+func (s *Stream) Stream() *ps.Stream {
15
+ return (*ps.Stream)(s)
16
+}
17
+
18
+// Conn returns the Conn associated with this Stream, as an inet.Conn
19
+func (s *Stream) Conn() inet.Conn {
20
+ return s.SwarmConn()
21
+}
22
+
23
+// SwarmConn returns the Conn associated with this Stream, as a *Conn
24
+func (s *Stream) SwarmConn() *Conn {
25
+ return (*Conn)(s.Stream().Conn())
26
+}
27
+
28
+// Wait waits for the stream to receive a reply.
29
+func (s *Stream) Wait() error {
30
+ return s.Stream().Wait()
31
+}
32
+
33
+// Read reads bytes from a stream.
34
+func (s *Stream) Read(p []byte) (n int, err error) {
35
+ return s.Stream().Read(p)
36
+}
37
+
38
+// Write writes bytes to a stream, flushing for each call.
39
+func (s *Stream) Write(p []byte) (n int, err error) {
40
+ return s.Stream().Write(p)
41
+}
42
+
43
+// Close closes the stream, indicating this side is finished
44
+// with the stream.
45
+func (s *Stream) Close() error {
46
+ return s.Stream().Close()
47
+}
48
+
49
+func wrapStream(pss *ps.Stream) *Stream {
50
+ return (*Stream)(pss)
51
+}
52
+
53
+func wrapStreams(st []*ps.Stream) []*Stream {
54
+ out := make([]*Stream, len(st))
55
+ for i, s := range st {
56
+ out[i] = wrapStream(s)
57
+ }
58
+ return out
59
+}
p2p/net2/swarm/swarm_test.go
new
+269
@@ -0,0 +1,269 @@
1
+package swarm
2
+
3
+import (
4
+ "bytes"
5
+ "io"
6
+ "sync"
7
+ "testing"
8
+ "time"
9
+
10
+ inet "github.com/jbenet/go-ipfs/p2p/net2"
11
+ peer "github.com/jbenet/go-ipfs/p2p/peer"
12
+ errors "github.com/jbenet/go-ipfs/util/debugerror"
13
+ testutil "github.com/jbenet/go-ipfs/util/testutil"
14
+
15
+ context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
16
+ ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
17
+)
18
+
19
+func EchoStreamHandler(stream inet.Stream) {
20
+ go func() {
21
+ defer stream.Close()
22
+
23
+ // pull out the ipfs conn
24
+ c := stream.Conn()
25
+ log.Debugf("%s ponging to %s", c.LocalPeer(), c.RemotePeer())
26
+
27
+ buf := make([]byte, 4)
28
+
29
+ for {
30
+ if _, err := stream.Read(buf); err != nil {
31
+ if err != io.EOF {
32
+ log.Error("ping receive error:", err)
33
+ }
34
+ return
35
+ }
36
+
37
+ if !bytes.Equal(buf, []byte("ping")) {
38
+ log.Errorf("ping receive error: ping != %s %v", buf, buf)
39
+ return
40
+ }
41
+
42
+ if _, err := stream.Write([]byte("pong")); err != nil {
43
+ log.Error("pond send error:", err)
44
+ return
45
+ }
46
+ }
47
+ }()
48
+}
49
+
50
+func makeSwarms(ctx context.Context, t *testing.T, num int) ([]*Swarm, []testutil.PeerNetParams) {
51
+ swarms := make([]*Swarm, 0, num)
52
+ peersnp := make([]testutil.PeerNetParams, 0, num)
53
+
54
+ for i := 0; i < num; i++ {
55
+ localnp := testutil.RandPeerNetParamsOrFatal(t)
56
+ peersnp = append(peersnp, localnp)
57
+
58
+ peerstore := peer.NewPeerstore()
59
+ peerstore.AddAddress(localnp.ID, localnp.Addr)
60
+ peerstore.AddPubKey(localnp.ID, localnp.PubKey)
61
+ peerstore.AddPrivKey(localnp.ID, localnp.PrivKey)
62
+
63
+ addrs := peerstore.Addresses(localnp.ID)
64
+ swarm, err := NewSwarm(ctx, addrs, localnp.ID, peerstore)
65
+ if err != nil {
66
+ t.Fatal(err)
67
+ }
68
+
69
+ swarm.SetStreamHandler(EchoStreamHandler)
70
+ swarms = append(swarms, swarm)
71
+ }
72
+
73
+ return swarms, peersnp
74
+}
75
+
76
+func connectSwarms(t *testing.T, ctx context.Context, swarms []*Swarm, peersnp []testutil.PeerNetParams) {
77
+
78
+ var wg sync.WaitGroup
79
+ connect := func(s *Swarm, dst peer.ID, addr ma.Multiaddr) {
80
+ // TODO: make a DialAddr func.
81
+ s.peers.AddAddress(dst, addr)
82
+ if _, err := s.Dial(ctx, dst); err != nil {
83
+ t.Fatal("error swarm dialing to peer", err)
84
+ }
85
+ wg.Done()
86
+ }
87
+
88
+ log.Info("Connecting swarms simultaneously.")
89
+ for _, s := range swarms {
90
+ for _, p := range peersnp {
91
+ if p.ID != s.local { // don't connect to self.
92
+ wg.Add(1)
93
+ connect(s, p.ID, p.Addr)
94
+ }
95
+ }
96
+ }
97
+ wg.Wait()
98
+
99
+ for _, s := range swarms {
100
+ log.Infof("%s swarm routing table: %s", s.local, s.Peers())
101
+ }
102
+}
103
+
104
+func SubtestSwarm(t *testing.T, SwarmNum int, MsgNum int) {
105
+ // t.Skip("skipping for another test")
106
+
107
+ ctx := context.Background()
108
+ swarms, peersnp := makeSwarms(ctx, t, SwarmNum)
109
+
110
+ // connect everyone
111
+ connectSwarms(t, ctx, swarms, peersnp)
112
+
113
+ // ping/pong
114
+ for _, s1 := range swarms {
115
+ log.Debugf("-------------------------------------------------------")
116
+ log.Debugf("%s ping pong round", s1.local)
117
+ log.Debugf("-------------------------------------------------------")
118
+
119
+ _, cancel := context.WithCancel(ctx)
120
+ got := map[peer.ID]int{}
121
+ errChan := make(chan error, MsgNum*len(peersnp))
122
+ streamChan := make(chan *Stream, MsgNum)
123
+
124
+ // send out "ping" x MsgNum to every peer
125
+ go func() {
126
+ defer close(streamChan)
127
+
128
+ var wg sync.WaitGroup
129
+ send := func(p peer.ID) {
130
+ defer wg.Done()
131
+
132
+ // first, one stream per peer (nice)
133
+ stream, err := s1.NewStreamWithPeer(p)
134
+ if err != nil {
135
+ errChan <- errors.Wrap(err)
136
+ return
137
+ }
138
+
139
+ // send out ping!
140
+ for k := 0; k < MsgNum; k++ { // with k messages
141
+ msg := "ping"
142
+ log.Debugf("%s %s %s (%d)", s1.local, msg, p, k)
143
+ stream.Write([]byte(msg))
144
+ }
145
+
146
+ // read it later
147
+ streamChan <- stream
148
+ }
149
+
150
+ for _, p := range peersnp {
151
+ if p.ID == s1.local {
152
+ continue // dont send to self...
153
+ }
154
+
155
+ wg.Add(1)
156
+ go send(p.ID)
157
+ }
158
+ wg.Wait()
159
+ }()
160
+
161
+ // receive "pong" x MsgNum from every peer
162
+ go func() {
163
+ defer close(errChan)
164
+ count := 0
165
+ countShouldBe := MsgNum * (len(peersnp) - 1)
166
+ for stream := range streamChan { // one per peer
167
+ defer stream.Close()
168
+
169
+ // get peer on the other side
170
+ p := stream.Conn().RemotePeer()
171
+
172
+ // receive pings
173
+ msgCount := 0
174
+ msg := make([]byte, 4)
175
+ for k := 0; k < MsgNum; k++ { // with k messages
176
+
177
+ // read from the stream
178
+ if _, err := stream.Read(msg); err != nil {
179
+ errChan <- errors.Wrap(err)
180
+ continue
181
+ }
182
+
183
+ if string(msg) != "pong" {
184
+ errChan <- errors.Errorf("unexpected message: %s", msg)
185
+ continue
186
+ }
187
+
188
+ log.Debugf("%s %s %s (%d)", s1.local, msg, p, k)
189
+ msgCount++
190
+ }
191
+
192
+ got[p] = msgCount
193
+ count += msgCount
194
+ }
195
+
196
+ if count != countShouldBe {
197
+ errChan <- errors.Errorf("count mismatch: %d != %d", count, countShouldBe)
198
+ }
199
+ }()
200
+
201
+ // check any errors (blocks till consumer is done)
202
+ for err := range errChan {
203
+ if err != nil {
204
+ t.Fatal(err.Error())
205
+ }
206
+ }
207
+
208
+ log.Debugf("%s got pongs", s1.local)
209
+ if (len(peersnp) - 1) != len(got) {
210
+ t.Errorf("got (%d) less messages than sent (%d).", len(got), len(peersnp))
211
+ }
212
+
213
+ for p, n := range got {
214
+ if n != MsgNum {
215
+ t.Error("peer did not get all msgs", p, n, "/", MsgNum)
216
+ }
217
+ }
218
+
219
+ cancel()
220
+ <-time.After(10 * time.Millisecond)
221
+ }
222
+
223
+ for _, s := range swarms {
224
+ s.Close()
225
+ }
226
+}
227
+
228
+func TestSwarm(t *testing.T) {
229
+ // t.Skip("skipping for another test")
230
+
231
+ // msgs := 1000
232
+ msgs := 100
233
+ swarms := 5
234
+ SubtestSwarm(t, swarms, msgs)
235
+}
236
+
237
+func TestConnHandler(t *testing.T) {
238
+ // t.Skip("skipping for another test")
239
+
240
+ ctx := context.Background()
241
+ swarms, peersnp := makeSwarms(ctx, t, 5)
242
+
243
+ gotconn := make(chan struct{}, 10)
244
+ swarms[0].SetConnHandler(func(conn *Conn) {
245
+ gotconn <- struct{}{}
246
+ })
247
+
248
+ connectSwarms(t, ctx, swarms, peersnp)
249
+
250
+ <-time.After(time.Millisecond)
251
+ // should've gotten 5 by now.
252
+
253
+ swarms[0].SetConnHandler(nil)
254
+
255
+ expect := 4
256
+ for i := 0; i < expect; i++ {
257
+ select {
258
+ case <-time.After(time.Second):
259
+ t.Fatal("failed to get connections")
260
+ case <-gotconn:
261
+ }
262
+ }
263
+
264
+ select {
265
+ case <-gotconn:
266
+ t.Fatalf("should have connected to %d swarms", expect)
267
+ default:
268
+ }
269
+}