basic_host.go 3.85 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2 3 4 5
package basichost

import (
	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"

6
	eventlog "github.com/jbenet/go-ipfs/thirdparty/eventlog"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
7

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
8
	inet "github.com/jbenet/go-ipfs/p2p/net"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
9 10 11 12 13 14 15 16 17 18
	peer "github.com/jbenet/go-ipfs/p2p/peer"
	protocol "github.com/jbenet/go-ipfs/p2p/protocol"
	identify "github.com/jbenet/go-ipfs/p2p/protocol/identify"
	relay "github.com/jbenet/go-ipfs/p2p/protocol/relay"
)

var log = eventlog.Logger("p2p/host/basic")

type BasicHost struct {
	network inet.Network
Brian Tiger Chow's avatar
Brian Tiger Chow committed
19
	mux     *protocol.Mux
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
20 21 22 23 24 25 26 27
	ids     *identify.IDService
	relay   *relay.RelayService
}

// New constructs and sets up a new *BasicHost with given Network
func New(net inet.Network) *BasicHost {
	h := &BasicHost{
		network: net,
Brian Tiger Chow's avatar
Brian Tiger Chow committed
28
		mux:     protocol.NewMux(),
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
29 30 31 32 33 34 35 36 37 38 39 40 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
	}

	// setup host services
	h.ids = identify.NewIDService(h)
	h.relay = relay.NewRelayService(h, h.Mux().HandleSync)

	net.SetConnHandler(h.newConnHandler)
	net.SetStreamHandler(h.newStreamHandler)

	return h
}

// newConnHandler is the remote-opened conn handler for inet.Network
func (h *BasicHost) newConnHandler(c inet.Conn) {
	h.ids.IdentifyConn(c)
}

// newStreamHandler is the remote-opened stream handler for inet.Network
func (h *BasicHost) newStreamHandler(s inet.Stream) {
	h.Mux().Handle(s)
}

// ID returns the (local) peer.ID associated with this Host
func (h *BasicHost) ID() peer.ID {
	return h.Network().LocalPeer()
}

// Peerstore returns the Host's repository of Peer Addresses and Keys.
func (h *BasicHost) Peerstore() peer.Peerstore {
	return h.Network().Peerstore()
}

// Networks returns the Network interface of the Host
func (h *BasicHost) Network() inet.Network {
	return h.network
}

// Mux returns the Mux multiplexing incoming streams to protocol handlers
func (h *BasicHost) Mux() *protocol.Mux {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
68
	return h.mux
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 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 142 143 144 145 146 147 148 149
}

func (h *BasicHost) IDService() *identify.IDService {
	return h.ids
}

// SetStreamHandler sets the protocol handler on the Host's Mux.
// This is equivalent to:
//   host.Mux().SetHandler(proto, handler)
// (Threadsafe)
func (h *BasicHost) SetStreamHandler(pid protocol.ID, handler inet.StreamHandler) {
	h.Mux().SetHandler(pid, handler)
}

// NewStream opens a new stream to given peer p, and writes a p2p/protocol
// header with given protocol.ID. If there is no connection to p, attempts
// to create one. If ProtocolID is "", writes no header.
// (Threadsafe)
func (h *BasicHost) NewStream(pid protocol.ID, p peer.ID) (inet.Stream, error) {
	s, err := h.Network().NewStream(p)
	if err != nil {
		return nil, err
	}

	if err := protocol.WriteHeader(s, pid); err != nil {
		s.Close()
		return nil, err
	}

	return s, nil
}

// Connect ensures there is a connection between this host and the peer with
// given peer.ID. Connect will absorb the addresses in pi into its internal
// peerstore. If there is not an active connection, Connect will issue a
// h.Network.Dial, and block until a connection is open, or an error is
// returned. // TODO: Relay + NAT.
func (h *BasicHost) Connect(ctx context.Context, pi peer.PeerInfo) error {

	// absorb addresses into peerstore
	h.Peerstore().AddPeerInfo(pi)

	cs := h.Network().ConnsToPeer(pi.ID)
	if len(cs) > 0 {
		return nil
	}

	return h.dialPeer(ctx, pi.ID)
}

// dialPeer opens a connection to peer, and makes sure to identify
// the connection once it has been opened.
func (h *BasicHost) dialPeer(ctx context.Context, p peer.ID) error {
	log.Debugf("host %s dialing %s", h.ID, p)
	c, err := h.Network().DialPeer(ctx, p)
	if err != nil {
		return err
	}

	// identify the connection before returning.
	done := make(chan struct{})
	go func() {
		h.ids.IdentifyConn(c)
		close(done)
	}()

	// respect don contexteone
	select {
	case <-done:
	case <-ctx.Done():
		return ctx.Err()
	}

	log.Debugf("host %s finished dialing %s", h.ID, p)
	return nil
}

// Close shuts down the Host's services (network, etc).
func (h *BasicHost) Close() error {
	return h.Network().Close()
}