@cryptotaxi247 / kubo / commits / 6d9dea628

Implement Unixfs.Get()

This commit was moved from ipfs/go-ipfs-http-client@b31bee083d1fc5002779d755b6a624b517d2b10c

Łukasz Magiera committed Jan 8, 2019 at 17:47 UTC 6d9dea62825012678ab0f117ec3cfe28c96fdaf0
2 files changed +231 -4
client/httpapi/apifile.go new
+231
@@ -0,0 +1,231 @@
1 +package httpapi
2 +
3 +import (
4 + "context"
5 + "encoding/json"
6 + "fmt"
7 + "github.com/ipfs/go-cid"
8 + "io"
9 +
10 + "github.com/ipfs/go-ipfs/core/coreapi/interface"
11 +
12 + "github.com/ipfs/go-ipfs-files"
13 + unixfspb "github.com/ipfs/go-unixfs/pb"
14 +)
15 +
16 +func (api *UnixfsAPI) Get(ctx context.Context, p iface.Path) (files.Node, error) {
17 + if p.Mutable() { // use resolved path in case we are dealing with IPNS / MFS
18 + var err error
19 + p, err = api.core().ResolvePath(ctx, p)
20 + if err != nil {
21 + return nil, err
22 + }
23 + }
24 +
25 + var stat struct{
26 + Hash string
27 + Type string
28 + Size int64 // unixfs size
29 + }
30 + err := api.core().request("files/stat", p.String()). Exec(ctx, &stat)
31 + if err != nil {
32 + return nil, err
33 + }
34 +
35 + switch stat.Type {
36 + case "file":
37 + return api.getFile(ctx, p, stat.Size)
38 + case "directory":
39 + return api.getDir(ctx, p, stat.Size)
40 + default:
41 + return nil, fmt.Errorf("unsupported file type '%s'", stat.Type)
42 + }
43 +}
44 +
45 +type apiFile struct {
46 + ctx context.Context
47 + core *HttpApi
48 + size int64
49 + path iface.Path
50 +
51 + r io.ReadCloser
52 + at int64
53 +}
54 +
55 +func (f *apiFile) reset() error {
56 + if f.r != nil {
57 + f.r.Close()
58 + }
59 + req := f.core.request("cat", f.path.String())
60 + if f.at != 0 {
61 + req.Option("offset", f.at)
62 + }
63 + resp, err := req.Send(f.ctx)
64 + if err != nil {
65 + return err
66 + }
67 + if resp.Error != nil {
68 + return resp.Error
69 + }
70 + f.r = resp.Output
71 + return nil
72 +}
73 +
74 +func (f *apiFile) Read(p []byte) (int, error) {
75 + n, err := f.r.Read(p)
76 + if n > 0 {
77 + f.at += int64(n)
78 + }
79 + return n, err
80 +}
81 +
82 +func (f *apiFile) Seek(offset int64, whence int) (int64, error) {
83 + panic("implement me") //TODO
84 +}
85 +
86 +func (f *apiFile) Close() error {
87 + if f.r != nil {
88 + return f.r.Close()
89 + }
90 + return nil
91 +}
92 +
93 +func (f *apiFile) Size() (int64, error) {
94 + return f.size, nil
95 +}
96 +
97 +func (api *UnixfsAPI) getFile(ctx context.Context, p iface.Path, size int64) (files.Node, error) {
98 + f := &apiFile{
99 + ctx: ctx,
100 + core: api.core(),
101 + size: size,
102 + path: p,
103 + }
104 +
105 + return f, f.reset()
106 +}
107 +
108 +type apiIter struct {
109 + ctx context.Context
110 + core *UnixfsAPI
111 +
112 + err error
113 +
114 + dec *json.Decoder
115 + curFile files.Node
116 + cur lsLink
117 +}
118 +
119 +func (it *apiIter) Err() error {
120 + return it.err
121 +}
122 +
123 +func (it *apiIter) Name() string {
124 + return it.cur.Name
125 +}
126 +
127 +func (it *apiIter) Next() bool {
128 + var out lsOutput
129 + if err := it.dec.Decode(&out); err != nil {
130 + if err != io.EOF {
131 + it.err = err
132 + }
133 + return false
134 + }
135 +
136 + if len(out.Objects) != 1 {
137 + it.err = fmt.Errorf("len(out.Objects) != 1 (is %d)", len(out.Objects))
138 + return false
139 + }
140 +
141 + if len(out.Objects[0].Links) != 1 {
142 + it.err = fmt.Errorf("len(out.Objects[0].Links) != 1 (is %d)", len(out.Objects[0].Links))
143 + return false
144 + }
145 +
146 + it.cur = out.Objects[0].Links[0]
147 + c, err := cid.Parse(it.cur.Hash)
148 + if err != nil {
149 + it.err = err
150 + return false
151 + }
152 +
153 + switch it.cur.Type {
154 + case unixfspb.Data_HAMTShard:
155 + fallthrough
156 + case unixfspb.Data_Metadata:
157 + fallthrough
158 + case unixfspb.Data_Directory:
159 + it.curFile, err = it.core.getDir(it.ctx, iface.IpfsPath(c), int64(it.cur.Size))
160 + if err != nil {
161 + it.err = err
162 + return false
163 + }
164 + case unixfspb.Data_File:
165 + it.curFile, err = it.core.getFile(it.ctx, iface.IpfsPath(c), int64(it.cur.Size))
166 + if err != nil {
167 + it.err = err
168 + return false
169 + }
170 + default:
171 + it.err = fmt.Errorf("file type %d not supported", it.cur.Type)
172 + return false
173 + }
174 + return true
175 +}
176 +
177 +func (it *apiIter) Node() files.Node {
178 + return it.curFile
179 +}
180 +
181 +type apiDir struct {
182 + ctx context.Context
183 + core *UnixfsAPI
184 + size int64
185 + path iface.Path
186 +
187 + dec *json.Decoder
188 +}
189 +
190 +func (d *apiDir) Close() error {
191 + return nil
192 +}
193 +
194 +func (d *apiDir) Size() (int64, error) {
195 + return d.size, nil
196 +}
197 +
198 +func (d *apiDir) Entries() files.DirIterator {
199 + return &apiIter{
200 + ctx: d.ctx,
201 + core: d.core,
202 + dec: d.dec,
203 + }
204 +}
205 +
206 +func (api *UnixfsAPI) getDir(ctx context.Context, p iface.Path, size int64) (files.Node, error) {
207 + resp, err := api.core().request("ls", p.String()).
208 + Option("resolve-size", true).
209 + Option("stream", true).Send(ctx)
210 +
211 + if err != nil {
212 + return nil, err
213 + }
214 + if resp.Error != nil {
215 + return nil, resp.Error
216 + }
217 +
218 + d := &apiDir{
219 + ctx: ctx,
220 + core: api,
221 + size: size,
222 + path: p,
223 +
224 + dec: json.NewDecoder(resp.Output),
225 + }
226 +
227 + return d, nil
228 +}
229 +
230 +var _ files.File = &apiFile{}
231 +var _ files.Directory = &apiDir{}
client/httpapi/unixfs.go
-4
@@ -85,10 +85,6 @@ func (api *UnixfsAPI) Add(ctx context.Context, f files.Node, opts ...caopts.Unix
85 return iface.IpfsPath(c), nil
86 }
87
88 -func (api *UnixfsAPI) Get(context.Context, iface.Path) (files.Node, error) {
89 - panic("implement me")
90 -}
91 -
88 type lsLink struct {
89 Name, Hash string
90 Size uint64