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  }