Skip to content
This repository was archived by the owner on Oct 5, 2023. It is now read-only.

Commit b31bee0

Browse files
committed
Implement Unixfs.Get()
1 parent 634b00b commit b31bee0

File tree

2 files changed

+231
-4
lines changed

2 files changed

+231
-4
lines changed

apifile.go

+231
Original file line numberDiff line numberDiff line change
@@ -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{}

unixfs.go

-4
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,6 @@ func (api *UnixfsAPI) Add(ctx context.Context, f files.Node, opts ...caopts.Unix
8585
return iface.IpfsPath(c), nil
8686
}
8787

88-
func (api *UnixfsAPI) Get(context.Context, iface.Path) (files.Node, error) {
89-
panic("implement me")
90-
}
91-
9288
type lsLink struct {
9389
Name, Hash string
9490
Size uint64

0 commit comments

Comments
 (0)