Commit 72df463f authored by Juan Batiz-Benet's avatar Juan Batiz-Benet
Browse files

net -> p2p/net

The net package is the next to move. It will be massaged
a bit still to fix the Network / "NetworkBackend" conflict.
parent bfcb95d6
// Package mocknet provides a mock net.Network to test with.
//
// - a Mocknet has many inet.Networks
// - a Mocknet has many Links
// - a Link joins two inet.Networks
// - inet.Conns and inet.Streams are created by inet.Networks
package mocknet
import (
"io"
"time"
ic "github.com/jbenet/go-ipfs/p2p/crypto"
inet "github.com/jbenet/go-ipfs/p2p/net"
peer "github.com/jbenet/go-ipfs/p2p/peer"
ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
)
type Mocknet interface {
// GenPeer generates a peer and its inet.Network in the Mocknet
GenPeer() (inet.Network, error)
// AddPeer adds an existing peer. we need both a privkey and addr.
// ID is derived from PrivKey
AddPeer(ic.PrivKey, ma.Multiaddr) (inet.Network, error)
// retrieve things (with randomized iteration order)
Peers() []peer.ID
Net(peer.ID) inet.Network
Nets() []inet.Network
Links() LinkMap
LinksBetweenPeers(a, b peer.ID) []Link
LinksBetweenNets(a, b inet.Network) []Link
// Links are the **ability to connect**.
// think of Links as the physical medium.
// For p1 and p2 to connect, a link must exist between them.
// (this makes it possible to test dial failures, and
// things like relaying traffic)
LinkPeers(peer.ID, peer.ID) (Link, error)
LinkNets(inet.Network, inet.Network) (Link, error)
Unlink(Link) error
UnlinkPeers(peer.ID, peer.ID) error
UnlinkNets(inet.Network, inet.Network) error
// LinkDefaults are the default options that govern links
// if they do not have thier own option set.
SetLinkDefaults(LinkOptions)
LinkDefaults() LinkOptions
// Connections are the usual. Connecting means Dialing.
// **to succeed, peers must be linked beforehand**
ConnectPeers(peer.ID, peer.ID) error
ConnectNets(inet.Network, inet.Network) error
DisconnectPeers(peer.ID, peer.ID) error
DisconnectNets(inet.Network, inet.Network) error
}
// LinkOptions are used to change aspects of the links.
// Sorry but they dont work yet :(
type LinkOptions struct {
Latency time.Duration
Bandwidth int // in bytes-per-second
// we can make these values distributions down the road.
}
// Link represents the **possibility** of a connection between
// two peers. Think of it like physical network links. Without
// them, the peers can try and try but they won't be able to
// connect. This allows constructing topologies where specific
// nodes cannot talk to each other directly. :)
type Link interface {
Networks() []inet.Network
Peers() []peer.ID
SetOptions(LinkOptions)
Options() LinkOptions
// Metrics() Metrics
}
// LinkMap is a 3D map to give us an easy way to track links.
// (wow, much map. so data structure. how compose. ahhh pointer)
type LinkMap map[string]map[string]map[Link]struct{}
// Printer lets you inspect things :)
type Printer interface {
// MocknetLinks shows the entire Mocknet's link table :)
MocknetLinks(mn Mocknet)
NetworkConns(ni inet.Network)
}
// PrinterTo returns a Printer ready to write to w.
func PrinterTo(w io.Writer) Printer {
return &printer{w}
}
package mocknet
import (
eventlog "github.com/jbenet/go-ipfs/util/eventlog"
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
)
var log = eventlog.Logger("mocknet")
// WithNPeers constructs a Mocknet with N peers.
func WithNPeers(ctx context.Context, n int) (Mocknet, error) {
m := New(ctx)
for i := 0; i < n; i++ {
if _, err := m.GenPeer(); err != nil {
return nil, err
}
}
return m, nil
}
// FullMeshLinked constructs a Mocknet with full mesh of Links.
// This means that all the peers **can** connect to each other
// (not that they already are connected. you can use m.ConnectAll())
func FullMeshLinked(ctx context.Context, n int) (Mocknet, error) {
m, err := WithNPeers(ctx, n)
if err != nil {
return nil, err
}
nets := m.Nets()
for _, n1 := range nets {
for _, n2 := range nets {
// yes, even self.
if _, err := m.LinkNets(n1, n2); err != nil {
return nil, err
}
}
}
return m, nil
}
// FullMeshConnected constructs a Mocknet with full mesh of Connections.
// This means that all the peers have dialed and are ready to talk to
// each other.
func FullMeshConnected(ctx context.Context, n int) (Mocknet, error) {
m, err := FullMeshLinked(ctx, n)
if err != nil {
return nil, err
}
nets := m.Nets()
for _, n1 := range nets {
for _, n2 := range nets {
if err := m.ConnectNets(n1, n2); err != nil {
return nil, err
}
}
}
return m, nil
}
package mocknet
import (
"container/list"
"sync"
ic "github.com/jbenet/go-ipfs/p2p/crypto"
inet "github.com/jbenet/go-ipfs/p2p/net"
mux "github.com/jbenet/go-ipfs/p2p/net/services/mux"
peer "github.com/jbenet/go-ipfs/p2p/peer"
ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
)
// conn represents one side's perspective of a
// live connection between two peers.
// it goes over a particular link.
type conn struct {
local peer.ID
remote peer.ID
localAddr ma.Multiaddr
remoteAddr ma.Multiaddr
localPrivKey ic.PrivKey
remotePubKey ic.PubKey
net *peernet
link *link
rconn *conn // counterpart
streams list.List
sync.RWMutex
}
func (c *conn) Close() error {
for _, s := range c.allStreams() {
s.Close()
}
c.net.removeConn(c)
return nil
}
func (c *conn) addStream(s *stream) {
c.Lock()
s.conn = c
c.streams.PushBack(s)
c.Unlock()
}
func (c *conn) removeStream(s *stream) {
c.Lock()
defer c.Unlock()
for e := c.streams.Front(); e != nil; e = e.Next() {
if s == e.Value {
c.streams.Remove(e)
return
}
}
}
func (c *conn) allStreams() []inet.Stream {
c.RLock()
defer c.RUnlock()
strs := make([]inet.Stream, 0, c.streams.Len())
for e := c.streams.Front(); e != nil; e = e.Next() {
s := e.Value.(*stream)
strs = append(strs, s)
}
return strs
}
func (c *conn) remoteOpenedStream(s *stream) {
c.addStream(s)
c.net.handleNewStream(s)
}
func (c *conn) openStream() *stream {
sl, sr := c.link.newStreamPair()
c.addStream(sl)
c.rconn.remoteOpenedStream(sr)
return sl
}
func (c *conn) NewStreamWithProtocol(pr inet.ProtocolID) (inet.Stream, error) {
log.Debugf("Conn.NewStreamWithProtocol: %s --> %s", c.local, c.remote)
s := c.openStream()
if err := mux.WriteProtocolHeader(pr, s); err != nil {
s.Close()
return nil, err
}
return s, nil
}
// LocalMultiaddr is the Multiaddr on this side
func (c *conn) LocalMultiaddr() ma.Multiaddr {
return c.localAddr
}
// LocalPeer is the Peer on our side of the connection
func (c *conn) LocalPeer() peer.ID {
return c.local
}
// LocalPrivateKey is the private key of the peer on our side.
func (c *conn) LocalPrivateKey() ic.PrivKey {
return c.localPrivKey
}
// RemoteMultiaddr is the Multiaddr on the remote side
func (c *conn) RemoteMultiaddr() ma.Multiaddr {
return c.remoteAddr
}
// RemotePeer is the Peer on the remote side
func (c *conn) RemotePeer() peer.ID {
return c.remote
}
// RemotePublicKey is the private key of the peer on our side.
func (c *conn) RemotePublicKey() ic.PubKey {
return c.remotePubKey
}
package mocknet
import (
"io"
"sync"
inet "github.com/jbenet/go-ipfs/p2p/net"
peer "github.com/jbenet/go-ipfs/p2p/peer"
)
// link implements mocknet.Link
// and, for simplicity, inet.Conn
type link struct {
mock *mocknet
nets []*peernet
opts LinkOptions
// this could have addresses on both sides.
sync.RWMutex
}
func newLink(mn *mocknet, opts LinkOptions) *link {
return &link{mock: mn, opts: opts}
}
func (l *link) newConnPair(dialer *peernet) (*conn, *conn) {
l.RLock()
defer l.RUnlock()
mkconn := func(ln, rn *peernet) *conn {
c := &conn{net: ln, link: l}
c.local = ln.peer
c.remote = rn.peer
c.localAddr = ln.ps.Addresses(ln.peer)[0]
c.remoteAddr = rn.ps.Addresses(rn.peer)[0]
c.localPrivKey = ln.ps.PrivKey(ln.peer)
c.remotePubKey = rn.ps.PubKey(rn.peer)
return c
}
c1 := mkconn(l.nets[0], l.nets[1])
c2 := mkconn(l.nets[1], l.nets[0])
c1.rconn = c2
c2.rconn = c1
if dialer == c1.net {
return c1, c2
}
return c2, c1
}
func (l *link) newStreamPair() (*stream, *stream) {
r1, w1 := io.Pipe()
r2, w2 := io.Pipe()
s1 := &stream{Reader: r1, Writer: w2}
s2 := &stream{Reader: r2, Writer: w1}
return s1, s2
}
func (l *link) Networks() []inet.Network {
l.RLock()
defer l.RUnlock()
cp := make([]inet.Network, len(l.nets))
for i, n := range l.nets {
cp[i] = n
}
return cp
}
func (l *link) Peers() []peer.ID {
l.RLock()
defer l.RUnlock()
cp := make([]peer.ID, len(l.nets))
for i, n := range l.nets {
cp[i] = n.peer
}
return cp
}
func (l *link) SetOptions(o LinkOptions) {
l.opts = o
}
func (l *link) Options() LinkOptions {
return l.opts
}
package mocknet
import (
"fmt"
"sync"
ic "github.com/jbenet/go-ipfs/p2p/crypto"
inet "github.com/jbenet/go-ipfs/p2p/net"
peer "github.com/jbenet/go-ipfs/p2p/peer"
testutil "github.com/jbenet/go-ipfs/util/testutil"
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
)
// mocknet implements mocknet.Mocknet
type mocknet struct {
// must map on peer.ID (instead of peer.ID) because
// each inet.Network has different peerstore
nets map[peer.ID]*peernet
// links make it possible to connect two peers.
// think of links as the physical medium.
// usually only one, but there could be multiple
// **links are shared between peers**
links map[peer.ID]map[peer.ID]map[*link]struct{}
linkDefaults LinkOptions
cg ctxgroup.ContextGroup // for Context closing
sync.RWMutex
}
func New(ctx context.Context) Mocknet {
return &mocknet{
nets: map[peer.ID]*peernet{},
links: map[peer.ID]map[peer.ID]map[*link]struct{}{},
cg: ctxgroup.WithContext(ctx),
}
}
func (mn *mocknet) GenPeer() (inet.Network, error) {
sk, _, err := testutil.SeededKeyPair(int64(len(mn.nets)))
if err != nil {
return nil, err
}
a := testutil.RandLocalTCPAddress()
n, err := mn.AddPeer(sk, a)
if err != nil {
return nil, err
}
return n, nil
}
func (mn *mocknet) AddPeer(k ic.PrivKey, a ma.Multiaddr) (inet.Network, error) {
n, err := newPeernet(mn.cg.Context(), mn, k, a)
if err != nil {
return nil, err
}
// make sure to add listening address!
// this makes debugging things simpler as remembering to register
// an address may cause unexpected failure.
n.Peerstore().AddAddress(n.LocalPeer(), a)
log.Debugf("mocknet added listen addr for peer: %s -- %s", n.LocalPeer(), a)
mn.cg.AddChildGroup(n.cg)
mn.Lock()
mn.nets[n.peer] = n
mn.Unlock()
return n, nil
}
func (mn *mocknet) Peers() []peer.ID {
mn.RLock()
defer mn.RUnlock()
cp := make([]peer.ID, 0, len(mn.nets))
for _, n := range mn.nets {
cp = append(cp, n.peer)
}
return cp
}
func (mn *mocknet) Net(pid peer.ID) inet.Network {
mn.RLock()
defer mn.RUnlock()
for _, n := range mn.nets {
if n.peer == pid {
return n
}
}
return nil
}
func (mn *mocknet) Nets() []inet.Network {
mn.RLock()
defer mn.RUnlock()
cp := make([]inet.Network, 0, len(mn.nets))
for _, n := range mn.nets {
cp = append(cp, n)
}
return cp
}
// Links returns a copy of the internal link state map.
// (wow, much map. so data structure. how compose. ahhh pointer)
func (mn *mocknet) Links() LinkMap {
mn.RLock()
defer mn.RUnlock()
links := map[string]map[string]map[Link]struct{}{}
for p1, lm := range mn.links {
sp1 := string(p1)
links[sp1] = map[string]map[Link]struct{}{}
for p2, ls := range lm {
sp2 := string(p2)
links[sp1][sp2] = map[Link]struct{}{}
for l := range ls {
links[sp1][sp2][l] = struct{}{}
}
}
}
return links
}
func (mn *mocknet) LinkAll() error {
nets := mn.Nets()
for _, n1 := range nets {
for _, n2 := range nets {
if _, err := mn.LinkNets(n1, n2); err != nil {
return err
}
}
}
return nil
}
func (mn *mocknet) LinkPeers(p1, p2 peer.ID) (Link, error) {
mn.RLock()
n1 := mn.nets[p1]
n2 := mn.nets[p2]
mn.RUnlock()
if n1 == nil {
return nil, fmt.Errorf("network for p1 not in mocknet")
}
if n2 == nil {
return nil, fmt.Errorf("network for p2 not in mocknet")
}
return mn.LinkNets(n1, n2)
}
func (mn *mocknet) validate(n inet.Network) (*peernet, error) {
// WARNING: assumes locks acquired
nr, ok := n.(*peernet)
if !ok {
return nil, fmt.Errorf("Network not supported (use mock package nets only)")
}
if _, found := mn.nets[nr.peer]; !found {
return nil, fmt.Errorf("Network not on mocknet. is it from another mocknet?")
}
return nr, nil
}
func (mn *mocknet) LinkNets(n1, n2 inet.Network) (Link, error) {
mn.RLock()
n1r, err1 := mn.validate(n1)
n2r, err2 := mn.validate(n2)
ld := mn.linkDefaults
mn.RUnlock()
if err1 != nil {
return nil, err1
}
if err2 != nil {
return nil, err2
}
l := newLink(mn, ld)
l.nets = append(l.nets, n1r, n2r)
mn.addLink(l)
return l, nil
}
func (mn *mocknet) Unlink(l2 Link) error {
l, ok := l2.(*link)
if !ok {
return fmt.Errorf("only links from mocknet are supported")
}
mn.removeLink(l)
return nil
}
func (mn *mocknet) UnlinkPeers(p1, p2 peer.ID) error {
ls := mn.LinksBetweenPeers(p1, p2)
if ls == nil {
return fmt.Errorf("no link between p1 and p2")
}
for _, l := range ls {
if err := mn.Unlink(l); err != nil {
return err
}
}
return nil
}
func (mn *mocknet) UnlinkNets(n1, n2 inet.Network) error {
return mn.UnlinkPeers(n1.LocalPeer(), n2.LocalPeer())
}
// get from the links map. and lazily contruct.
func (mn *mocknet) linksMapGet(p1, p2 peer.ID) *map[*link]struct{} {
l1, found := mn.links[p1]
if !found {
mn.links[p1] = map[peer.ID]map[*link]struct{}{}
l1 = mn.links[p1] // so we make sure it's there.
}
l2, found := l1[p2]
if !found {
m := map[*link]struct{}{}
l1[p2] = m
l2 = l1[p2]
}
return &l2
}
func (mn *mocknet) addLink(l *link) {
mn.Lock()
defer mn.Unlock()
n1, n2 := l.nets[0], l.nets[1]
(*mn.linksMapGet(n1.peer, n2.peer))[l] = struct{}{}
(*mn.linksMapGet(n2.peer, n1.peer))[l] = struct{}{}
}
func (mn *mocknet) removeLink(l *link) {
mn.Lock()
defer mn.Unlock()
n1, n2 := l.nets[0], l.nets[1]
delete(*mn.linksMapGet(n1.peer, n2.peer), l)
delete(*mn.linksMapGet(n2.peer, n1.peer), l)
}
func (mn *mocknet) ConnectAll() error {
nets := mn.Nets()
for _, n1 := range nets {
for _, n2 := range nets {
if n1 == n2 {
continue
}
if err := mn.ConnectNets(n1, n2); err != nil {
return err
}
}
}
return nil
}
func (mn *mocknet) ConnectPeers(a, b peer.ID) error {
return mn.Net(a).DialPeer(mn.cg.Context(), b)
}
func (mn *mocknet) ConnectNets(a, b inet.Network) error {
return a.DialPeer(mn.cg.Context(), b.LocalPeer())
}
func (mn *mocknet) DisconnectPeers(p1, p2 peer.ID) error {
return mn.Net(p1).ClosePeer(p2)
}
func (mn *mocknet) DisconnectNets(n1, n2 inet.Network) error {
return n1.ClosePeer(n2.LocalPeer())
}
func (mn *mocknet) LinksBetweenPeers(p1, p2 peer.ID) []Link {
mn.RLock()
defer mn.RUnlock()
ls2 := *mn.linksMapGet(p1, p2)
cp := make([]Link, 0, len(ls2))
for l := range ls2 {
cp = append(cp, l)
}
return cp
}
func (mn *mocknet) LinksBetweenNets(n1, n2 inet.Network) []Link {
return mn.LinksBetweenPeers(n1.LocalPeer(), n2.LocalPeer())
}
func (mn *mocknet) SetLinkDefaults(o LinkOptions) {
mn.Lock()
mn.linkDefaults = o
mn.Unlock()
}
func (mn *mocknet) LinkDefaults() LinkOptions {
mn.RLock()
defer mn.RUnlock()
return mn.linkDefaults
}
package mocknet
import (
"fmt"
"math/rand"
"sync"
ic "github.com/jbenet/go-ipfs/p2p/crypto"
inet "github.com/jbenet/go-ipfs/p2p/net"
ids "github.com/jbenet/go-ipfs/p2p/net/services/identify"
mux "github.com/jbenet/go-ipfs/p2p/net/services/mux"
relay "github.com/jbenet/go-ipfs/p2p/net/services/relay"
peer "github.com/jbenet/go-ipfs/p2p/peer"
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
)
// peernet implements inet.Network
type peernet struct {
mocknet *mocknet // parent
peer peer.ID
ps peer.Peerstore
// conns are actual live connections between peers.
// many conns could run over each link.
// **conns are NOT shared between peers**
connsByPeer map[peer.ID]map[*conn]struct{}
connsByLink map[*link]map[*conn]struct{}
// needed to implement inet.Network
mux mux.Mux
ids *ids.IDService
relay *relay.RelayService
cg ctxgroup.ContextGroup
sync.RWMutex
}
// newPeernet constructs a new peernet
func newPeernet(ctx context.Context, m *mocknet, k ic.PrivKey,
a ma.Multiaddr) (*peernet, error) {
p, err := peer.IDFromPublicKey(k.GetPublic())
if err != nil {
return nil, err
}
// create our own entirely, so that peers knowledge doesn't get shared
ps := peer.NewPeerstore()
ps.AddAddress(p, a)
ps.AddPrivKey(p, k)
ps.AddPubKey(p, k.GetPublic())
n := &peernet{
mocknet: m,
peer: p,
ps: ps,
mux: mux.Mux{Handlers: inet.StreamHandlerMap{}},
cg: ctxgroup.WithContext(ctx),
connsByPeer: map[peer.ID]map[*conn]struct{}{},
connsByLink: map[*link]map[*conn]struct{}{},
}
n.cg.SetTeardown(n.teardown)
// setup a conn handler that immediately "asks the other side about them"
// this is ProtocolIdentify.
n.ids = ids.NewIDService(n)
// setup ProtocolRelay to allow traffic relaying.
// Feed things we get for ourselves into the muxer.
n.relay = relay.NewRelayService(n.cg.Context(), n, n.mux.HandleSync)
return n, nil
}
func (pn *peernet) teardown() error {
// close the connections
for _, c := range pn.allConns() {
c.Close()
}
return nil
}
// allConns returns all the connections between this peer and others
func (pn *peernet) allConns() []*conn {
pn.RLock()
var cs []*conn
for _, csl := range pn.connsByPeer {
for c := range csl {
cs = append(cs, c)
}
}
pn.RUnlock()
return cs
}
// Close calls the ContextCloser func
func (pn *peernet) Close() error {
return pn.cg.Close()
}
func (pn *peernet) Protocols() []inet.ProtocolID {
return pn.mux.Protocols()
}
func (pn *peernet) Peerstore() peer.Peerstore {
return pn.ps
}
func (pn *peernet) String() string {
return fmt.Sprintf("<mock.peernet %s - %d conns>", pn.peer, len(pn.allConns()))
}
// handleNewStream is an internal function to trigger the muxer handler
func (pn *peernet) handleNewStream(s inet.Stream) {
go pn.mux.Handle(s)
}
// DialPeer attempts to establish a connection to a given peer.
// Respects the context.
func (pn *peernet) DialPeer(ctx context.Context, p peer.ID) error {
return pn.connect(p)
}
func (pn *peernet) connect(p peer.ID) error {
// first, check if we already have live connections
pn.RLock()
cs, found := pn.connsByPeer[p]
pn.RUnlock()
if found && len(cs) > 0 {
return nil
}
log.Debugf("%s (newly) dialing %s", pn.peer, p)
// ok, must create a new connection. we need a link
links := pn.mocknet.LinksBetweenPeers(pn.peer, p)
if len(links) < 1 {
return fmt.Errorf("%s cannot connect to %s", pn.peer, p)
}
// if many links found, how do we select? for now, randomly...
// this would be an interesting place to test logic that can measure
// links (network interfaces) and select properly
l := links[rand.Intn(len(links))]
log.Debugf("%s dialing %s openingConn", pn.peer, p)
// create a new connection with link
pn.openConn(p, l.(*link))
return nil
}
func (pn *peernet) openConn(r peer.ID, l *link) *conn {
lc, rc := l.newConnPair(pn)
log.Debugf("%s opening connection to %s", pn.LocalPeer(), lc.RemotePeer())
pn.addConn(lc)
rc.net.remoteOpenedConn(rc)
return lc
}
func (pn *peernet) remoteOpenedConn(c *conn) {
log.Debugf("%s accepting connection from %s", pn.LocalPeer(), c.RemotePeer())
pn.addConn(c)
}
// addConn constructs and adds a connection
// to given remote peer over given link
func (pn *peernet) addConn(c *conn) {
// run the Identify protocol/handshake.
pn.ids.IdentifyConn(c)
pn.Lock()
cs, found := pn.connsByPeer[c.RemotePeer()]
if !found {
cs = map[*conn]struct{}{}
pn.connsByPeer[c.RemotePeer()] = cs
}
pn.connsByPeer[c.RemotePeer()][c] = struct{}{}
cs, found = pn.connsByLink[c.link]
if !found {
cs = map[*conn]struct{}{}
pn.connsByLink[c.link] = cs
}
pn.connsByLink[c.link][c] = struct{}{}
pn.Unlock()
}
// removeConn removes a given conn
func (pn *peernet) removeConn(c *conn) {
pn.Lock()
defer pn.Unlock()
cs, found := pn.connsByLink[c.link]
if !found || len(cs) < 1 {
panic("attempting to remove a conn that doesnt exist")
}
delete(cs, c)
cs, found = pn.connsByPeer[c.remote]
if !found {
panic("attempting to remove a conn that doesnt exist")
}
delete(cs, c)
}
// CtxGroup returns the network's ContextGroup
func (pn *peernet) CtxGroup() ctxgroup.ContextGroup {
return pn.cg
}
// LocalPeer the network's LocalPeer
func (pn *peernet) LocalPeer() peer.ID {
return pn.peer
}
// Peers returns the connected peers
func (pn *peernet) Peers() []peer.ID {
pn.RLock()
defer pn.RUnlock()
peers := make([]peer.ID, 0, len(pn.connsByPeer))
for _, cs := range pn.connsByPeer {
for c := range cs {
peers = append(peers, c.remote)
break
}
}
return peers
}
// Conns returns all the connections of this peer
func (pn *peernet) Conns() []inet.Conn {
pn.RLock()
defer pn.RUnlock()
out := make([]inet.Conn, 0, len(pn.connsByPeer))
for _, cs := range pn.connsByPeer {
for c := range cs {
out = append(out, c)
}
}
return out
}
func (pn *peernet) ConnsToPeer(p peer.ID) []inet.Conn {
pn.RLock()
defer pn.RUnlock()
cs, found := pn.connsByPeer[p]
if !found || len(cs) == 0 {
return nil
}
var cs2 []inet.Conn
for c := range cs {
cs2 = append(cs2, c)
}
return cs2
}
// ClosePeer connections to peer
func (pn *peernet) ClosePeer(p peer.ID) error {
pn.RLock()
cs, found := pn.connsByPeer[p]
pn.RUnlock()
if !found {
return nil
}
for c := range cs {
c.Close()
}
return nil
}
// BandwidthTotals returns the total amount of bandwidth transferred
func (pn *peernet) BandwidthTotals() (in uint64, out uint64) {
// need to implement this. probably best to do it in swarm this time.
// need a "metrics" object
return 0, 0
}
// ListenAddresses returns a list of addresses at which this network listens.
func (pn *peernet) ListenAddresses() []ma.Multiaddr {
return pn.Peerstore().Addresses(pn.LocalPeer())
}
// InterfaceListenAddresses returns a list of addresses at which this network
// listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to
// use the known local interfaces.
func (pn *peernet) InterfaceListenAddresses() ([]ma.Multiaddr, error) {
return pn.ListenAddresses(), nil
}
// Connectedness returns a state signaling connection capabilities
// For now only returns Connecter || NotConnected. Expand into more later.
func (pn *peernet) Connectedness(p peer.ID) inet.Connectedness {
pn.Lock()
defer pn.Unlock()
cs, found := pn.connsByPeer[p]
if found && len(cs) > 0 {
return inet.Connected
}
return inet.NotConnected
}
// NewStream returns a new stream to given peer p.
// If there is no connection to p, attempts to create one.
// If ProtocolID is "", writes no header.
func (pn *peernet) NewStream(pr inet.ProtocolID, p peer.ID) (inet.Stream, error) {
pn.Lock()
defer pn.Unlock()
cs, found := pn.connsByPeer[p]
if !found || len(cs) < 1 {
return nil, fmt.Errorf("no connection to peer")
}
// if many conns are found, how do we select? for now, randomly...
// this would be an interesting place to test logic that can measure
// links (network interfaces) and select properly
n := rand.Intn(len(cs))
var c *conn
for c = range cs {
if n == 0 {
break
}
n--
}
return c.NewStreamWithProtocol(pr)
}
// SetHandler sets the protocol handler on the Network's Muxer.
// This operation is threadsafe.
func (pn *peernet) SetHandler(p inet.ProtocolID, h inet.StreamHandler) {
pn.mux.SetHandler(p, h)
}
func (pn *peernet) IdentifyProtocol() *ids.IDService {
return pn.ids
}
package mocknet
import (
"fmt"
"io"
inet "github.com/jbenet/go-ipfs/p2p/net"
peer "github.com/jbenet/go-ipfs/p2p/peer"
)
// separate object so our interfaces are separate :)
type printer struct {
w io.Writer
}
func (p *printer) MocknetLinks(mn Mocknet) {
links := mn.Links()
fmt.Fprintf(p.w, "Mocknet link map:\n")
for p1, lm := range links {
fmt.Fprintf(p.w, "\t%s linked to:\n", peer.ID(p1))
for p2, l := range lm {
fmt.Fprintf(p.w, "\t\t%s (%d links)\n", peer.ID(p2), len(l))
}
}
fmt.Fprintf(p.w, "\n")
}
func (p *printer) NetworkConns(ni inet.Network) {
fmt.Fprintf(p.w, "%s connected to:\n", ni.LocalPeer())
for _, c := range ni.Conns() {
fmt.Fprintf(p.w, "\t%s (addr: %s)\n", c.RemotePeer(), c.RemoteMultiaddr())
}
fmt.Fprintf(p.w, "\n")
}
package mocknet
import (
"io"
inet "github.com/jbenet/go-ipfs/p2p/net"
)
// stream implements inet.Stream
type stream struct {
io.Reader
io.Writer
conn *conn
}
func (s *stream) Close() error {
s.conn.removeStream(s)
if r, ok := (s.Reader).(io.Closer); ok {
r.Close()
}
if w, ok := (s.Writer).(io.Closer); ok {
return w.Close()
}
return nil
}
func (s *stream) Conn() inet.Conn {
return s.conn
}
package mocknet
import (
"bytes"
"io"
"math/rand"
"sync"
"testing"
inet "github.com/jbenet/go-ipfs/p2p/net"
peer "github.com/jbenet/go-ipfs/p2p/peer"
testutil "github.com/jbenet/go-ipfs/util/testutil"
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
)
func randPeer(t *testing.T) peer.ID {
p, err := testutil.RandPeerID()
if err != nil {
t.Fatal(err)
}
return p
}
func TestNetworkSetup(t *testing.T) {
ctx := context.Background()
sk1, _, err := testutil.RandKeyPair(512)
if err != nil {
t.Fatal(t)
}
sk2, _, err := testutil.RandKeyPair(512)
if err != nil {
t.Fatal(t)
}
sk3, _, err := testutil.RandKeyPair(512)
if err != nil {
t.Fatal(t)
}
mn := New(ctx)
// peers := []peer.ID{p1, p2, p3}
// add peers to mock net
a1 := testutil.RandLocalTCPAddress()
a2 := testutil.RandLocalTCPAddress()
a3 := testutil.RandLocalTCPAddress()
n1, err := mn.AddPeer(sk1, a1)
if err != nil {
t.Fatal(err)
}
p1 := n1.LocalPeer()
n2, err := mn.AddPeer(sk2, a2)
if err != nil {
t.Fatal(err)
}
p2 := n2.LocalPeer()
n3, err := mn.AddPeer(sk3, a3)
if err != nil {
t.Fatal(err)
}
p3 := n3.LocalPeer()
// check peers and net
if mn.Net(p1) != n1 {
t.Error("net for p1.ID != n1")
}
if mn.Net(p2) != n2 {
t.Error("net for p2.ID != n1")
}
if mn.Net(p3) != n3 {
t.Error("net for p3.ID != n1")
}
// link p1<-->p2, p1<-->p1, p2<-->p3, p3<-->p2
l12, err := mn.LinkPeers(p1, p2)
if err != nil {
t.Fatal(err)
}
if !(l12.Networks()[0] == n1 && l12.Networks()[1] == n2) &&
!(l12.Networks()[0] == n2 && l12.Networks()[1] == n1) {
t.Error("l12 networks incorrect")
}
l11, err := mn.LinkPeers(p1, p1)
if err != nil {
t.Fatal(err)
}
if !(l11.Networks()[0] == n1 && l11.Networks()[1] == n1) {
t.Error("l11 networks incorrect")
}
l23, err := mn.LinkPeers(p2, p3)
if err != nil {
t.Fatal(err)
}
if !(l23.Networks()[0] == n2 && l23.Networks()[1] == n3) &&
!(l23.Networks()[0] == n3 && l23.Networks()[1] == n2) {
t.Error("l23 networks incorrect")
}
l32, err := mn.LinkPeers(p3, p2)
if err != nil {
t.Fatal(err)
}
if !(l32.Networks()[0] == n2 && l32.Networks()[1] == n3) &&
!(l32.Networks()[0] == n3 && l32.Networks()[1] == n2) {
t.Error("l32 networks incorrect")
}
// check things
links12 := mn.LinksBetweenPeers(p1, p2)
if len(links12) != 1 {
t.Errorf("should be 1 link bt. p1 and p2 (found %d)", len(links12))
}
if links12[0] != l12 {
t.Error("links 1-2 should be l12.")
}
links11 := mn.LinksBetweenPeers(p1, p1)
if len(links11) != 1 {
t.Errorf("should be 1 link bt. p1 and p1 (found %d)", len(links11))
}
if links11[0] != l11 {
t.Error("links 1-1 should be l11.")
}
links23 := mn.LinksBetweenPeers(p2, p3)
if len(links23) != 2 {
t.Errorf("should be 2 link bt. p2 and p3 (found %d)", len(links23))
}
if !((links23[0] == l23 && links23[1] == l32) ||
(links23[0] == l32 && links23[1] == l23)) {
t.Error("links 2-3 should be l23 and l32.")
}
// unlinking
if err := mn.UnlinkPeers(p2, p1); err != nil {
t.Error(err)
}
// check only one link affected:
links12 = mn.LinksBetweenPeers(p1, p2)
if len(links12) != 0 {
t.Errorf("should be 0 now...", len(links12))
}
links11 = mn.LinksBetweenPeers(p1, p1)
if len(links11) != 1 {
t.Errorf("should be 1 link bt. p1 and p1 (found %d)", len(links11))
}
if links11[0] != l11 {
t.Error("links 1-1 should be l11.")
}
links23 = mn.LinksBetweenPeers(p2, p3)
if len(links23) != 2 {
t.Errorf("should be 2 link bt. p2 and p3 (found %d)", len(links23))
}
if !((links23[0] == l23 && links23[1] == l32) ||
(links23[0] == l32 && links23[1] == l23)) {
t.Error("links 2-3 should be l23 and l32.")
}
// check connecting
// first, no conns
if len(n2.Conns()) > 0 || len(n3.Conns()) > 0 {
t.Error("should have 0 conn. Got: (%d, %d)", len(n2.Conns()), len(n3.Conns()))
}
// connect p2->p3
if err := n2.DialPeer(ctx, p3); err != nil {
t.Error(err)
}
if len(n2.Conns()) != 1 || len(n3.Conns()) != 1 {
t.Errorf("should have (1,1) conn. Got: (%d, %d)", len(n2.Conns()), len(n3.Conns()))
}
// p := PrinterTo(os.Stdout)
// p.NetworkConns(n1)
// p.NetworkConns(n2)
// p.NetworkConns(n3)
// can create a stream 2->3, 3->2,
if _, err := n2.NewStream(inet.ProtocolDiag, p3); err != nil {
t.Error(err)
}
if _, err := n3.NewStream(inet.ProtocolDiag, p2); err != nil {
t.Error(err)
}
// but not 1->2 nor 2->2 (not linked), nor 1->1 (not connected)
if _, err := n1.NewStream(inet.ProtocolDiag, p2); err == nil {
t.Error("should not be able to connect")
}
if _, err := n2.NewStream(inet.ProtocolDiag, p2); err == nil {
t.Error("should not be able to connect")
}
if _, err := n1.NewStream(inet.ProtocolDiag, p1); err == nil {
t.Error("should not be able to connect")
}
// connect p1->p1 (should work)
if err := n1.DialPeer(ctx, p1); err != nil {
t.Error("p1 should be able to dial self.", err)
}
// and a stream too
if _, err := n1.NewStream(inet.ProtocolDiag, p1); err != nil {
t.Error(err)
}
// connect p1->p2
if err := n1.DialPeer(ctx, p2); err == nil {
t.Error("p1 should not be able to dial p2, not connected...")
}
// connect p3->p1
if err := n3.DialPeer(ctx, p1); err == nil {
t.Error("p3 should not be able to dial p1, not connected...")
}
// relink p1->p2
l12, err = mn.LinkPeers(p1, p2)
if err != nil {
t.Fatal(err)
}
if !(l12.Networks()[0] == n1 && l12.Networks()[1] == n2) &&
!(l12.Networks()[0] == n2 && l12.Networks()[1] == n1) {
t.Error("l12 networks incorrect")
}
// should now be able to connect
// connect p1->p2
if err := n1.DialPeer(ctx, p2); err != nil {
t.Error(err)
}
// and a stream should work now too :)
if _, err := n2.NewStream(inet.ProtocolDiag, p3); err != nil {
t.Error(err)
}
}
func TestStreams(t *testing.T) {
mn, err := FullMeshConnected(context.Background(), 3)
if err != nil {
t.Fatal(err)
}
handler := func(s inet.Stream) {
go func() {
b := make([]byte, 4)
if _, err := io.ReadFull(s, b); err != nil {
panic(err)
}
if !bytes.Equal(b, []byte("beep")) {
panic("bytes mismatch")
}
if _, err := s.Write([]byte("boop")); err != nil {
panic(err)
}
s.Close()
}()
}
nets := mn.Nets()
for _, n := range nets {
n.SetHandler(inet.ProtocolDHT, handler)
}
s, err := nets[0].NewStream(inet.ProtocolDHT, nets[1].LocalPeer())
if err != nil {
t.Fatal(err)
}
if _, err := s.Write([]byte("beep")); err != nil {
panic(err)
}
b := make([]byte, 4)
if _, err := io.ReadFull(s, b); err != nil {
panic(err)
}
if !bytes.Equal(b, []byte("boop")) {
panic("bytes mismatch 2")
}
}
func makePinger(st string, n int) func(inet.Stream) {
return func(s inet.Stream) {
go func() {
defer s.Close()
for i := 0; i < n; i++ {
b := make([]byte, 4+len(st))
if _, err := s.Write([]byte("ping" + st)); err != nil {
panic(err)
}
if _, err := io.ReadFull(s, b); err != nil {
panic(err)
}
if !bytes.Equal(b, []byte("pong"+st)) {
panic("bytes mismatch")
}
}
}()
}
}
func makePonger(st string) func(inet.Stream) {
return func(s inet.Stream) {
go func() {
defer s.Close()
for {
b := make([]byte, 4+len(st))
if _, err := io.ReadFull(s, b); err != nil {
if err == io.EOF {
return
}
panic(err)
}
if !bytes.Equal(b, []byte("ping"+st)) {
panic("bytes mismatch")
}
if _, err := s.Write([]byte("pong" + st)); err != nil {
panic(err)
}
}
}()
}
}
func TestStreamsStress(t *testing.T) {
mn, err := FullMeshConnected(context.Background(), 100)
if err != nil {
t.Fatal(err)
}
protos := []inet.ProtocolID{
inet.ProtocolDHT,
inet.ProtocolBitswap,
inet.ProtocolDiag,
}
nets := mn.Nets()
for _, n := range nets {
for _, p := range protos {
n.SetHandler(p, makePonger(string(p)))
}
}
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
from := rand.Intn(len(nets))
to := rand.Intn(len(nets))
p := rand.Intn(3)
proto := protos[p]
s, err := nets[from].NewStream(protos[p], nets[to].LocalPeer())
if err != nil {
log.Debugf("%d (%s) %d (%s) %d (%s)", from, nets[from], to, nets[to], p, protos[p])
panic(err)
}
log.Infof("%d start pinging", i)
makePinger(string(proto), rand.Intn(100))(s)
log.Infof("%d done pinging", i)
}(i)
}
wg.Wait()
}
func TestAdding(t *testing.T) {
mn := New(context.Background())
peers := []peer.ID{}
for i := 0; i < 3; i++ {
sk, _, err := testutil.RandKeyPair(512)
if err != nil {
t.Fatal(err)
}
a := testutil.RandLocalTCPAddress()
n, err := mn.AddPeer(sk, a)
if err != nil {
t.Fatal(err)
}
peers = append(peers, n.LocalPeer())
}
p1 := peers[0]
p2 := peers[1]
// link them
for _, p1 := range peers {
for _, p2 := range peers {
if _, err := mn.LinkPeers(p1, p2); err != nil {
t.Error(err)
}
}
}
// set the new stream handler on p2
n2 := mn.Net(p2)
if n2 == nil {
t.Fatalf("no network for %s", p2)
}
n2.SetHandler(inet.ProtocolBitswap, func(s inet.Stream) {
go func() {
defer s.Close()
b := make([]byte, 4)
if _, err := io.ReadFull(s, b); err != nil {
panic(err)
}
if string(b) != "beep" {
panic("did not beep!")
}
if _, err := s.Write([]byte("boop")); err != nil {
panic(err)
}
}()
})
// connect p1 to p2
if err := mn.ConnectPeers(p1, p2); err != nil {
t.Fatal(err)
}
// talk to p2
n1 := mn.Net(p1)
if n1 == nil {
t.Fatalf("no network for %s", p1)
}
s, err := n1.NewStream(inet.ProtocolBitswap, p2)
if err != nil {
t.Fatal(err)
}
if _, err := s.Write([]byte("beep")); err != nil {
t.Error(err)
}
b := make([]byte, 4)
if _, err := io.ReadFull(s, b); err != nil {
t.Error(err)
}
if !bytes.Equal(b, []byte("boop")) {
t.Error("bytes mismatch 2")
}
}
package identify
import (
"sync"
ggio "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/gogoprotobuf/io"
ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
inet "github.com/jbenet/go-ipfs/p2p/net"
handshake "github.com/jbenet/go-ipfs/p2p/net/handshake"
pb "github.com/jbenet/go-ipfs/p2p/net/handshake/pb"
eventlog "github.com/jbenet/go-ipfs/util/eventlog"
)
var log = eventlog.Logger("net/identify")
// ProtocolIdentify is the ProtocolID of the Identify Service.
const ProtocolIdentify inet.ProtocolID = "/ipfs/identify"
// IDService is a structure that implements ProtocolIdentify.
// It is a trivial service that gives the other peer some
// useful information about the local peer. A sort of hello.
//
// The IDService sends:
// * Our IPFS Protocol Version
// * Our IPFS Agent Version
// * Our public Listen Addresses
type IDService struct {
Network inet.Network
// connections undergoing identification
// for wait purposes
currid map[inet.Conn]chan struct{}
currmu sync.RWMutex
}
func NewIDService(n inet.Network) *IDService {
s := &IDService{
Network: n,
currid: make(map[inet.Conn]chan struct{}),
}
n.SetHandler(ProtocolIdentify, s.RequestHandler)
return s
}
func (ids *IDService) IdentifyConn(c inet.Conn) {
ids.currmu.Lock()
if wait, found := ids.currid[c]; found {
ids.currmu.Unlock()
log.Debugf("IdentifyConn called twice on: %s", c)
<-wait // already identifying it. wait for it.
return
}
ids.currid[c] = make(chan struct{})
ids.currmu.Unlock()
s, err := c.NewStreamWithProtocol(ProtocolIdentify)
if err != nil {
log.Error("network: unable to open initial stream for %s", ProtocolIdentify)
log.Event(ids.Network.CtxGroup().Context(), "IdentifyOpenFailed", c.RemotePeer())
} else {
// ok give the response to our handler.
ids.ResponseHandler(s)
}
ids.currmu.Lock()
ch, found := ids.currid[c]
delete(ids.currid, c)
ids.currmu.Unlock()
if !found {
log.Errorf("IdentifyConn failed to find channel (programmer error) for %s", c)
return
}
close(ch) // release everyone waiting.
}
func (ids *IDService) RequestHandler(s inet.Stream) {
defer s.Close()
c := s.Conn()
w := ggio.NewDelimitedWriter(s)
mes := pb.Handshake3{}
ids.populateMessage(&mes, s.Conn())
w.WriteMsg(&mes)
log.Debugf("%s sent message to %s %s", ProtocolIdentify,
c.RemotePeer(), c.RemoteMultiaddr())
}
func (ids *IDService) ResponseHandler(s inet.Stream) {
defer s.Close()
c := s.Conn()
r := ggio.NewDelimitedReader(s, 2048)
mes := pb.Handshake3{}
if err := r.ReadMsg(&mes); err != nil {
log.Errorf("%s error receiving message from %s %s", ProtocolIdentify,
c.RemotePeer(), c.RemoteMultiaddr())
return
}
ids.consumeMessage(&mes, c)
log.Debugf("%s received message from %s %s", ProtocolIdentify,
c.RemotePeer(), c.RemoteMultiaddr())
}
func (ids *IDService) populateMessage(mes *pb.Handshake3, c inet.Conn) {
// set protocols this node is currently handling
protos := ids.Network.Protocols()
mes.Protocols = make([]string, len(protos))
for i, p := range protos {
mes.Protocols[i] = string(p)
}
// observed address so other side is informed of their
// "public" address, at least in relation to us.
mes.ObservedAddr = c.RemoteMultiaddr().Bytes()
// set listen addrs
laddrs, err := ids.Network.InterfaceListenAddresses()
if err != nil {
log.Error(err)
} else {
mes.ListenAddrs = make([][]byte, len(laddrs))
for i, addr := range laddrs {
mes.ListenAddrs[i] = addr.Bytes()
}
log.Debugf("%s sent listen addrs to %s: %s", c.LocalPeer(), c.RemotePeer(), laddrs)
}
// set protocol versions
mes.H1 = handshake.NewHandshake1("", "")
}
func (ids *IDService) consumeMessage(mes *pb.Handshake3, c inet.Conn) {
p := c.RemotePeer()
// mes.Protocols
// mes.ObservedAddr
// mes.ListenAddrs
laddrs := mes.GetListenAddrs()
lmaddrs := make([]ma.Multiaddr, 0, len(laddrs))
for _, addr := range laddrs {
maddr, err := ma.NewMultiaddrBytes(addr)
if err != nil {
log.Errorf("%s failed to parse multiaddr from %s %s", ProtocolIdentify, p,
c.RemoteMultiaddr())
continue
}
lmaddrs = append(lmaddrs, maddr)
}
// update our peerstore with the addresses.
ids.Network.Peerstore().AddAddresses(p, lmaddrs)
log.Debugf("%s received listen addrs for %s: %s", c.LocalPeer(), c.RemotePeer(), lmaddrs)
// get protocol versions
pv := *mes.H1.ProtocolVersion
av := *mes.H1.AgentVersion
ids.Network.Peerstore().Put(p, "ProtocolVersion", pv)
ids.Network.Peerstore().Put(p, "AgentVersion", av)
}
// IdentifyWait returns a channel which will be closed once
// "ProtocolIdentify" (handshake3) finishes on given conn.
// This happens async so the connection can start to be used
// even if handshake3 knowledge is not necesary.
// Users **MUST** call IdentifyWait _after_ IdentifyConn
func (ids *IDService) IdentifyWait(c inet.Conn) <-chan struct{} {
ids.currmu.Lock()
ch, found := ids.currid[c]
ids.currmu.Unlock()
if found {
return ch
}
// if not found, it means we are already done identifying it, or
// haven't even started. either way, return a new channel closed.
ch = make(chan struct{})
close(ch)
return ch
}
package identify_test
import (
"testing"
"time"
inet "github.com/jbenet/go-ipfs/p2p/net"
handshake "github.com/jbenet/go-ipfs/p2p/net/handshake"
netutil "github.com/jbenet/go-ipfs/p2p/net/swarmnet/util"
peer "github.com/jbenet/go-ipfs/p2p/peer"
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
)
func subtestIDService(t *testing.T, postDialWait time.Duration) {
// the generated networks should have the id service wired in.
ctx := context.Background()
n1 := netutil.GenNetwork(t, ctx)
n2 := netutil.GenNetwork(t, ctx)
n1p := n1.LocalPeer()
n2p := n2.LocalPeer()
testKnowsAddrs(t, n1, n2p, []ma.Multiaddr{}) // nothing
testKnowsAddrs(t, n2, n1p, []ma.Multiaddr{}) // nothing
// have n2 tell n1, so we can dial...
netutil.DivulgeAddresses(n2, n1)
testKnowsAddrs(t, n1, n2p, n2.Peerstore().Addresses(n2p)) // has them
testKnowsAddrs(t, n2, n1p, []ma.Multiaddr{}) // nothing
if err := n1.DialPeer(ctx, n2p); err != nil {
t.Fatalf("Failed to dial:", err)
}
// we need to wait here if Dial returns before ID service is finished.
if postDialWait > 0 {
<-time.After(postDialWait)
}
// the IDService should be opened automatically, by the network.
// what we should see now is that both peers know about each others listen addresses.
testKnowsAddrs(t, n1, n2p, n2.Peerstore().Addresses(n2p)) // has them
testHasProtocolVersions(t, n1, n2p)
// now, this wait we do have to do. it's the wait for the Listening side
// to be done identifying the connection.
c := n2.ConnsToPeer(n1.LocalPeer())
if len(c) < 1 {
t.Fatal("should have connection by now at least.")
}
<-n2.IdentifyProtocol().IdentifyWait(c[0])
// and the protocol versions.
testKnowsAddrs(t, n2, n1p, n1.Peerstore().Addresses(n1p)) // has them
testHasProtocolVersions(t, n2, n1p)
}
func testKnowsAddrs(t *testing.T, n inet.Network, p peer.ID, expected []ma.Multiaddr) {
actual := n.Peerstore().Addresses(p)
if len(actual) != len(expected) {
t.Error("dont have the same addresses")
}
have := map[string]struct{}{}
for _, addr := range actual {
have[addr.String()] = struct{}{}
}
for _, addr := range expected {
if _, found := have[addr.String()]; !found {
t.Errorf("%s did not have addr for %s: %s", n.LocalPeer(), p, addr)
// panic("ahhhhhhh")
}
}
}
func testHasProtocolVersions(t *testing.T, n inet.Network, p peer.ID) {
v, err := n.Peerstore().Get(p, "ProtocolVersion")
if v == nil {
t.Error("no protocol version")
return
}
if v.(string) != handshake.IpfsVersion.String() {
t.Error("protocol mismatch", err)
}
v, err = n.Peerstore().Get(p, "AgentVersion")
if v.(string) != handshake.ClientVersion {
t.Error("agent version mismatch", err)
}
}
// TestIDServiceWait gives the ID service 100ms to finish after dialing
// this is becasue it used to be concurrent. Now, Dial wait till the
// id service is done.
func TestIDServiceWait(t *testing.T) {
N := 3
for i := 0; i < N; i++ {
subtestIDService(t, 100*time.Millisecond)
}
}
func TestIDServiceNoWait(t *testing.T) {
N := 3
for i := 0; i < N; i++ {
subtestIDService(t, 0)
}
}
package mux
import (
"fmt"
"io"
"sync"
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
inet "github.com/jbenet/go-ipfs/p2p/net"
eventlog "github.com/jbenet/go-ipfs/util/eventlog"
lgbl "github.com/jbenet/go-ipfs/util/eventlog/loggables"
)
var log = eventlog.Logger("net/mux")
// Mux provides simple stream multixplexing.
// It helps you precisely when:
// * You have many streams
// * You have function handlers
//
// It contains the handlers for each protocol accepted.
// It dispatches handlers for streams opened by remote peers.
//
// We use a totally ad-hoc encoding:
// <1 byte length in bytes><string name>
// So "bitswap" is 0x0762697473776170
//
// NOTE: only the dialer specifies this muxing line.
// This is because we're using Streams :)
//
// WARNING: this datastructure IS NOT threadsafe.
// do not modify it once the network is using it.
type Mux struct {
Default inet.StreamHandler // handles unknown protocols.
Handlers inet.StreamHandlerMap
sync.RWMutex
}
// Protocols returns the list of protocols this muxer has handlers for
func (m *Mux) Protocols() []inet.ProtocolID {
m.RLock()
l := make([]inet.ProtocolID, 0, len(m.Handlers))
for p := range m.Handlers {
l = append(l, p)
}
m.RUnlock()
return l
}
// ReadProtocolHeader reads the stream and returns the next Handler function
// according to the muxer encoding.
func (m *Mux) ReadProtocolHeader(s io.Reader) (string, inet.StreamHandler, error) {
// log.Error("ReadProtocolHeader")
name, err := ReadLengthPrefix(s)
if err != nil {
return "", nil, err
}
// log.Debug("ReadProtocolHeader got:", name)
m.RLock()
h, found := m.Handlers[inet.ProtocolID(name)]
m.RUnlock()
switch {
case !found && m.Default != nil:
return name, m.Default, nil
case !found && m.Default == nil:
return name, nil, fmt.Errorf("%s no handler with name: %s (%d)", m, name, len(name))
default:
return name, h, nil
}
}
// String returns the muxer's printing representation
func (m *Mux) String() string {
m.RLock()
defer m.RUnlock()
return fmt.Sprintf("<Muxer %p %d>", m, len(m.Handlers))
}
// SetHandler sets the protocol handler on the Network's Muxer.
// This operation is threadsafe.
func (m *Mux) SetHandler(p inet.ProtocolID, h inet.StreamHandler) {
log.Debugf("%s setting handler for protocol: %s (%d)", m, p, len(p))
m.Lock()
m.Handlers[p] = h
m.Unlock()
}
// Handle reads the next name off the Stream, and calls a handler function
// This is done in its own goroutine, to avoid blocking the caller.
func (m *Mux) Handle(s inet.Stream) {
go m.HandleSync(s)
}
// HandleSync reads the next name off the Stream, and calls a handler function
// This is done synchronously. The handler function will return before
// HandleSync returns.
func (m *Mux) HandleSync(s inet.Stream) {
ctx := context.Background()
name, handler, err := m.ReadProtocolHeader(s)
if err != nil {
err = fmt.Errorf("protocol mux error: %s", err)
log.Error(err)
log.Event(ctx, "muxError", lgbl.Error(err))
return
}
log.Infof("muxer handle protocol: %s", name)
log.Event(ctx, "muxHandle", eventlog.Metadata{"protocol": name})
handler(s)
}
// ReadLengthPrefix reads the name from Reader with a length-byte-prefix.
func ReadLengthPrefix(r io.Reader) (string, error) {
// c-string identifier
// the first byte is our length
l := make([]byte, 1)
if _, err := io.ReadFull(r, l); err != nil {
return "", err
}
length := int(l[0])
// the next are our identifier
name := make([]byte, length)
if _, err := io.ReadFull(r, name); err != nil {
return "", err
}
return string(name), nil
}
// WriteLengthPrefix writes the name into Writer with a length-byte-prefix.
func WriteLengthPrefix(w io.Writer, name string) error {
// log.Error("WriteLengthPrefix", name)
s := make([]byte, len(name)+1)
s[0] = byte(len(name))
copy(s[1:], []byte(name))
_, err := w.Write(s)
return err
}
// WriteProtocolHeader defines how a protocol is written into the header of
// a stream. This is so the muxer can multiplex between services.
func WriteProtocolHeader(pr inet.ProtocolID, s inet.Stream) error {
if pr != "" { // only write proper protocol headers
if err := WriteLengthPrefix(s, string(pr)); err != nil {
return err
}
}
return nil
}
package mux
import (
"bytes"
"testing"
inet "github.com/jbenet/go-ipfs/p2p/net"
)
var testCases = map[string]string{
"bitswap": "\u0007bitswap",
"dht": "\u0003dht",
"ipfs": "\u0004ipfs",
"ipfsdksnafkasnfkdajfkdajfdsjadosiaaodjasofdias": ".ipfsdksnafkasnfkdajfkdajfdsjadosiaaodjasofdias",
}
func TestWrite(t *testing.T) {
for k, v := range testCases {
var buf bytes.Buffer
WriteLengthPrefix(&buf, k)
v2 := buf.Bytes()
if !bytes.Equal(v2, []byte(v)) {
t.Errorf("failed: %s - %v != %v", k, []byte(v), v2)
}
}
}
func TestHandler(t *testing.T) {
outs := make(chan string, 10)
h := func(n string) func(s inet.Stream) {
return func(s inet.Stream) {
outs <- n
}
}
m := Mux{Handlers: inet.StreamHandlerMap{}}
m.Default = h("default")
m.Handlers["dht"] = h("bitswap")
// m.Handlers["ipfs"] = h("bitswap") // default!
m.Handlers["bitswap"] = h("bitswap")
m.Handlers["ipfsdksnafkasnfkdajfkdajfdsjadosiaaodjasofdias"] = h("bitswap")
for k, v := range testCases {
var buf bytes.Buffer
if _, err := buf.Write([]byte(v)); err != nil {
t.Error(err)
continue
}
name, _, err := m.ReadProtocolHeader(&buf)
if err != nil {
t.Error(err)
continue
}
if name != k {
t.Errorf("name mismatch: %s != %s", k, name)
continue
}
}
}
package relay
import (
"fmt"
"io"
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
inet "github.com/jbenet/go-ipfs/p2p/net"
peer "github.com/jbenet/go-ipfs/p2p/peer"
eventlog "github.com/jbenet/go-ipfs/util/eventlog"
)
var log = eventlog.Logger("relay")
// ProtocolRelay is the ProtocolID of the Relay Service.
const ProtocolRelay inet.ProtocolID = "/ipfs/relay"
// Relay is a structure that implements ProtocolRelay.
// It is a simple relay service which forwards traffic
// between two directly connected peers.
//
// the protocol is very simple:
//
// /ipfs/relay\n
// <multihash src id>
// <multihash dst id>
// <data stream>
//
type RelayService struct {
Network inet.Network
handler inet.StreamHandler // for streams sent to us locally.
cg ctxgroup.ContextGroup
}
func NewRelayService(ctx context.Context, n inet.Network, sh inet.StreamHandler) *RelayService {
s := &RelayService{
Network: n,
handler: sh,
cg: ctxgroup.WithContext(ctx),
}
n.SetHandler(inet.ProtocolRelay, s.requestHandler)
return s
}
// requestHandler is the function called by clients
func (rs *RelayService) requestHandler(s inet.Stream) {
if err := rs.handleStream(s); err != nil {
log.Error("RelayService error:", err)
}
}
// handleStream is our own handler, which returns an error for simplicity.
func (rs *RelayService) handleStream(s inet.Stream) error {
defer s.Close()
// read the header (src and dst peer.IDs)
src, dst, err := ReadHeader(s)
if err != nil {
return fmt.Errorf("stream with bad header: %s", err)
}
local := rs.Network.LocalPeer()
switch {
case src == local:
return fmt.Errorf("relaying from self")
case dst == local: // it's for us! yaaay.
log.Debugf("%s consuming stream from %s", rs.Network.LocalPeer(), src)
return rs.consumeStream(s)
default: // src and dst are not local. relay it.
log.Debugf("%s relaying stream %s <--> %s", rs.Network.LocalPeer(), src, dst)
return rs.pipeStream(src, dst, s)
}
}
// consumeStream connects streams directed to the local peer
// to our handler, with the header now stripped (read).
func (rs *RelayService) consumeStream(s inet.Stream) error {
rs.handler(s) // boom.
return nil
}
// pipeStream relays over a stream to a remote peer. It's like `cat`
func (rs *RelayService) pipeStream(src, dst peer.ID, s inet.Stream) error {
s2, err := rs.openStreamToPeer(dst)
if err != nil {
return fmt.Errorf("failed to open stream to peer: %s -- %s", dst, err)
}
if err := WriteHeader(s2, src, dst); err != nil {
return err
}
// connect the series of tubes.
done := make(chan retio, 2)
go func() {
n, err := io.Copy(s2, s)
done <- retio{n, err}
}()
go func() {
n, err := io.Copy(s, s2)
done <- retio{n, err}
}()
r1 := <-done
r2 := <-done
log.Infof("relayed %d/%d bytes between %s and %s", r1.n, r2.n, src, dst)
if r1.err != nil {
return r1.err
}
return r2.err
}
// openStreamToPeer opens a pipe to a remote endpoint
// for now, can only open streams to directly connected peers.
// maybe we can do some routing later on.
func (rs *RelayService) openStreamToPeer(p peer.ID) (inet.Stream, error) {
return rs.Network.NewStream(ProtocolRelay, p)
}
func ReadHeader(r io.Reader) (src, dst peer.ID, err error) {
mhr := mh.NewReader(r)
s, err := mhr.ReadMultihash()
if err != nil {
return "", "", err
}
d, err := mhr.ReadMultihash()
if err != nil {
return "", "", err
}
return peer.ID(s), peer.ID(d), nil
}
func WriteHeader(w io.Writer, src, dst peer.ID) error {
// write header to w.
mhw := mh.NewWriter(w)
if err := mhw.WriteMultihash(mh.Multihash(src)); err != nil {
return fmt.Errorf("failed to write relay header: %s -- %s", dst, err)
}
if err := mhw.WriteMultihash(mh.Multihash(dst)); err != nil {
return fmt.Errorf("failed to write relay header: %s -- %s", dst, err)
}
return nil
}
type retio struct {
n int64
err error
}
package relay_test
import (
"io"
"testing"
inet "github.com/jbenet/go-ipfs/p2p/net"
mux "github.com/jbenet/go-ipfs/p2p/net/services/mux"
relay "github.com/jbenet/go-ipfs/p2p/net/services/relay"
netutil "github.com/jbenet/go-ipfs/p2p/net/swarmnet/util"
eventlog "github.com/jbenet/go-ipfs/util/eventlog"
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
)
var log = eventlog.Logger("relay_test")
func TestRelaySimple(t *testing.T) {
ctx := context.Background()
// these networks have the relay service wired in already.
n1 := netutil.GenNetwork(t, ctx)
n2 := netutil.GenNetwork(t, ctx)
n3 := netutil.GenNetwork(t, ctx)
n1p := n1.LocalPeer()
n2p := n2.LocalPeer()
n3p := n3.LocalPeer()
netutil.DivulgeAddresses(n2, n1)
netutil.DivulgeAddresses(n2, n3)
if err := n1.DialPeer(ctx, n2p); err != nil {
t.Fatalf("Failed to dial:", err)
}
if err := n3.DialPeer(ctx, n2p); err != nil {
t.Fatalf("Failed to dial:", err)
}
// setup handler on n3 to copy everything over to the pipe.
piper, pipew := io.Pipe()
n3.SetHandler(inet.ProtocolTesting, func(s inet.Stream) {
log.Debug("relay stream opened to n3!")
log.Debug("piping and echoing everything")
w := io.MultiWriter(s, pipew)
io.Copy(w, s)
log.Debug("closing stream")
s.Close()
})
// ok, now we can try to relay n1--->n2--->n3.
log.Debug("open relay stream")
s, err := n1.NewStream(relay.ProtocolRelay, n2p)
if err != nil {
t.Fatal(err)
}
// ok first thing we write the relay header n1->n3
log.Debug("write relay header")
if err := relay.WriteHeader(s, n1p, n3p); err != nil {
t.Fatal(err)
}
// ok now the header's there, we can write the next protocol header.
log.Debug("write testing header")
if err := mux.WriteProtocolHeader(inet.ProtocolTesting, s); err != nil {
t.Fatal(err)
}
// okay, now we should be able to write text, and read it out.
buf1 := []byte("abcdefghij")
buf2 := make([]byte, 10)
buf3 := make([]byte, 10)
log.Debug("write in some text.")
if _, err := s.Write(buf1); err != nil {
t.Fatal(err)
}
// read it out from the pipe.
log.Debug("read it out from the pipe.")
if _, err := io.ReadFull(piper, buf2); err != nil {
t.Fatal(err)
}
if string(buf1) != string(buf2) {
t.Fatal("should've gotten that text out of the pipe")
}
// read it out from the stream (echoed)
log.Debug("read it out from the stream (echoed).")
if _, err := io.ReadFull(s, buf3); err != nil {
t.Fatal(err)
}
if string(buf1) != string(buf3) {
t.Fatal("should've gotten that text out of the stream")
}
// sweet. relay works.
log.Debug("sweet, relay works.")
s.Close()
}
func TestRelayAcrossFour(t *testing.T) {
ctx := context.Background()
// these networks have the relay service wired in already.
n1 := netutil.GenNetwork(t, ctx)
n2 := netutil.GenNetwork(t, ctx)
n3 := netutil.GenNetwork(t, ctx)
n4 := netutil.GenNetwork(t, ctx)
n5 := netutil.GenNetwork(t, ctx)
n1p := n1.LocalPeer()
n2p := n2.LocalPeer()
n3p := n3.LocalPeer()
n4p := n4.LocalPeer()
n5p := n5.LocalPeer()
netutil.DivulgeAddresses(n2, n1)
netutil.DivulgeAddresses(n2, n3)
netutil.DivulgeAddresses(n4, n3)
netutil.DivulgeAddresses(n4, n5)
if err := n1.DialPeer(ctx, n2p); err != nil {
t.Fatalf("Failed to dial:", err)
}
if err := n3.DialPeer(ctx, n2p); err != nil {
t.Fatalf("Failed to dial:", err)
}
if err := n3.DialPeer(ctx, n4p); err != nil {
t.Fatalf("Failed to dial:", err)
}
if err := n5.DialPeer(ctx, n4p); err != nil {
t.Fatalf("Failed to dial:", err)
}
// setup handler on n5 to copy everything over to the pipe.
piper, pipew := io.Pipe()
n5.SetHandler(inet.ProtocolTesting, func(s inet.Stream) {
log.Debug("relay stream opened to n5!")
log.Debug("piping and echoing everything")
w := io.MultiWriter(s, pipew)
io.Copy(w, s)
log.Debug("closing stream")
s.Close()
})
// ok, now we can try to relay n1--->n2--->n3--->n4--->n5
log.Debug("open relay stream")
s, err := n1.NewStream(relay.ProtocolRelay, n2p)
if err != nil {
t.Fatal(err)
}
log.Debugf("write relay header n1->n3 (%s -> %s)", n1p, n3p)
if err := relay.WriteHeader(s, n1p, n3p); err != nil {
t.Fatal(err)
}
log.Debugf("write relay header n1->n4 (%s -> %s)", n1p, n4p)
if err := mux.WriteProtocolHeader(relay.ProtocolRelay, s); err != nil {
t.Fatal(err)
}
if err := relay.WriteHeader(s, n1p, n4p); err != nil {
t.Fatal(err)
}
log.Debugf("write relay header n1->n5 (%s -> %s)", n1p, n5p)
if err := mux.WriteProtocolHeader(relay.ProtocolRelay, s); err != nil {
t.Fatal(err)
}
if err := relay.WriteHeader(s, n1p, n5p); err != nil {
t.Fatal(err)
}
// ok now the header's there, we can write the next protocol header.
log.Debug("write testing header")
if err := mux.WriteProtocolHeader(inet.ProtocolTesting, s); err != nil {
t.Fatal(err)
}
// okay, now we should be able to write text, and read it out.
buf1 := []byte("abcdefghij")
buf2 := make([]byte, 10)
buf3 := make([]byte, 10)
log.Debug("write in some text.")
if _, err := s.Write(buf1); err != nil {
t.Fatal(err)
}
// read it out from the pipe.
log.Debug("read it out from the pipe.")
if _, err := io.ReadFull(piper, buf2); err != nil {
t.Fatal(err)
}
if string(buf1) != string(buf2) {
t.Fatal("should've gotten that text out of the pipe")
}
// read it out from the stream (echoed)
log.Debug("read it out from the stream (echoed).")
if _, err := io.ReadFull(s, buf3); err != nil {
t.Fatal(err)
}
if string(buf1) != string(buf3) {
t.Fatal("should've gotten that text out of the stream")
}
// sweet. relay works.
log.Debug("sweet, relaying across 4 works.")
s.Close()
}
func TestRelayStress(t *testing.T) {
buflen := 1 << 18
iterations := 10
ctx := context.Background()
// these networks have the relay service wired in already.
n1 := netutil.GenNetwork(t, ctx)
n2 := netutil.GenNetwork(t, ctx)
n3 := netutil.GenNetwork(t, ctx)
n1p := n1.LocalPeer()
n2p := n2.LocalPeer()
n3p := n3.LocalPeer()
netutil.DivulgeAddresses(n2, n1)
netutil.DivulgeAddresses(n2, n3)
if err := n1.DialPeer(ctx, n2p); err != nil {
t.Fatalf("Failed to dial:", err)
}
if err := n3.DialPeer(ctx, n2p); err != nil {
t.Fatalf("Failed to dial:", err)
}
// setup handler on n3 to copy everything over to the pipe.
piper, pipew := io.Pipe()
n3.SetHandler(inet.ProtocolTesting, func(s inet.Stream) {
log.Debug("relay stream opened to n3!")
log.Debug("piping and echoing everything")
w := io.MultiWriter(s, pipew)
io.Copy(w, s)
log.Debug("closing stream")
s.Close()
})
// ok, now we can try to relay n1--->n2--->n3.
log.Debug("open relay stream")
s, err := n1.NewStream(relay.ProtocolRelay, n2p)
if err != nil {
t.Fatal(err)
}
// ok first thing we write the relay header n1->n3
log.Debug("write relay header")
if err := relay.WriteHeader(s, n1p, n3p); err != nil {
t.Fatal(err)
}
// ok now the header's there, we can write the next protocol header.
log.Debug("write testing header")
if err := mux.WriteProtocolHeader(inet.ProtocolTesting, s); err != nil {
t.Fatal(err)
}
// okay, now write lots of text and read it back out from both
// the pipe and the stream.
buf1 := make([]byte, buflen)
buf2 := make([]byte, len(buf1))
buf3 := make([]byte, len(buf1))
fillbuf := func(buf []byte, b byte) {
for i := range buf {
buf[i] = b
}
}
for i := 0; i < iterations; i++ {
fillbuf(buf1, byte(int('a')+i))
log.Debugf("writing %d bytes (%d/%d)", len(buf1), i, iterations)
if _, err := s.Write(buf1); err != nil {
t.Fatal(err)
}
log.Debug("read it out from the pipe.")
if _, err := io.ReadFull(piper, buf2); err != nil {
t.Fatal(err)
}
if string(buf1) != string(buf2) {
t.Fatal("should've gotten that text out of the pipe")
}
// read it out from the stream (echoed)
log.Debug("read it out from the stream (echoed).")
if _, err := io.ReadFull(s, buf3); err != nil {
t.Fatal(err)
}
if string(buf1) != string(buf3) {
t.Fatal("should've gotten that text out of the stream")
}
}
log.Debug("sweet, relay works under stress.")
s.Close()
}
package swarm
import (
conn "github.com/jbenet/go-ipfs/p2p/net/conn"
eventlog "github.com/jbenet/go-ipfs/util/eventlog"
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
)
// ListenAddresses returns a list of addresses at which this swarm listens.
func (s *Swarm) ListenAddresses() []ma.Multiaddr {
listeners := s.swarm.Listeners()
addrs := make([]ma.Multiaddr, 0, len(listeners))
for _, l := range listeners {
if l2, ok := l.NetListener().(conn.Listener); ok {
addrs = append(addrs, l2.Multiaddr())
}
}
return addrs
}
// InterfaceListenAddresses returns a list of addresses at which this swarm
// listens. It expands "any interface" addresses (/ip4/0.0.0.0, /ip6/::) to
// use the known local interfaces.
func InterfaceListenAddresses(s *Swarm) ([]ma.Multiaddr, error) {
return resolveUnspecifiedAddresses(s.ListenAddresses())
}
// resolveUnspecifiedAddresses expands unspecified ip addresses (/ip4/0.0.0.0, /ip6/::) to
// use the known local interfaces.
func resolveUnspecifiedAddresses(unspecifiedAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) {
var outputAddrs []ma.Multiaddr
// todo optimize: only fetch these if we have a "any" addr.
ifaceAddrs, err := interfaceAddresses()
if err != nil {
return nil, err
}
for _, a := range unspecifiedAddrs {
// split address into its components
split := ma.Split(a)
// if first component (ip) is not unspecified, use it as is.
if !manet.IsIPUnspecified(split[0]) {
outputAddrs = append(outputAddrs, a)
continue
}
// unspecified? add one address per interface.
for _, ia := range ifaceAddrs {
split[0] = ia
joined := ma.Join(split...)
outputAddrs = append(outputAddrs, joined)
}
}
log.Event(context.TODO(), "interfaceListenAddresses", func() eventlog.Loggable {
var addrs []string
for _, addr := range outputAddrs {
addrs = append(addrs, addr.String())
}
return eventlog.Metadata{"addresses": addrs}
}())
log.Debug("InterfaceListenAddresses:", outputAddrs)
return outputAddrs, nil
}
// interfaceAddresses returns a list of addresses associated with local machine
func interfaceAddresses() ([]ma.Multiaddr, error) {
maddrs, err := manet.InterfaceMultiaddrs()
if err != nil {
return nil, err
}
var nonLoopback []ma.Multiaddr
for _, a := range maddrs {
if !manet.IsIPLoopback(a) {
nonLoopback = append(nonLoopback, a)
}
}
return nonLoopback, nil
}
// addrInList returns whether or not an address is part of a list.
// this is useful to check if NAT is happening (or other bugs?)
func addrInList(addr ma.Multiaddr, list []ma.Multiaddr) bool {
for _, addr2 := range list {
if addr.Equal(addr2) {
return true
}
}
return false
}
// checkNATWarning checks if our observed addresses differ. if so,
// informs the user that certain things might not work yet
func checkNATWarning(s *Swarm, observed ma.Multiaddr, expected ma.Multiaddr) {
if observed.Equal(expected) {
return
}
listen, err := InterfaceListenAddresses(s)
if err != nil {
log.Errorf("Error retrieving swarm.InterfaceListenAddresses: %s", err)
return
}
if !addrInList(observed, listen) { // probably a nat
log.Warningf(natWarning, observed, listen)
}
}
const natWarning = `Remote peer observed our address to be: %s
The local addresses are: %s
Thus, connection is going through NAT, and other connections may fail.
IPFS NAT traversal is still under development. Please bug us on github or irc to fix this.
Baby steps: http://jbenet.static.s3.amazonaws.com/271dfcf/baby-steps.gif
`
package swarm
import (
"sync"
"testing"
"time"
peer "github.com/jbenet/go-ipfs/p2p/peer"
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
)
func TestSimultOpen(t *testing.T) {
// t.Skip("skipping for another test")
ctx := context.Background()
swarms, peers := makeSwarms(ctx, t, 2)
// connect everyone
{
var wg sync.WaitGroup
connect := func(s *Swarm, dst peer.ID, addr ma.Multiaddr) {
// copy for other peer
s.peers.AddAddress(dst, addr)
if _, err := s.Dial(ctx, dst); err != nil {
t.Fatal("error swarm dialing to peer", err)
}
wg.Done()
}
log.Info("Connecting swarms simultaneously.")
wg.Add(2)
go connect(swarms[0], swarms[1].local, peers[1].Addr)
go connect(swarms[1], swarms[0].local, peers[0].Addr)
wg.Wait()
}
for _, s := range swarms {
s.Close()
}
}
func TestSimultOpenMany(t *testing.T) {
// t.Skip("very very slow")
addrs := 20
SubtestSwarm(t, addrs, 10)
}
func TestSimultOpenFewStress(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
// t.Skip("skipping for another test")
msgs := 40
swarms := 2
rounds := 10
// rounds := 100
for i := 0; i < rounds; i++ {
SubtestSwarm(t, swarms, msgs)
<-time.After(10 * time.Millisecond)
}
}
// package swarm implements a connection muxer with a pair of channels
// to synchronize all network communication.
package swarm
import (
peer "github.com/jbenet/go-ipfs/p2p/peer"
eventlog "github.com/jbenet/go-ipfs/util/eventlog"
context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
ctxgroup "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-ctxgroup"
ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
ps "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-peerstream"
psy "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-peerstream/transport/yamux"
)
var log = eventlog.Logger("swarm2")
var PSTransport = psy.DefaultTransport
// Swarm is a connection muxer, allowing connections to other peers to
// be opened and closed, while still using the same Chan for all
// communication. The Chan sends/receives Messages, which note the
// destination or source Peer.
//
// Uses peerstream.Swarm
type Swarm struct {
swarm *ps.Swarm
local peer.ID
peers peer.Peerstore
connh ConnHandler
cg ctxgroup.ContextGroup
}
// NewSwarm constructs a Swarm, with a Chan.
func NewSwarm(ctx context.Context, listenAddrs []ma.Multiaddr,
local peer.ID, peers peer.Peerstore) (*Swarm, error) {
s := &Swarm{
swarm: ps.NewSwarm(PSTransport),
local: local,
peers: peers,
cg: ctxgroup.WithContext(ctx),
}
// configure Swarm
s.cg.SetTeardown(s.teardown)
s.SetConnHandler(nil) // make sure to setup our own conn handler.
return s, s.listen(listenAddrs)
}
func (s *Swarm) teardown() error {
return s.swarm.Close()
}
// CtxGroup returns the Context Group of the swarm
func (s *Swarm) CtxGroup() ctxgroup.ContextGroup {
return s.cg
}
// Close stops the Swarm.
func (s *Swarm) Close() error {
return s.cg.Close()
}
// StreamSwarm returns the underlying peerstream.Swarm
func (s *Swarm) StreamSwarm() *ps.Swarm {
return s.swarm
}
// SetConnHandler assigns the handler for new connections.
// See peerstream. You will rarely use this. See SetStreamHandler
func (s *Swarm) SetConnHandler(handler ConnHandler) {
// handler is nil if user wants to clear the old handler.
if handler == nil {
s.swarm.SetConnHandler(func(psconn *ps.Conn) {
s.connHandler(psconn)
})
return
}
s.swarm.SetConnHandler(func(psconn *ps.Conn) {
// sc is nil if closed in our handler.
if sc := s.connHandler(psconn); sc != nil {
// call the user's handler. in a goroutine for sync safety.
go handler(sc)
}
})
}
// SetStreamHandler assigns the handler for new streams.
// See peerstream.
func (s *Swarm) SetStreamHandler(handler StreamHandler) {
s.swarm.SetStreamHandler(func(s *ps.Stream) {
handler(wrapStream(s))
})
}
// NewStreamWithPeer creates a new stream on any available connection to p
func (s *Swarm) NewStreamWithPeer(p peer.ID) (*Stream, error) {
// if we have no connections, try connecting.
if len(s.ConnectionsToPeer(p)) == 0 {
log.Debug("Swarm: NewStreamWithPeer no connections. Attempting to connect...")
if _, err := s.Dial(context.Background(), p); err != nil {
return nil, err
}
}
log.Debug("Swarm: NewStreamWithPeer...")
st, err := s.swarm.NewStreamWithGroup(p)
return wrapStream(st), err
}
// StreamsWithPeer returns all the live Streams to p
func (s *Swarm) StreamsWithPeer(p peer.ID) []*Stream {
return wrapStreams(ps.StreamsWithGroup(p, s.swarm.Streams()))
}
// ConnectionsToPeer returns all the live connections to p
func (s *Swarm) ConnectionsToPeer(p peer.ID) []*Conn {
return wrapConns(ps.ConnsWithGroup(p, s.swarm.Conns()))
}
// Connections returns a slice of all connections.
func (s *Swarm) Connections() []*Conn {
return wrapConns(s.swarm.Conns())
}
// CloseConnection removes a given peer from swarm + closes the connection
func (s *Swarm) CloseConnection(p peer.ID) error {
conns := s.swarm.ConnsWithGroup(p) // boom.
for _, c := range conns {
c.Close()
}
return nil
}
// Peers returns a copy of the set of peers swarm is connected to.
func (s *Swarm) Peers() []peer.ID {
conns := s.Connections()
seen := make(map[peer.ID]struct{})
peers := make([]peer.ID, 0, len(conns))
for _, c := range conns {
p := c.RemotePeer()
if _, found := seen[p]; found {
continue
}
peers = append(peers, p)
}
return peers
}
// LocalPeer returns the local peer swarm is associated to.
func (s *Swarm) LocalPeer() peer.ID {
return s.local
}
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment