func (dht *IpfsDHT) findPeerSingle(p *peer.Peer, id peer.ID, timeout time.Duration, level int) (*PBDHTMessage, error) { pmes := Message{ Type: PBDHTMessage_FIND_NODE, Key: string(id), ID: swarm.GenerateMessageID(), Value: []byte{byte(level)}, } mes := swarm.NewMessage(p, pmes.ToProtobuf()) listenChan := dht.listener.Listen(pmes.ID, 1, time.Minute) t := time.Now() dht.netChan.Outgoing <- mes after := time.After(timeout) select { case <-after: dht.listener.Unlisten(pmes.ID) return nil, u.ErrTimeout case resp := <-listenChan: roundtrip := time.Since(t) resp.Peer.SetLatency(roundtrip) pmesOut := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { return nil, err } return pmesOut, nil } }
func (dht *IpfsDHT) findProvidersSingle(p *peer.Peer, key u.Key, level int, timeout time.Duration) (*PBDHTMessage, error) { pmes := Message{ Type: PBDHTMessage_GET_PROVIDERS, Key: string(key), ID: swarm.GenerateMessageID(), Value: []byte{byte(level)}, } mes := swarm.NewMessage(p, pmes.ToProtobuf()) listenChan := dht.listener.Listen(pmes.ID, 1, time.Minute) dht.netChan.Outgoing <- mes after := time.After(timeout) select { case <-after: dht.listener.Unlisten(pmes.ID) return nil, u.ErrTimeout case resp := <-listenChan: u.DOut("FindProviders: got response.\n") pmesOut := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { return nil, err } return pmesOut, nil } }
// getValueSingle simply performs the get value RPC with the given parameters func (dht *IpfsDHT) getValueSingle(p *peer.Peer, key u.Key, timeout time.Duration, level int) (*PBDHTMessage, error) { pmes := Message{ Type: PBDHTMessage_GET_VALUE, Key: string(key), Value: []byte{byte(level)}, ID: swarm.GenerateMessageID(), } responseChan := dht.listener.Listen(pmes.ID, 1, time.Minute) mes := swarm.NewMessage(p, pmes.ToProtobuf()) t := time.Now() dht.netChan.Outgoing <- mes // Wait for either the response or a timeout timeup := time.After(timeout) select { case <-timeup: dht.listener.Unlisten(pmes.ID) return nil, u.ErrTimeout case resp, ok := <-responseChan: if !ok { u.PErr("response channel closed before timeout, please investigate.\n") return nil, u.ErrTimeout } roundtrip := time.Since(t) resp.Peer.SetLatency(roundtrip) pmesOut := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { return nil, err } return pmesOut, nil } }
// Ping a peer, log the time it took func (dht *IpfsDHT) Ping(p *peer.Peer, timeout time.Duration) error { // Thoughts: maybe this should accept an ID and do a peer lookup? u.DOut("Enter Ping.\n") pmes := Message{ID: swarm.GenerateMessageID(), Type: PBDHTMessage_PING} mes := swarm.NewMessage(p, pmes.ToProtobuf()) before := time.Now() responseChan := dht.listener.Listen(pmes.ID, 1, time.Minute) dht.netChan.Outgoing <- mes tout := time.After(timeout) select { case <-responseChan: roundtrip := time.Since(before) p.SetLatency(roundtrip) u.DOut("Ping took %s.\n", roundtrip.String()) return nil case <-tout: // Timed out, think about removing peer from network u.DOut("[%s] Ping peer [%s] timed out.", dht.self.ID.Pretty(), p.ID.Pretty()) dht.listener.Unlisten(pmes.ID) return u.ErrTimeout } }
func (dht *IpfsDHT) putValueToNetwork(p *peer.Peer, key string, value []byte) error { pmes := Message{ Type: PBDHTMessage_PUT_VALUE, Key: key, Value: value, ID: swarm.GenerateMessageID(), } mes := swarm.NewMessage(p, pmes.ToProtobuf()) dht.netChan.Outgoing <- mes return nil }
func (dht *IpfsDHT) getDiagnostic(timeout time.Duration) ([]*diagInfo, error) { u.DOut("Begin Diagnostic") //Send to N closest peers targets := dht.routingTables[0].NearestPeers(kb.ConvertPeerID(dht.self.ID), 10) // TODO: Add timeout to this struct so nodes know when to return pmes := Message{ Type: PBDHTMessage_DIAGNOSTIC, ID: swarm.GenerateMessageID(), } listenChan := dht.listener.Listen(pmes.ID, len(targets), time.Minute*2) pbmes := pmes.ToProtobuf() for _, p := range targets { mes := swarm.NewMessage(p, pbmes) dht.netChan.Outgoing <- mes } var out []*diagInfo after := time.After(timeout) for count := len(targets); count > 0; { select { case <-after: u.DOut("Diagnostic request timed out.") return out, u.ErrTimeout case resp := <-listenChan: pmesOut := new(PBDHTMessage) err := proto.Unmarshal(resp.Data, pmesOut) if err != nil { // NOTE: here and elsewhere, need to audit error handling, // some errors should be continued on from return out, err } dec := json.NewDecoder(bytes.NewBuffer(pmesOut.GetValue())) for { di := new(diagInfo) err := dec.Decode(di) if err != nil { break } out = append(out, di) } } } return nil, nil }
func TestGetFailures(t *testing.T) { fn := newFauxNet() fn.Listen() local := new(peer.Peer) local.ID = peer.ID("test_peer") d := NewDHT(local, fn, ds.NewMapDatastore()) other := &peer.Peer{ID: peer.ID("other_peer")} d.Start() d.Update(other) // This one should time out _, err := d.GetValue(u.Key("test"), time.Millisecond*10) if err != nil { if err != u.ErrTimeout { t.Fatal("Got different error than we expected.") } } else { t.Fatal("Did not get expected error!") } // Reply with failures to every message fn.AddHandler(func(mes *swarm.Message) *swarm.Message { pmes := new(PBDHTMessage) err := proto.Unmarshal(mes.Data, pmes) if err != nil { t.Fatal(err) } resp := Message{ Type: pmes.GetType(), ID: pmes.GetId(), Response: true, Success: false, } return swarm.NewMessage(mes.Peer, resp.ToProtobuf()) }) // This one should fail with NotFound _, err = d.GetValue(u.Key("test"), time.Millisecond*1000) if err != nil { if err != u.ErrNotFound { t.Fatalf("Expected ErrNotFound, got: %s", err) } } else { t.Fatal("expected error, got none.") } success := make(chan struct{}) fn.handlers = nil fn.AddHandler(func(mes *swarm.Message) *swarm.Message { resp := new(PBDHTMessage) err := proto.Unmarshal(mes.Data, resp) if err != nil { t.Fatal(err) } if resp.GetSuccess() { t.Fatal("Get returned success when it shouldnt have.") } success <- struct{}{} return nil }) // Now we test this DHT's handleGetValue failure req := Message{ Type: PBDHTMessage_GET_VALUE, Key: "hello", ID: swarm.GenerateMessageID(), Value: []byte{0}, } fn.Chan.Incoming <- swarm.NewMessage(other, req.ToProtobuf()) <-success }