github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/routing/supernode/proxy/standard.go (about) 1 package proxy 2 3 import ( 4 "errors" 5 6 ggio "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/io" 7 context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" 8 9 key "github.com/ipfs/go-ipfs/blocks/key" 10 host "github.com/ipfs/go-ipfs/p2p/host" 11 inet "github.com/ipfs/go-ipfs/p2p/net" 12 peer "github.com/ipfs/go-ipfs/p2p/peer" 13 dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb" 14 kbucket "github.com/ipfs/go-ipfs/routing/kbucket" 15 eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" 16 ) 17 18 const ProtocolSNR = "/ipfs/supernoderouting" 19 20 var log = eventlog.Logger("supernode/proxy") 21 22 type Proxy interface { 23 Bootstrap(context.Context) error 24 HandleStream(inet.Stream) 25 SendMessage(ctx context.Context, m *dhtpb.Message) error 26 SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) 27 } 28 29 type standard struct { 30 Host host.Host 31 32 remoteInfos []peer.PeerInfo // addr required for bootstrapping 33 remoteIDs []peer.ID // []ID is required for each req. here, cached for performance. 34 } 35 36 func Standard(h host.Host, remotes []peer.PeerInfo) Proxy { 37 var ids []peer.ID 38 for _, remote := range remotes { 39 ids = append(ids, remote.ID) 40 } 41 return &standard{h, remotes, ids} 42 } 43 44 func (px *standard) Bootstrap(ctx context.Context) error { 45 var cxns []peer.PeerInfo 46 for _, info := range px.remoteInfos { 47 if err := px.Host.Connect(ctx, info); err != nil { 48 continue 49 } 50 cxns = append(cxns, info) 51 } 52 if len(cxns) == 0 { 53 log.Error("unable to bootstrap to any supernode routers") 54 } else { 55 log.Infof("bootstrapped to %d supernode routers: %s", len(cxns), cxns) 56 } 57 return nil 58 } 59 60 func (p *standard) HandleStream(s inet.Stream) { 61 // TODO(brian): Should clients be able to satisfy requests? 62 log.Error("supernode client received (dropped) a routing message from", s.Conn().RemotePeer()) 63 s.Close() 64 } 65 66 const replicationFactor = 2 67 68 // SendMessage sends message to each remote sequentially (randomized order), 69 // stopping after the first successful response. If all fail, returns the last 70 // error. 71 func (px *standard) SendMessage(ctx context.Context, m *dhtpb.Message) error { 72 var err error 73 var numSuccesses int 74 for _, remote := range sortedByKey(px.remoteIDs, m.GetKey()) { 75 if err = px.sendMessage(ctx, m, remote); err != nil { // careful don't re-declare err! 76 continue 77 } 78 numSuccesses++ 79 switch m.GetType() { 80 case dhtpb.Message_ADD_PROVIDER, dhtpb.Message_PUT_VALUE: 81 if numSuccesses < replicationFactor { 82 continue 83 } 84 } 85 return nil // success 86 } 87 return err // NB: returns the last error 88 } 89 90 func (px *standard) sendMessage(ctx context.Context, m *dhtpb.Message, remote peer.ID) (err error) { 91 e := log.EventBegin(ctx, "sendRoutingMessage", px.Host.ID(), remote, m) 92 defer func() { 93 if err != nil { 94 e.SetError(err) 95 } 96 e.Done() 97 }() 98 if err = px.Host.Connect(ctx, peer.PeerInfo{ID: remote}); err != nil { 99 return err 100 } 101 s, err := px.Host.NewStream(ProtocolSNR, remote) 102 if err != nil { 103 return err 104 } 105 defer s.Close() 106 pbw := ggio.NewDelimitedWriter(s) 107 if err := pbw.WriteMsg(m); err != nil { 108 return err 109 } 110 return nil 111 } 112 113 // SendRequest sends the request to each remote sequentially (randomized order), 114 // stopping after the first successful response. If all fail, returns the last 115 // error. 116 func (px *standard) SendRequest(ctx context.Context, m *dhtpb.Message) (*dhtpb.Message, error) { 117 var err error 118 for _, remote := range sortedByKey(px.remoteIDs, m.GetKey()) { 119 var reply *dhtpb.Message 120 reply, err = px.sendRequest(ctx, m, remote) // careful don't redeclare err! 121 if err != nil { 122 continue 123 } 124 return reply, nil // success 125 } 126 return nil, err // NB: returns the last error 127 } 128 129 func (px *standard) sendRequest(ctx context.Context, m *dhtpb.Message, remote peer.ID) (*dhtpb.Message, error) { 130 e := log.EventBegin(ctx, "sendRoutingRequest", px.Host.ID(), remote, eventlog.Pair("request", m)) 131 defer e.Done() 132 if err := px.Host.Connect(ctx, peer.PeerInfo{ID: remote}); err != nil { 133 e.SetError(err) 134 return nil, err 135 } 136 s, err := px.Host.NewStream(ProtocolSNR, remote) 137 if err != nil { 138 e.SetError(err) 139 return nil, err 140 } 141 defer s.Close() 142 r := ggio.NewDelimitedReader(s, inet.MessageSizeMax) 143 w := ggio.NewDelimitedWriter(s) 144 if err = w.WriteMsg(m); err != nil { 145 e.SetError(err) 146 return nil, err 147 } 148 149 response := &dhtpb.Message{} 150 if err = r.ReadMsg(response); err != nil { 151 e.SetError(err) 152 return nil, err 153 } 154 // need ctx expiration? 155 if response == nil { 156 err := errors.New("no response to request") 157 e.SetError(err) 158 return nil, err 159 } 160 e.Append(eventlog.Pair("response", response)) 161 e.Append(eventlog.Pair("uuid", eventlog.Uuid("foo"))) 162 return response, nil 163 } 164 165 func sortedByKey(peers []peer.ID, skey string) []peer.ID { 166 target := kbucket.ConvertKey(key.Key(skey)) 167 return kbucket.SortClosestPeers(peers, target) 168 }