OSDN Git Service

add ipfs client
[bytom/vapor.git] / vendor / github.com / ipfs / go-ipfs-api / shell.go
1 // package shell implements a remote API interface for a running ipfs daemon
2 package shell
3
4 import (
5         "bytes"
6         "context"
7         "encoding/json"
8         "errors"
9         "fmt"
10         "io"
11         "io/ioutil"
12         gohttp "net/http"
13         "os"
14         "path"
15         "strings"
16         "time"
17
18         files "github.com/ipfs/go-ipfs-files"
19         homedir "github.com/mitchellh/go-homedir"
20         ma "github.com/multiformats/go-multiaddr"
21         manet "github.com/multiformats/go-multiaddr-net"
22         tar "github.com/whyrusleeping/tar-utils"
23 )
24
25 const (
26         DefaultPathName = ".ipfs"
27         DefaultPathRoot = "~/" + DefaultPathName
28         DefaultApiFile  = "api"
29         EnvDir          = "IPFS_PATH"
30 )
31
32 type Shell struct {
33         url     string
34         httpcli gohttp.Client
35 }
36
37 func NewLocalShell() *Shell {
38         baseDir := os.Getenv(EnvDir)
39         if baseDir == "" {
40                 baseDir = DefaultPathRoot
41         }
42
43         baseDir, err := homedir.Expand(baseDir)
44         if err != nil {
45                 return nil
46         }
47
48         apiFile := path.Join(baseDir, DefaultApiFile)
49
50         if _, err := os.Stat(apiFile); err != nil {
51                 return nil
52         }
53
54         api, err := ioutil.ReadFile(apiFile)
55         if err != nil {
56                 return nil
57         }
58
59         return NewShell(strings.TrimSpace(string(api)))
60 }
61
62 func NewShell(url string) *Shell {
63         c := &gohttp.Client{
64                 Transport: &gohttp.Transport{
65                         Proxy:             gohttp.ProxyFromEnvironment,
66                         DisableKeepAlives: true,
67                 },
68         }
69
70         return NewShellWithClient(url, c)
71 }
72
73 func NewShellWithClient(url string, c *gohttp.Client) *Shell {
74         if a, err := ma.NewMultiaddr(url); err == nil {
75                 _, host, err := manet.DialArgs(a)
76                 if err == nil {
77                         url = host
78                 }
79         }
80         var sh Shell
81         sh.url = url
82         sh.httpcli = *c
83         // We don't support redirects.
84         sh.httpcli.CheckRedirect = func(_ *gohttp.Request, _ []*gohttp.Request) error {
85                 return fmt.Errorf("unexpected redirect")
86         }
87         return &sh
88 }
89
90 func (s *Shell) SetTimeout(d time.Duration) {
91         s.httpcli.Timeout = d
92 }
93
94 func (s *Shell) Request(command string, args ...string) *RequestBuilder {
95         return &RequestBuilder{
96                 command: command,
97                 args:    args,
98                 shell:   s,
99         }
100 }
101
102 type IdOutput struct {
103         ID              string
104         PublicKey       string
105         Addresses       []string
106         AgentVersion    string
107         ProtocolVersion string
108 }
109
110 // ID gets information about a given peer.  Arguments:
111 //
112 // peer: peer.ID of the node to look up.  If no peer is specified,
113 //   return information about the local peer.
114 func (s *Shell) ID(peer ...string) (*IdOutput, error) {
115         if len(peer) > 1 {
116                 return nil, fmt.Errorf("Too many peer arguments")
117         }
118
119         var out IdOutput
120         if err := s.Request("id", peer...).Exec(context.Background(), &out); err != nil {
121                 return nil, err
122         }
123         return &out, nil
124 }
125
126 // Cat the content at the given path. Callers need to drain and close the returned reader after usage.
127 func (s *Shell) Cat(path string) (io.ReadCloser, error) {
128         resp, err := s.Request("cat", path).Send(context.Background())
129         if err != nil {
130                 return nil, err
131         }
132         if resp.Error != nil {
133                 return nil, resp.Error
134         }
135
136         return resp.Output, nil
137 }
138
139 const (
140         TRaw = iota
141         TDirectory
142         TFile
143         TMetadata
144         TSymlink
145 )
146
147 // List entries at the given path
148 func (s *Shell) List(path string) ([]*LsLink, error) {
149         var out struct{ Objects []LsObject }
150         err := s.Request("ls", path).Exec(context.Background(), &out)
151         if err != nil {
152                 return nil, err
153         }
154         if len(out.Objects) != 1 {
155                 return nil, errors.New("bad response from server")
156         }
157         return out.Objects[0].Links, nil
158 }
159
160 type LsLink struct {
161         Hash string
162         Name string
163         Size uint64
164         Type int
165 }
166
167 type LsObject struct {
168         Links []*LsLink
169         LsLink
170 }
171
172 // Pin the given path
173 func (s *Shell) Pin(path string) error {
174         return s.Request("pin/add", path).
175                 Option("recursive", true).
176                 Exec(context.Background(), nil)
177 }
178
179 // Unpin the given path
180 func (s *Shell) Unpin(path string) error {
181         return s.Request("pin/rm", path).
182                 Option("recursive", true).
183                 Exec(context.Background(), nil)
184 }
185
186 const (
187         DirectPin    = "direct"
188         RecursivePin = "recursive"
189         IndirectPin  = "indirect"
190 )
191
192 type PinInfo struct {
193         Type string
194 }
195
196 // Pins returns a map of the pin hashes to their info (currently just the
197 // pin type, one of DirectPin, RecursivePin, or IndirectPin. A map is returned
198 // instead of a slice because it is easier to do existence lookup by map key
199 // than unordered array searching. The map is likely to be more useful to a
200 // client than a flat list.
201 func (s *Shell) Pins() (map[string]PinInfo, error) {
202         var raw struct{ Keys map[string]PinInfo }
203         return raw.Keys, s.Request("pin/ls").Exec(context.Background(), &raw)
204 }
205
206 type PeerInfo struct {
207         Addrs []string
208         ID    string
209 }
210
211 func (s *Shell) FindPeer(peer string) (*PeerInfo, error) {
212         var peers struct{ Responses []PeerInfo }
213         err := s.Request("dht/findpeer", peer).Exec(context.Background(), &peers)
214         if err != nil {
215                 return nil, err
216         }
217         if len(peers.Responses) == 0 {
218                 return nil, errors.New("peer not found")
219         }
220         return &peers.Responses[0], nil
221 }
222
223 func (s *Shell) Refs(hash string, recursive bool) (<-chan string, error) {
224         resp, err := s.Request("refs", hash).
225                 Option("recursive", recursive).
226                 Send(context.Background())
227         if err != nil {
228                 return nil, err
229         }
230
231         if resp.Error != nil {
232                 resp.Close()
233                 return nil, resp.Error
234         }
235
236         out := make(chan string)
237         go func() {
238                 defer resp.Close()
239                 var ref struct {
240                         Ref string
241                 }
242                 defer close(out)
243                 dec := json.NewDecoder(resp.Output)
244                 for {
245                         err := dec.Decode(&ref)
246                         if err != nil {
247                                 return
248                         }
249                         if len(ref.Ref) > 0 {
250                                 out <- ref.Ref
251                         }
252                 }
253         }()
254
255         return out, nil
256 }
257
258 func (s *Shell) Patch(root, action string, args ...string) (string, error) {
259         var out object
260         return out.Hash, s.Request("object/patch/"+action, root).
261                 Arguments(args...).
262                 Exec(context.Background(), &out)
263 }
264
265 func (s *Shell) PatchData(root string, set bool, data interface{}) (string, error) {
266         var read io.Reader
267         switch d := data.(type) {
268         case io.Reader:
269                 read = d
270         case []byte:
271                 read = bytes.NewReader(d)
272         case string:
273                 read = strings.NewReader(d)
274         default:
275                 return "", fmt.Errorf("unrecognized type: %#v", data)
276         }
277
278         cmd := "append-data"
279         if set {
280                 cmd = "set-data"
281         }
282
283         fr := files.NewReaderFile(read)
284         slf := files.NewSliceDirectory([]files.DirEntry{files.FileEntry("", fr)})
285         fileReader := files.NewMultiFileReader(slf, true)
286
287         var out object
288         return out.Hash, s.Request("object/patch/"+cmd, root).
289                 Body(fileReader).
290                 Exec(context.Background(), &out)
291 }
292
293 func (s *Shell) PatchLink(root, path, childhash string, create bool) (string, error) {
294         var out object
295         return out.Hash, s.Request("object/patch/add-link", root, path, childhash).
296                 Option("create", create).
297                 Exec(context.Background(), &out)
298 }
299
300 func (s *Shell) Get(hash, outdir string) error {
301         resp, err := s.Request("get", hash).Option("create", true).Send(context.Background())
302         if err != nil {
303                 return err
304         }
305         defer resp.Close()
306
307         if resp.Error != nil {
308                 return resp.Error
309         }
310
311         extractor := &tar.Extractor{Path: outdir}
312         return extractor.Extract(resp.Output)
313 }
314
315 func (s *Shell) NewObject(template string) (string, error) {
316         var out object
317         req := s.Request("object/new")
318         if template != "" {
319                 req.Arguments(template)
320         }
321         return out.Hash, req.Exec(context.Background(), &out)
322 }
323
324 func (s *Shell) ResolvePath(path string) (string, error) {
325         var out struct {
326                 Path string
327         }
328         err := s.Request("resolve", path).Exec(context.Background(), &out)
329         if err != nil {
330                 return "", err
331         }
332
333         return strings.TrimPrefix(out.Path, "/ipfs/"), nil
334 }
335
336 // returns ipfs version and commit sha
337 func (s *Shell) Version() (string, string, error) {
338         ver := struct {
339                 Version string
340                 Commit  string
341         }{}
342
343         if err := s.Request("version").Exec(context.Background(), &ver); err != nil {
344                 return "", "", err
345         }
346         return ver.Version, ver.Commit, nil
347 }
348
349 func (s *Shell) IsUp() bool {
350         _, _, err := s.Version()
351         return err == nil
352 }
353
354 func (s *Shell) BlockStat(path string) (string, int, error) {
355         var inf struct {
356                 Key  string
357                 Size int
358         }
359
360         if err := s.Request("block/stat", path).Exec(context.Background(), &inf); err != nil {
361                 return "", 0, err
362         }
363         return inf.Key, inf.Size, nil
364 }
365
366 func (s *Shell) BlockGet(path string) ([]byte, error) {
367         resp, err := s.Request("block/get", path).Send(context.Background())
368         if err != nil {
369                 return nil, err
370         }
371         defer resp.Close()
372
373         if resp.Error != nil {
374                 return nil, resp.Error
375         }
376
377         return ioutil.ReadAll(resp.Output)
378 }
379
380 func (s *Shell) BlockPut(block []byte, format, mhtype string, mhlen int) (string, error) {
381         var out struct {
382                 Key string
383         }
384
385         fr := files.NewBytesFile(block)
386         slf := files.NewSliceDirectory([]files.DirEntry{files.FileEntry("", fr)})
387         fileReader := files.NewMultiFileReader(slf, true)
388
389         return out.Key, s.Request("block/put").
390                 Option("mhtype", mhtype).
391                 Option("format", format).
392                 Option("mhlen", mhlen).
393                 Body(fileReader).
394                 Exec(context.Background(), &out)
395 }
396
397 type IpfsObject struct {
398         Links []ObjectLink
399         Data  string
400 }
401
402 type ObjectLink struct {
403         Name, Hash string
404         Size       uint64
405 }
406
407 func (s *Shell) ObjectGet(path string) (*IpfsObject, error) {
408         var obj IpfsObject
409         if err := s.Request("object/get", path).Exec(context.Background(), &obj); err != nil {
410                 return nil, err
411         }
412         return &obj, nil
413 }
414
415 func (s *Shell) ObjectPut(obj *IpfsObject) (string, error) {
416         var data bytes.Buffer
417         err := json.NewEncoder(&data).Encode(obj)
418         if err != nil {
419                 return "", err
420         }
421
422         fr := files.NewReaderFile(&data)
423         slf := files.NewSliceDirectory([]files.DirEntry{files.FileEntry("", fr)})
424         fileReader := files.NewMultiFileReader(slf, true)
425
426         var out object
427         return out.Hash, s.Request("object/put").
428                 Body(fileReader).
429                 Exec(context.Background(), &out)
430 }
431
432 func (s *Shell) PubSubSubscribe(topic string) (*PubSubSubscription, error) {
433         // connect
434         resp, err := s.Request("pubsub/sub", topic).Send(context.Background())
435         if err != nil {
436                 return nil, err
437         }
438         return newPubSubSubscription(resp), nil
439 }
440
441 func (s *Shell) PubSubPublish(topic, data string) (err error) {
442         resp, err := s.Request("pubsub/pub", topic, data).Send(context.Background())
443         if err != nil {
444                 return err
445         }
446         defer resp.Close()
447         if resp.Error != nil {
448                 return resp.Error
449         }
450         return nil
451 }
452
453 type ObjectStats struct {
454         Hash           string
455         BlockSize      int
456         CumulativeSize int
457         DataSize       int
458         LinksSize      int
459         NumLinks       int
460 }
461
462 // ObjectStat gets stats for the DAG object named by key. It returns
463 // the stats of the requested Object or an error.
464 func (s *Shell) ObjectStat(key string) (*ObjectStats, error) {
465         var stat ObjectStats
466         err := s.Request("object/stat", key).Exec(context.Background(), &stat)
467         if err != nil {
468                 return nil, err
469         }
470         return &stat, nil
471 }
472
473 // ObjectStat gets stats for the DAG object named by key. It returns
474 // the stats of the requested Object or an error.
475 /*
476 func (s *Shell) StatsBW(ctx context.Context) (*p2pmetrics.Stats, error) {
477         v := &p2pmetrics.Stats{}
478         err := s.Request("stats/bw").Exec(ctx, &v)
479         return v, err
480 }
481 */
482
483 type SwarmStreamInfo struct {
484         Protocol string
485 }
486
487 type SwarmConnInfo struct {
488         Addr    string
489         Peer    string
490         Latency string
491         Muxer   string
492         Streams []SwarmStreamInfo
493 }
494
495 type SwarmConnInfos struct {
496         Peers []SwarmConnInfo
497 }
498
499 // SwarmPeers gets all the swarm peers
500 func (s *Shell) SwarmPeers(ctx context.Context) (*SwarmConnInfos, error) {
501         v := &SwarmConnInfos{}
502         err := s.Request("swarm/peers").Exec(ctx, &v)
503         return v, err
504 }
505
506 type swarmConnection struct {
507         Strings []string
508 }
509
510 // SwarmConnect opens a swarm connection to a specific address.
511 func (s *Shell) SwarmConnect(ctx context.Context, addr ...string) error {
512         var conn *swarmConnection
513         err := s.Request("swarm/connect").
514                 Arguments(addr...).
515                 Exec(ctx, &conn)
516         return err
517 }