swarm_conn.go 4.37 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2 3
package swarm

import (
Jeromy's avatar
Jeromy committed
4
	"context"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
5 6
	"fmt"

Jeromy's avatar
Jeromy committed
7
	ps "github.com/jbenet/go-peerstream"
Jeromy's avatar
Jeromy committed
8
	ic "github.com/libp2p/go-libp2p-crypto"
9 10
	iconn "github.com/libp2p/go-libp2p-interface-conn"
	inet "github.com/libp2p/go-libp2p-net"
Jeromy's avatar
Jeromy committed
11 12
	peer "github.com/libp2p/go-libp2p-peer"
	ma "github.com/multiformats/go-multiaddr"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
13 14
)

15
// Conn is a simple wrapper around a ps.Conn that also exposes
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
// some of the methods from the underlying conn.Conn.
// There's **five** "layers" to each connection:
//  * 0. the net.Conn - underlying net.Conn (TCP/UDP/UTP/etc)
//  * 1. the manet.Conn - provides multiaddr friendly Conn
//  * 2. the conn.Conn - provides Peer friendly Conn (inc Secure channel)
//  * 3. the peerstream.Conn - provides peerstream / spdysptream happiness
//  * 4. the Conn - abstracts everyting out, exposing only key parts of underlying layers
// (I know, this is kinda crazy. it's more historical than a good design. though the
// layers do build up pieces of functionality. and they're all just io.RW :) )
type Conn ps.Conn

// ConnHandler is called when new conns are opened from remote peers.
// See peerstream.ConnHandler
type ConnHandler func(*Conn)

func (c *Conn) StreamConn() *ps.Conn {
	return (*ps.Conn)(c)
}

35
func (c *Conn) RawConn() iconn.Conn {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
36 37 38 39
	// righly panic if these things aren't true. it is an expected
	// invariant that these Conns are all of the typewe expect:
	// 		ps.Conn wrapping a conn.Conn
	// if we get something else it is programmer error.
40
	return (*ps.Conn)(c).NetConn().(iconn.Conn)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
}

func (c *Conn) String() string {
	return fmt.Sprintf("<SwarmConn %s>", c.RawConn())
}

// LocalMultiaddr is the Multiaddr on this side
func (c *Conn) LocalMultiaddr() ma.Multiaddr {
	return c.RawConn().LocalMultiaddr()
}

// LocalPeer is the Peer on our side of the connection
func (c *Conn) LocalPeer() peer.ID {
	return c.RawConn().LocalPeer()
}

// RemoteMultiaddr is the Multiaddr on the remote side
func (c *Conn) RemoteMultiaddr() ma.Multiaddr {
	return c.RawConn().RemoteMultiaddr()
}

// RemotePeer is the Peer on the remote side
func (c *Conn) RemotePeer() peer.ID {
	return c.RawConn().RemotePeer()
}

// LocalPrivateKey is the public key of the peer on this side
func (c *Conn) LocalPrivateKey() ic.PrivKey {
	return c.RawConn().LocalPrivateKey()
}

// RemotePublicKey is the public key of the peer on the remote side
func (c *Conn) RemotePublicKey() ic.PubKey {
	return c.RawConn().RemotePublicKey()
}

// NewSwarmStream returns a new Stream from this connection
func (c *Conn) NewSwarmStream() (*Stream, error) {
	s, err := c.StreamConn().NewStream()
	return wrapStream(s), err
}

// NewStream returns a new Stream from this connection
func (c *Conn) NewStream() (inet.Stream, error) {
	s, err := c.NewSwarmStream()
	return inet.Stream(s), err
}

89
// Close closes the underlying stream connection
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
90 91 92 93 94 95
func (c *Conn) Close() error {
	return c.StreamConn().Close()
}

func wrapConn(psc *ps.Conn) (*Conn, error) {
	// grab the underlying connection.
96
	if _, ok := psc.NetConn().(iconn.Conn); !ok {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
		// this should never happen. if we see it ocurring it means that we added
		// a Listener to the ps.Swarm that is NOT one of our net/conn.Listener.
		return nil, fmt.Errorf("swarm connHandler: invalid conn (not a conn.Conn): %s", psc)
	}
	return (*Conn)(psc), nil
}

// wrapConns returns a *Conn for all these ps.Conns
func wrapConns(conns1 []*ps.Conn) []*Conn {
	conns2 := make([]*Conn, len(conns1))
	for i, c1 := range conns1 {
		if c2, err := wrapConn(c1); err == nil {
			conns2[i] = c2
		}
	}
	return conns2
}

// newConnSetup does the swarm's "setup" for a connection. returns the underlying
// conn.Conn this method is used by both swarm.Dial and ps.Swarm connHandler
func (s *Swarm) newConnSetup(ctx context.Context, psConn *ps.Conn) (*Conn, error) {

	// wrap with a Conn
	sc, err := wrapConn(psConn)
	if err != nil {
		return nil, err
	}

	// if we have a public key, make sure we add it to our peerstore!
	// This is an important detail. Otherwise we must fetch the public
	// key from the DHT or some other system.
	if pk := sc.RemotePublicKey(); pk != nil {
		s.peers.AddPubKey(sc.RemotePeer(), pk)
	}

	// ok great! we can use it. add it to our group.

	// set the RemotePeer as a group on the conn. this lets us group
	// connections in the StreamSwarm by peer, and get a streams from
	// any available connection in the group (better multiconn):
	//   swarm.StreamSwarm().NewStreamWithGroup(remotePeer)
	psConn.AddGroup(sc.RemotePeer())

	return sc, nil
}