Skip to content

Commit ec8d9eb

Browse files
committed
improved gateway directory listing for sharded nodes
License: MIT Signed-off-by: Jeromy <[email protected]>
1 parent d59554a commit ec8d9eb

File tree

2 files changed

+113
-96
lines changed

2 files changed

+113
-96
lines changed

core/commands/files/files.go

+32-27
Original file line numberDiff line numberDiff line change
@@ -162,38 +162,43 @@ func statNode(ds dag.DAGService, fsn mfs.FSNode) (*Object, error) {
162162

163163
c := nd.Cid()
164164

165-
pbnd, ok := nd.(*dag.ProtoNode)
166-
if !ok {
167-
return nil, dag.ErrNotProtobuf
168-
}
165+
var out Object
166+
out.Hash = c.String()
169167

170-
d, err := ft.FromBytes(pbnd.Data())
171-
if err != nil {
172-
return nil, err
173-
}
168+
switch nd := nd.(type) {
169+
case *dag.ProtoNode:
174170

175-
cumulsize, err := nd.Size()
176-
if err != nil {
177-
return nil, err
178-
}
171+
d, err := ft.FromBytes(nd.Data())
172+
if err != nil {
173+
return nil, err
174+
}
175+
176+
cumulsize, err := nd.Size()
177+
if err != nil {
178+
return nil, err
179+
}
179180

180-
var ndtype string
181-
switch fsn.Type() {
182-
case mfs.TDir:
183-
ndtype = "directory"
184-
case mfs.TFile:
185-
ndtype = "file"
181+
switch fsn.Type() {
182+
case mfs.TDir:
183+
out.Type = "directory"
184+
case mfs.TFile:
185+
out.Type = "file"
186+
default:
187+
return nil, fmt.Errorf("Unrecognized node type: %s", fsn.Type())
188+
}
189+
out.Blocks = len(nd.Links())
190+
out.Size = d.GetFilesize()
191+
out.CumulativeSize = cumulsize
192+
193+
case *dag.RawNode:
194+
out.Type = "file"
195+
out.Blocks = 1
196+
out.Size = uint64(len(nd.Block.RawData()))
197+
out.CumulativeSize = out.Size
186198
default:
187-
return nil, fmt.Errorf("Unrecognized node type: %s", fsn.Type())
199+
return nil, fmt.Errorf("unrecognized node type: %T", fsn.Type())
188200
}
189-
190-
return &Object{
191-
Hash: c.String(),
192-
Blocks: len(nd.Links()),
193-
Size: d.GetFilesize(),
194-
CumulativeSize: cumulsize,
195-
Type: ndtype,
196-
}, nil
201+
return &out, nil
197202
}
198203

199204
var FilesCpCmd = &cmds.Command{

core/corehttp/gateway_handler.go

+81-69
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"io"
88
"net/http"
9+
"os"
910
gopath "path"
1011
"runtime/debug"
1112
"strings"
@@ -20,6 +21,7 @@ import (
2021
dagutils "github.com/ipfs/go-ipfs/merkledag/utils"
2122
path "github.com/ipfs/go-ipfs/path"
2223
ft "github.com/ipfs/go-ipfs/unixfs"
24+
uio "github.com/ipfs/go-ipfs/unixfs/io"
2325

2426
humanize "gx/ipfs/QmPSBJL4momYnE7DcUyk2DVhD6rH488ZmHBGLbxNdhU44K/go-humanize"
2527
cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid"
@@ -227,92 +229,102 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr
227229
return
228230
}
229231

230-
links, err := i.api.Unixfs().Ls(ctx, resolvedPath)
232+
nd, err := i.api.ResolveNode(ctx, resolvedPath)
231233
if err != nil {
232234
internalWebError(w, err)
233235
return
234236
}
235237

236-
// storage for directory listing
237-
var dirListing []directoryItem
238-
// loop through files
239-
foundIndex := false
240-
for _, link := range links {
241-
if link.Name == "index.html" {
242-
log.Debugf("found index.html link for %s", urlPath)
243-
foundIndex = true
244-
245-
if urlPath[len(urlPath)-1] != '/' {
246-
// See comment above where originalUrlPath is declared.
247-
http.Redirect(w, r, originalUrlPath+"/", 302)
248-
log.Debugf("redirect to %s", originalUrlPath+"/")
249-
return
250-
}
238+
dirr, err := uio.NewDirectoryFromNode(i.node.DAG, nd)
239+
if err != nil {
240+
internalWebError(w, err)
241+
return
242+
}
251243

252-
dr, err := i.api.Unixfs().Cat(ctx, coreapi.ParseCid(link.Cid))
253-
if err != nil {
254-
internalWebError(w, err)
255-
return
256-
}
257-
defer dr.Close()
244+
ixnd, err := dirr.Find(ctx, "index.html")
245+
switch {
246+
case err == nil:
247+
log.Debugf("found index.html link for %s", urlPath)
248+
249+
if urlPath[len(urlPath)-1] != '/' {
250+
// See comment above where originalUrlPath is declared.
251+
http.Redirect(w, r, originalUrlPath+"/", 302)
252+
log.Debugf("redirect to %s", originalUrlPath+"/")
253+
return
254+
}
258255

259-
// write to request
260-
http.ServeContent(w, r, "index.html", modtime, dr)
261-
break
256+
dr, err := i.api.Unixfs().Cat(ctx, coreapi.ParseCid(ixnd.Cid()))
257+
if err != nil {
258+
internalWebError(w, err)
259+
return
262260
}
261+
defer dr.Close()
263262

263+
// write to request
264+
http.ServeContent(w, r, "index.html", modtime, dr)
265+
return
266+
default:
267+
internalWebError(w, err)
268+
return
269+
case os.IsNotExist(err):
270+
}
271+
272+
if r.Method == "HEAD" {
273+
return
274+
}
275+
276+
// storage for directory listing
277+
var dirListing []directoryItem
278+
dirr.ForEachLink(ctx, func(link *node.Link) error {
264279
// See comment above where originalUrlPath is declared.
265280
di := directoryItem{humanize.Bytes(link.Size), link.Name, gopath.Join(originalUrlPath, link.Name)}
266281
dirListing = append(dirListing, di)
267-
}
282+
return nil
283+
})
268284

269-
if !foundIndex {
270-
if r.Method != "HEAD" {
271-
// construct the correct back link
272-
// https://github.com/ipfs/go-ipfs/issues/1365
273-
var backLink string = prefix + urlPath
274-
275-
// don't go further up than /ipfs/$hash/
276-
pathSplit := path.SplitList(backLink)
277-
switch {
278-
// keep backlink
279-
case len(pathSplit) == 3: // url: /ipfs/$hash
280-
281-
// keep backlink
282-
case len(pathSplit) == 4 && pathSplit[3] == "": // url: /ipfs/$hash/
283-
284-
// add the correct link depending on wether the path ends with a slash
285-
default:
286-
if strings.HasSuffix(backLink, "/") {
287-
backLink += "./.."
288-
} else {
289-
backLink += "/.."
290-
}
291-
}
285+
// construct the correct back link
286+
// https://github.com/ipfs/go-ipfs/issues/1365
287+
var backLink string = prefix + urlPath
292288

293-
// strip /ipfs/$hash from backlink if IPNSHostnameOption touched the path.
294-
if ipnsHostname {
295-
backLink = prefix + "/"
296-
if len(pathSplit) > 5 {
297-
// also strip the trailing segment, because it's a backlink
298-
backLinkParts := pathSplit[3 : len(pathSplit)-2]
299-
backLink += path.Join(backLinkParts) + "/"
300-
}
301-
}
289+
// don't go further up than /ipfs/$hash/
290+
pathSplit := path.SplitList(backLink)
291+
switch {
292+
// keep backlink
293+
case len(pathSplit) == 3: // url: /ipfs/$hash
302294

303-
// See comment above where originalUrlPath is declared.
304-
tplData := listingTemplateData{
305-
Listing: dirListing,
306-
Path: originalUrlPath,
307-
BackLink: backLink,
308-
}
309-
err := listingTemplate.Execute(w, tplData)
310-
if err != nil {
311-
internalWebError(w, err)
312-
return
313-
}
295+
// keep backlink
296+
case len(pathSplit) == 4 && pathSplit[3] == "": // url: /ipfs/$hash/
297+
298+
// add the correct link depending on wether the path ends with a slash
299+
default:
300+
if strings.HasSuffix(backLink, "/") {
301+
backLink += "./.."
302+
} else {
303+
backLink += "/.."
314304
}
315305
}
306+
307+
// strip /ipfs/$hash from backlink if IPNSHostnameOption touched the path.
308+
if ipnsHostname {
309+
backLink = prefix + "/"
310+
if len(pathSplit) > 5 {
311+
// also strip the trailing segment, because it's a backlink
312+
backLinkParts := pathSplit[3 : len(pathSplit)-2]
313+
backLink += path.Join(backLinkParts) + "/"
314+
}
315+
}
316+
317+
// See comment above where originalUrlPath is declared.
318+
tplData := listingTemplateData{
319+
Listing: dirListing,
320+
Path: originalUrlPath,
321+
BackLink: backLink,
322+
}
323+
err = listingTemplate.Execute(w, tplData)
324+
if err != nil {
325+
internalWebError(w, err)
326+
return
327+
}
316328
}
317329

318330
func (i *gatewayHandler) postHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {

0 commit comments

Comments
 (0)