github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/p2p/host/basic/basic_host.go (about)

     1  package basichost
     2  
     3  import (
     4  	"io"
     5  
     6  	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
     7  	goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
     8  	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
     9  	metrics "github.com/ipfs/go-ipfs/metrics"
    10  	mstream "github.com/ipfs/go-ipfs/metrics/stream"
    11  	eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
    12  
    13  	inet "github.com/ipfs/go-ipfs/p2p/net"
    14  	peer "github.com/ipfs/go-ipfs/p2p/peer"
    15  	protocol "github.com/ipfs/go-ipfs/p2p/protocol"
    16  	identify "github.com/ipfs/go-ipfs/p2p/protocol/identify"
    17  	relay "github.com/ipfs/go-ipfs/p2p/protocol/relay"
    18  )
    19  
    20  var log = eventlog.Logger("p2p/host/basic")
    21  
    22  // Option is a type used to pass in options to the host.
    23  type Option int
    24  
    25  const (
    26  	// NATPortMap makes the host attempt to open port-mapping in NAT devices
    27  	// for all its listeners. Pass in this option in the constructor to
    28  	// asynchronously a) find a gateway, b) open port mappings, c) republish
    29  	// port mappings periodically. The NATed addresses are included in the
    30  	// Host's Addrs() list.
    31  	NATPortMap Option = iota
    32  )
    33  
    34  // BasicHost is the basic implementation of the host.Host interface. This
    35  // particular host implementation:
    36  //  * uses a protocol muxer to mux per-protocol streams
    37  //  * uses an identity service to send + receive node information
    38  //  * uses a relay service to allow hosts to relay conns for each other
    39  //  * uses a nat service to establish NAT port mappings
    40  type BasicHost struct {
    41  	network inet.Network
    42  	mux     *protocol.Mux
    43  	ids     *identify.IDService
    44  	relay   *relay.RelayService
    45  	natmgr  *natManager
    46  
    47  	proc goprocess.Process
    48  
    49  	bwc metrics.Reporter
    50  }
    51  
    52  // New constructs and sets up a new *BasicHost with given Network
    53  func New(net inet.Network, opts ...interface{}) *BasicHost {
    54  	h := &BasicHost{
    55  		network: net,
    56  		mux:     protocol.NewMux(),
    57  		bwc:     metrics.NewBandwidthCounter(),
    58  	}
    59  
    60  	h.proc = goprocess.WithTeardown(func() error {
    61  		if h.natmgr != nil {
    62  			h.natmgr.Close()
    63  		}
    64  
    65  		return h.Network().Close()
    66  	})
    67  
    68  	// setup host services
    69  	h.ids = identify.NewIDService(h)
    70  	h.relay = relay.NewRelayService(h, h.Mux().HandleSync)
    71  
    72  	for _, o := range opts {
    73  		switch o := o.(type) {
    74  		case Option:
    75  			switch o {
    76  			case NATPortMap:
    77  				h.natmgr = newNatManager(h)
    78  			}
    79  		case metrics.Reporter:
    80  			h.bwc = o
    81  		}
    82  	}
    83  
    84  	net.SetConnHandler(h.newConnHandler)
    85  	net.SetStreamHandler(h.newStreamHandler)
    86  
    87  	return h
    88  }
    89  
    90  // newConnHandler is the remote-opened conn handler for inet.Network
    91  func (h *BasicHost) newConnHandler(c inet.Conn) {
    92  	h.ids.IdentifyConn(c)
    93  }
    94  
    95  // newStreamHandler is the remote-opened stream handler for inet.Network
    96  // TODO: this feels a bit wonky
    97  func (h *BasicHost) newStreamHandler(s inet.Stream) {
    98  	protoID, handle, err := h.Mux().ReadHeader(s)
    99  	if err != nil {
   100  		if err == io.EOF {
   101  			log.Debugf("protocol EOF: %s", s.Conn().RemotePeer())
   102  		} else {
   103  			log.Warning("protocol mux failed: %s", err)
   104  		}
   105  		return
   106  	}
   107  
   108  	logStream := mstream.WrapStream(s, protoID, h.bwc)
   109  
   110  	go handle(logStream)
   111  }
   112  
   113  // ID returns the (local) peer.ID associated with this Host
   114  func (h *BasicHost) ID() peer.ID {
   115  	return h.Network().LocalPeer()
   116  }
   117  
   118  // Peerstore returns the Host's repository of Peer Addresses and Keys.
   119  func (h *BasicHost) Peerstore() peer.Peerstore {
   120  	return h.Network().Peerstore()
   121  }
   122  
   123  // Network returns the Network interface of the Host
   124  func (h *BasicHost) Network() inet.Network {
   125  	return h.network
   126  }
   127  
   128  // Mux returns the Mux multiplexing incoming streams to protocol handlers
   129  func (h *BasicHost) Mux() *protocol.Mux {
   130  	return h.mux
   131  }
   132  
   133  // IDService returns
   134  func (h *BasicHost) IDService() *identify.IDService {
   135  	return h.ids
   136  }
   137  
   138  // SetStreamHandler sets the protocol handler on the Host's Mux.
   139  // This is equivalent to:
   140  //   host.Mux().SetHandler(proto, handler)
   141  // (Threadsafe)
   142  func (h *BasicHost) SetStreamHandler(pid protocol.ID, handler inet.StreamHandler) {
   143  	h.Mux().SetHandler(pid, handler)
   144  }
   145  
   146  // RemoveStreamHandler returns ..
   147  func (h *BasicHost) RemoveStreamHandler(pid protocol.ID) {
   148  	h.Mux().RemoveHandler(pid)
   149  }
   150  
   151  // NewStream opens a new stream to given peer p, and writes a p2p/protocol
   152  // header with given protocol.ID. If there is no connection to p, attempts
   153  // to create one. If ProtocolID is "", writes no header.
   154  // (Threadsafe)
   155  func (h *BasicHost) NewStream(pid protocol.ID, p peer.ID) (inet.Stream, error) {
   156  	s, err := h.Network().NewStream(p)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  
   161  	logStream := mstream.WrapStream(s, pid, h.bwc)
   162  
   163  	if err := protocol.WriteHeader(logStream, pid); err != nil {
   164  		logStream.Close()
   165  		return nil, err
   166  	}
   167  
   168  	return logStream, nil
   169  }
   170  
   171  // Connect ensures there is a connection between this host and the peer with
   172  // given peer.ID. Connect will absorb the addresses in pi into its internal
   173  // peerstore. If there is not an active connection, Connect will issue a
   174  // h.Network.Dial, and block until a connection is open, or an error is
   175  // returned. // TODO: Relay + NAT.
   176  func (h *BasicHost) Connect(ctx context.Context, pi peer.PeerInfo) error {
   177  
   178  	// absorb addresses into peerstore
   179  	h.Peerstore().AddAddrs(pi.ID, pi.Addrs, peer.TempAddrTTL)
   180  
   181  	cs := h.Network().ConnsToPeer(pi.ID)
   182  	if len(cs) > 0 {
   183  		return nil
   184  	}
   185  
   186  	return h.dialPeer(ctx, pi.ID)
   187  }
   188  
   189  // dialPeer opens a connection to peer, and makes sure to identify
   190  // the connection once it has been opened.
   191  func (h *BasicHost) dialPeer(ctx context.Context, p peer.ID) error {
   192  	log.Debugf("host %s dialing %s", h.ID, p)
   193  	c, err := h.Network().DialPeer(ctx, p)
   194  	if err != nil {
   195  		return err
   196  	}
   197  
   198  	// identify the connection before returning.
   199  	done := make(chan struct{})
   200  	go func() {
   201  		h.ids.IdentifyConn(c)
   202  		close(done)
   203  	}()
   204  
   205  	// respect don contexteone
   206  	select {
   207  	case <-done:
   208  	case <-ctx.Done():
   209  		return ctx.Err()
   210  	}
   211  
   212  	log.Debugf("host %s finished dialing %s", h.ID, p)
   213  	return nil
   214  }
   215  
   216  // Addrs returns all the addresses of BasicHost at this moment in time.
   217  // It's ok to not include addresses if they're not available to be used now.
   218  func (h *BasicHost) Addrs() []ma.Multiaddr {
   219  	addrs, err := h.Network().InterfaceListenAddresses()
   220  	if err != nil {
   221  		log.Debug("error retrieving network interface addrs")
   222  	}
   223  
   224  	if h.ids != nil { // add external observed addresses
   225  		addrs = append(addrs, h.ids.OwnObservedAddrs()...)
   226  	}
   227  
   228  	if h.natmgr != nil { // natmgr is nil if we do not use nat option.
   229  		nat := h.natmgr.NAT()
   230  		if nat != nil { // nat is nil if not ready, or no nat is available.
   231  			addrs = append(addrs, nat.ExternalAddrs()...)
   232  		}
   233  	}
   234  
   235  	return addrs
   236  }
   237  
   238  // Close shuts down the Host's services (network, etc).
   239  func (h *BasicHost) Close() error {
   240  	return h.proc.Close()
   241  }
   242  
   243  // GetBandwidthReporter exposes the Host's bandiwth metrics reporter
   244  func (h *BasicHost) GetBandwidthReporter() metrics.Reporter {
   245  	return h.bwc
   246  }