github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/core/commands/ping.go (about) 1 package commands 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "reflect" 8 "strings" 9 "time" 10 11 cmds "github.com/ipfs/go-ipfs/commands" 12 core "github.com/ipfs/go-ipfs/core" 13 peer "github.com/ipfs/go-ipfs/p2p/peer" 14 u "github.com/ipfs/go-ipfs/util" 15 16 ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" 17 context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" 18 ) 19 20 const kPingTimeout = 10 * time.Second 21 22 type PingResult struct { 23 Success bool 24 Time time.Duration 25 Text string 26 } 27 28 var PingCmd = &cmds.Command{ 29 Helptext: cmds.HelpText{ 30 Tagline: "send echo request packets to IPFS hosts", 31 Synopsis: ` 32 Send pings to a peer using the routing system to discover its address 33 `, 34 ShortDescription: ` 35 ipfs ping is a tool to test sending data to other nodes. It finds nodes 36 via the routing system, send pings, wait for pongs, and print out round- 37 trip latency information. 38 `, 39 }, 40 Arguments: []cmds.Argument{ 41 cmds.StringArg("peer ID", true, true, "ID of peer to be pinged").EnableStdin(), 42 }, 43 Options: []cmds.Option{ 44 cmds.IntOption("count", "n", "number of ping messages to send"), 45 }, 46 Marshalers: cmds.MarshalerMap{ 47 cmds.Text: func(res cmds.Response) (io.Reader, error) { 48 outChan, ok := res.Output().(<-chan interface{}) 49 if !ok { 50 fmt.Println(reflect.TypeOf(res.Output())) 51 return nil, u.ErrCast() 52 } 53 54 marshal := func(v interface{}) (io.Reader, error) { 55 obj, ok := v.(*PingResult) 56 if !ok { 57 return nil, u.ErrCast() 58 } 59 60 buf := new(bytes.Buffer) 61 if len(obj.Text) > 0 { 62 buf = bytes.NewBufferString(obj.Text + "\n") 63 } else if obj.Success { 64 fmt.Fprintf(buf, "Pong received: time=%.2f ms\n", obj.Time.Seconds()*1000) 65 } else { 66 fmt.Fprintf(buf, "Pong failed\n") 67 } 68 return buf, nil 69 } 70 71 return &cmds.ChannelMarshaler{ 72 Channel: outChan, 73 Marshaler: marshal, 74 Res: res, 75 }, nil 76 }, 77 }, 78 Run: func(req cmds.Request, res cmds.Response) { 79 ctx := req.Context() 80 n, err := req.InvocContext().GetNode() 81 if err != nil { 82 res.SetError(err, cmds.ErrNormal) 83 return 84 } 85 86 // Must be online! 87 if !n.OnlineMode() { 88 res.SetError(errNotOnline, cmds.ErrClient) 89 return 90 } 91 92 addr, peerID, err := ParsePeerParam(req.Arguments()[0]) 93 if err != nil { 94 res.SetError(err, cmds.ErrNormal) 95 return 96 } 97 98 if addr != nil { 99 n.Peerstore.AddAddr(peerID, addr, peer.TempAddrTTL) // temporary 100 } 101 102 // Set up number of pings 103 numPings := 10 104 val, found, err := req.Option("count").Int() 105 if err != nil { 106 res.SetError(err, cmds.ErrNormal) 107 return 108 } 109 if found { 110 numPings = val 111 } 112 113 outChan := pingPeer(ctx, n, peerID, numPings) 114 res.SetOutput(outChan) 115 }, 116 Type: PingResult{}, 117 } 118 119 func pingPeer(ctx context.Context, n *core.IpfsNode, pid peer.ID, numPings int) <-chan interface{} { 120 outChan := make(chan interface{}) 121 go func() { 122 defer close(outChan) 123 124 if len(n.Peerstore.Addrs(pid)) == 0 { 125 // Make sure we can find the node in question 126 outChan <- &PingResult{ 127 Text: fmt.Sprintf("Looking up peer %s", pid.Pretty()), 128 } 129 130 ctx, cancel := context.WithTimeout(ctx, kPingTimeout) 131 defer cancel() 132 p, err := n.Routing.FindPeer(ctx, pid) 133 if err != nil { 134 outChan <- &PingResult{Text: fmt.Sprintf("Peer lookup error: %s", err)} 135 return 136 } 137 n.Peerstore.AddAddrs(p.ID, p.Addrs, peer.TempAddrTTL) 138 } 139 140 outChan <- &PingResult{Text: fmt.Sprintf("PING %s.", pid.Pretty())} 141 142 ctx, cancel := context.WithTimeout(ctx, kPingTimeout*time.Duration(numPings)) 143 defer cancel() 144 pings, err := n.Ping.Ping(ctx, pid) 145 if err != nil { 146 log.Debugf("Ping error: %s", err) 147 outChan <- &PingResult{Text: fmt.Sprintf("Ping error: %s", err)} 148 return 149 } 150 151 var done bool 152 var total time.Duration 153 for i := 0; i < numPings && !done; i++ { 154 select { 155 case <-ctx.Done(): 156 done = true 157 break 158 case t, ok := <-pings: 159 if !ok { 160 done = true 161 break 162 } 163 164 outChan <- &PingResult{ 165 Success: true, 166 Time: t, 167 } 168 total += t 169 time.Sleep(time.Second) 170 } 171 } 172 averagems := total.Seconds() * 1000 / float64(numPings) 173 outChan <- &PingResult{ 174 Text: fmt.Sprintf("Average latency: %.2fms", averagems), 175 } 176 }() 177 return outChan 178 } 179 180 func ParsePeerParam(text string) (ma.Multiaddr, peer.ID, error) { 181 // to be replaced with just multiaddr parsing, once ptp is a multiaddr protocol 182 idx := strings.LastIndex(text, "/") 183 if idx == -1 { 184 pid, err := peer.IDB58Decode(text) 185 if err != nil { 186 return nil, "", err 187 } 188 189 return nil, pid, nil 190 } 191 192 addrS := text[:idx] 193 peeridS := text[idx+1:] 194 195 var maddr ma.Multiaddr 196 var pid peer.ID 197 198 // make sure addrS parses as a multiaddr. 199 if len(addrS) > 0 { 200 var err error 201 maddr, err = ma.NewMultiaddr(addrS) 202 if err != nil { 203 return nil, "", err 204 } 205 } 206 207 // make sure idS parses as a peer.ID 208 var err error 209 pid, err = peer.IDB58Decode(peeridS) 210 if err != nil { 211 return nil, "", err 212 } 213 214 return maddr, pid, nil 215 }