github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/p2p/protocol/relay/relay.go (about)

     1  package relay
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  
     7  	mh "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
     8  
     9  	host "github.com/ipfs/go-ipfs/p2p/host"
    10  	inet "github.com/ipfs/go-ipfs/p2p/net"
    11  	peer "github.com/ipfs/go-ipfs/p2p/peer"
    12  	protocol "github.com/ipfs/go-ipfs/p2p/protocol"
    13  	eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog"
    14  )
    15  
    16  var log = eventlog.Logger("p2p/protocol/relay")
    17  
    18  // ID is the protocol.ID of the Relay Service.
    19  const ID protocol.ID = "/ipfs/relay"
    20  
    21  // Relay is a structure that implements ProtocolRelay.
    22  // It is a simple relay service which forwards traffic
    23  // between two directly connected peers.
    24  //
    25  // the protocol is very simple:
    26  //
    27  //   /ipfs/relay\n
    28  //   <multihash src id>
    29  //   <multihash dst id>
    30  //   <data stream>
    31  //
    32  type RelayService struct {
    33  	host    host.Host
    34  	handler inet.StreamHandler // for streams sent to us locally.
    35  }
    36  
    37  func NewRelayService(h host.Host, sh inet.StreamHandler) *RelayService {
    38  	s := &RelayService{
    39  		host:    h,
    40  		handler: sh,
    41  	}
    42  	h.SetStreamHandler(ID, s.requestHandler)
    43  	return s
    44  }
    45  
    46  // requestHandler is the function called by clients
    47  func (rs *RelayService) requestHandler(s inet.Stream) {
    48  	if err := rs.handleStream(s); err != nil {
    49  		log.Debugf("RelayService error:", err)
    50  	}
    51  }
    52  
    53  // handleStream is our own handler, which returns an error for simplicity.
    54  func (rs *RelayService) handleStream(s inet.Stream) error {
    55  	defer s.Close()
    56  
    57  	// read the header (src and dst peer.IDs)
    58  	src, dst, err := ReadHeader(s)
    59  	if err != nil {
    60  		return fmt.Errorf("stream with bad header: %s", err)
    61  	}
    62  
    63  	local := rs.host.ID()
    64  
    65  	switch {
    66  	case src == local:
    67  		return fmt.Errorf("relaying from self")
    68  	case dst == local: // it's for us! yaaay.
    69  		log.Debugf("%s consuming stream from %s", local, src)
    70  		return rs.consumeStream(s)
    71  	default: // src and dst are not local. relay it.
    72  		log.Debugf("%s relaying stream %s <--> %s", local, src, dst)
    73  		return rs.pipeStream(src, dst, s)
    74  	}
    75  }
    76  
    77  // consumeStream connects streams directed to the local peer
    78  // to our handler, with the header now stripped (read).
    79  func (rs *RelayService) consumeStream(s inet.Stream) error {
    80  	rs.handler(s) // boom.
    81  	return nil
    82  }
    83  
    84  // pipeStream relays over a stream to a remote peer. It's like `cat`
    85  func (rs *RelayService) pipeStream(src, dst peer.ID, s inet.Stream) error {
    86  	s2, err := rs.openStreamToPeer(dst)
    87  	if err != nil {
    88  		return fmt.Errorf("failed to open stream to peer: %s -- %s", dst, err)
    89  	}
    90  
    91  	if err := WriteHeader(s2, src, dst); err != nil {
    92  		return err
    93  	}
    94  
    95  	// connect the series of tubes.
    96  	done := make(chan retio, 2)
    97  	go func() {
    98  		n, err := io.Copy(s2, s)
    99  		done <- retio{n, err}
   100  	}()
   101  	go func() {
   102  		n, err := io.Copy(s, s2)
   103  		done <- retio{n, err}
   104  	}()
   105  
   106  	r1 := <-done
   107  	r2 := <-done
   108  	log.Infof("%s relayed %d/%d bytes between %s and %s", rs.host.ID(), r1.n, r2.n, src, dst)
   109  
   110  	if r1.err != nil {
   111  		return r1.err
   112  	}
   113  	return r2.err
   114  }
   115  
   116  // openStreamToPeer opens a pipe to a remote endpoint
   117  // for now, can only open streams to directly connected peers.
   118  // maybe we can do some routing later on.
   119  func (rs *RelayService) openStreamToPeer(p peer.ID) (inet.Stream, error) {
   120  	return rs.host.NewStream(ID, p)
   121  }
   122  
   123  func ReadHeader(r io.Reader) (src, dst peer.ID, err error) {
   124  
   125  	mhr := mh.NewReader(r)
   126  
   127  	s, err := mhr.ReadMultihash()
   128  	if err != nil {
   129  		return "", "", err
   130  	}
   131  
   132  	d, err := mhr.ReadMultihash()
   133  	if err != nil {
   134  		return "", "", err
   135  	}
   136  
   137  	return peer.ID(s), peer.ID(d), nil
   138  }
   139  
   140  func WriteHeader(w io.Writer, src, dst peer.ID) error {
   141  	// write header to w.
   142  	mhw := mh.NewWriter(w)
   143  	if err := mhw.WriteMultihash(mh.Multihash(src)); err != nil {
   144  		return fmt.Errorf("failed to write relay header: %s -- %s", dst, err)
   145  	}
   146  	if err := mhw.WriteMultihash(mh.Multihash(dst)); err != nil {
   147  		return fmt.Errorf("failed to write relay header: %s -- %s", dst, err)
   148  	}
   149  
   150  	return nil
   151  }
   152  
   153  type retio struct {
   154  	n   int64
   155  	err error
   156  }