@cryptotaxi247 / kubo / commits / d32282487

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 +![](https://docs.google.com/drawings/d/1FvU7GImRsb9GvAWDDo1le85jIrnFJNVB_OTPXC15WwM/pub?h=480)
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 +}