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 }