mirror of
				https://github.com/ipfs/kubo.git
				synced 2025-10-30 17:39:49 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			215 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			215 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package commands
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"reflect"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	cmds "github.com/ipfs/go-ipfs/commands"
 | |
| 	core "github.com/ipfs/go-ipfs/core"
 | |
| 	peer "github.com/ipfs/go-ipfs/p2p/peer"
 | |
| 	u "github.com/ipfs/go-ipfs/util"
 | |
| 
 | |
| 	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
 | |
| 	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
 | |
| )
 | |
| 
 | |
| const kPingTimeout = 10 * time.Second
 | |
| 
 | |
| type PingResult struct {
 | |
| 	Success bool
 | |
| 	Time    time.Duration
 | |
| 	Text    string
 | |
| }
 | |
| 
 | |
| var PingCmd = &cmds.Command{
 | |
| 	Helptext: cmds.HelpText{
 | |
| 		Tagline: "send echo request packets to IPFS hosts",
 | |
| 		Synopsis: `
 | |
| Send pings to a peer using the routing system to discover its address
 | |
| 		`,
 | |
| 		ShortDescription: `
 | |
| ipfs ping is a tool to test sending data to other nodes. It finds nodes
 | |
| via the routing system, send pings, wait for pongs, and print out round-
 | |
| trip latency information.
 | |
| 		`,
 | |
| 	},
 | |
| 	Arguments: []cmds.Argument{
 | |
| 		cmds.StringArg("peer ID", true, true, "ID of peer to be pinged").EnableStdin(),
 | |
| 	},
 | |
| 	Options: []cmds.Option{
 | |
| 		cmds.IntOption("count", "n", "number of ping messages to send"),
 | |
| 	},
 | |
| 	Marshalers: cmds.MarshalerMap{
 | |
| 		cmds.Text: func(res cmds.Response) (io.Reader, error) {
 | |
| 			outChan, ok := res.Output().(<-chan interface{})
 | |
| 			if !ok {
 | |
| 				fmt.Println(reflect.TypeOf(res.Output()))
 | |
| 				return nil, u.ErrCast()
 | |
| 			}
 | |
| 
 | |
| 			marshal := func(v interface{}) (io.Reader, error) {
 | |
| 				obj, ok := v.(*PingResult)
 | |
| 				if !ok {
 | |
| 					return nil, u.ErrCast()
 | |
| 				}
 | |
| 
 | |
| 				buf := new(bytes.Buffer)
 | |
| 				if len(obj.Text) > 0 {
 | |
| 					buf = bytes.NewBufferString(obj.Text + "\n")
 | |
| 				} else if obj.Success {
 | |
| 					fmt.Fprintf(buf, "Pong received: time=%.2f ms\n", obj.Time.Seconds()*1000)
 | |
| 				} else {
 | |
| 					fmt.Fprintf(buf, "Pong failed\n")
 | |
| 				}
 | |
| 				return buf, nil
 | |
| 			}
 | |
| 
 | |
| 			return &cmds.ChannelMarshaler{
 | |
| 				Channel:   outChan,
 | |
| 				Marshaler: marshal,
 | |
| 			}, nil
 | |
| 		},
 | |
| 	},
 | |
| 	Run: func(req cmds.Request, res cmds.Response) {
 | |
| 		ctx := req.Context()
 | |
| 		n, err := req.InvocContext().GetNode()
 | |
| 		if err != nil {
 | |
| 			res.SetError(err, cmds.ErrNormal)
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		// Must be online!
 | |
| 		if !n.OnlineMode() {
 | |
| 			res.SetError(errNotOnline, cmds.ErrClient)
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		addr, peerID, err := ParsePeerParam(req.Arguments()[0])
 | |
| 		if err != nil {
 | |
| 			res.SetError(err, cmds.ErrNormal)
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		if addr != nil {
 | |
| 			n.Peerstore.AddAddr(peerID, addr, peer.TempAddrTTL) // temporary
 | |
| 		}
 | |
| 
 | |
| 		// Set up number of pings
 | |
| 		numPings := 10
 | |
| 		val, found, err := req.Option("count").Int()
 | |
| 		if err != nil {
 | |
| 			res.SetError(err, cmds.ErrNormal)
 | |
| 			return
 | |
| 		}
 | |
| 		if found {
 | |
| 			numPings = val
 | |
| 		}
 | |
| 
 | |
| 		outChan := pingPeer(ctx, n, peerID, numPings)
 | |
| 		res.SetOutput(outChan)
 | |
| 	},
 | |
| 	Type: PingResult{},
 | |
| }
 | |
| 
 | |
| func pingPeer(ctx context.Context, n *core.IpfsNode, pid peer.ID, numPings int) <-chan interface{} {
 | |
| 	outChan := make(chan interface{})
 | |
| 	go func() {
 | |
| 		defer close(outChan)
 | |
| 
 | |
| 		if len(n.Peerstore.Addrs(pid)) == 0 {
 | |
| 			// Make sure we can find the node in question
 | |
| 			outChan <- &PingResult{
 | |
| 				Text: fmt.Sprintf("Looking up peer %s", pid.Pretty()),
 | |
| 			}
 | |
| 
 | |
| 			ctx, cancel := context.WithTimeout(ctx, kPingTimeout)
 | |
| 			defer cancel()
 | |
| 			p, err := n.Routing.FindPeer(ctx, pid)
 | |
| 			if err != nil {
 | |
| 				outChan <- &PingResult{Text: fmt.Sprintf("Peer lookup error: %s", err)}
 | |
| 				return
 | |
| 			}
 | |
| 			n.Peerstore.AddAddrs(p.ID, p.Addrs, peer.TempAddrTTL)
 | |
| 		}
 | |
| 
 | |
| 		outChan <- &PingResult{Text: fmt.Sprintf("PING %s.", pid.Pretty())}
 | |
| 
 | |
| 		ctx, cancel := context.WithTimeout(ctx, kPingTimeout*time.Duration(numPings))
 | |
| 		defer cancel()
 | |
| 		pings, err := n.Ping.Ping(ctx, pid)
 | |
| 		if err != nil {
 | |
| 			log.Debugf("Ping error: %s", err)
 | |
| 			outChan <- &PingResult{Text: fmt.Sprintf("Ping error: %s", err)}
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		var done bool
 | |
| 		var total time.Duration
 | |
| 		for i := 0; i < numPings && !done; i++ {
 | |
| 			select {
 | |
| 			case <-ctx.Done():
 | |
| 				done = true
 | |
| 				break
 | |
| 			case t, ok := <-pings:
 | |
| 				if !ok {
 | |
| 					done = true
 | |
| 					break
 | |
| 				}
 | |
| 
 | |
| 				outChan <- &PingResult{
 | |
| 					Success: true,
 | |
| 					Time:    t,
 | |
| 				}
 | |
| 				total += t
 | |
| 				time.Sleep(time.Second)
 | |
| 			}
 | |
| 		}
 | |
| 		averagems := total.Seconds() * 1000 / float64(numPings)
 | |
| 		outChan <- &PingResult{
 | |
| 			Text: fmt.Sprintf("Average latency: %.2fms", averagems),
 | |
| 		}
 | |
| 	}()
 | |
| 	return outChan
 | |
| }
 | |
| 
 | |
| func ParsePeerParam(text string) (ma.Multiaddr, peer.ID, error) {
 | |
| 	// to be replaced with just multiaddr parsing, once ptp is a multiaddr protocol
 | |
| 	idx := strings.LastIndex(text, "/")
 | |
| 	if idx == -1 {
 | |
| 		pid, err := peer.IDB58Decode(text)
 | |
| 		if err != nil {
 | |
| 			return nil, "", err
 | |
| 		}
 | |
| 
 | |
| 		return nil, pid, nil
 | |
| 	}
 | |
| 
 | |
| 	addrS := text[:idx]
 | |
| 	peeridS := text[idx+1:]
 | |
| 
 | |
| 	var maddr ma.Multiaddr
 | |
| 	var pid peer.ID
 | |
| 
 | |
| 	// make sure addrS parses as a multiaddr.
 | |
| 	if len(addrS) > 0 {
 | |
| 		var err error
 | |
| 		maddr, err = ma.NewMultiaddr(addrS)
 | |
| 		if err != nil {
 | |
| 			return nil, "", err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// make sure idS parses as a peer.ID
 | |
| 	var err error
 | |
| 	pid, err = peer.IDB58Decode(peeridS)
 | |
| 	if err != nil {
 | |
| 		return nil, "", err
 | |
| 	}
 | |
| 
 | |
| 	return maddr, pid, nil
 | |
| }
 | 
