-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
improved gateway directory listing for sharded nodes #3897
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ import ( | |
"fmt" | ||
"io" | ||
"net/http" | ||
"os" | ||
gopath "path" | ||
"runtime/debug" | ||
"strings" | ||
|
@@ -20,6 +21,7 @@ import ( | |
dagutils "github.com/ipfs/go-ipfs/merkledag/utils" | ||
path "github.com/ipfs/go-ipfs/path" | ||
ft "github.com/ipfs/go-ipfs/unixfs" | ||
uio "github.com/ipfs/go-ipfs/unixfs/io" | ||
|
||
humanize "gx/ipfs/QmPSBJL4momYnE7DcUyk2DVhD6rH488ZmHBGLbxNdhU44K/go-humanize" | ||
cid "gx/ipfs/QmYhQaCYEcaPPjxJX7YcPcVKkQfRy6sJ7B3XmGFk82XYdQ/go-cid" | ||
|
@@ -227,92 +229,102 @@ func (i *gatewayHandler) getOrHeadHandler(ctx context.Context, w http.ResponseWr | |
return | ||
} | ||
|
||
links, err := i.api.Unixfs().Ls(ctx, resolvedPath) | ||
nd, err := i.api.ResolveNode(ctx, resolvedPath) | ||
if err != nil { | ||
internalWebError(w, err) | ||
return | ||
} | ||
|
||
// storage for directory listing | ||
var dirListing []directoryItem | ||
// loop through files | ||
foundIndex := false | ||
for _, link := range links { | ||
if link.Name == "index.html" { | ||
log.Debugf("found index.html link for %s", urlPath) | ||
foundIndex = true | ||
|
||
if urlPath[len(urlPath)-1] != '/' { | ||
// See comment above where originalUrlPath is declared. | ||
http.Redirect(w, r, originalUrlPath+"/", 302) | ||
log.Debugf("redirect to %s", originalUrlPath+"/") | ||
return | ||
} | ||
dirr, err := uio.NewDirectoryFromNode(i.node.DAG, nd) | ||
if err != nil { | ||
internalWebError(w, err) | ||
return | ||
} | ||
|
||
dr, err := i.api.Unixfs().Cat(ctx, coreapi.ParseCid(link.Cid)) | ||
if err != nil { | ||
internalWebError(w, err) | ||
return | ||
} | ||
defer dr.Close() | ||
ixnd, err := dirr.Find(ctx, "index.html") | ||
switch { | ||
case err == nil: | ||
log.Debugf("found index.html link for %s", urlPath) | ||
|
||
if urlPath[len(urlPath)-1] != '/' { | ||
// See comment above where originalUrlPath is declared. | ||
http.Redirect(w, r, originalUrlPath+"/", 302) | ||
log.Debugf("redirect to %s", originalUrlPath+"/") | ||
return | ||
} | ||
|
||
// write to request | ||
http.ServeContent(w, r, "index.html", modtime, dr) | ||
break | ||
dr, err := i.api.Unixfs().Cat(ctx, coreapi.ParseCid(ixnd.Cid())) | ||
if err != nil { | ||
internalWebError(w, err) | ||
return | ||
} | ||
defer dr.Close() | ||
|
||
// write to request | ||
http.ServeContent(w, r, "index.html", modtime, dr) | ||
return | ||
default: | ||
internalWebError(w, err) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what the official go style is here, but I would put the default as the last case, although I have seen it as the first case in go code, just don't put in the middle. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this case it isn't about keyword but its meaning. In this case |
||
return | ||
case os.IsNotExist(err): | ||
} | ||
|
||
if r.Method == "HEAD" { | ||
return | ||
} | ||
|
||
// storage for directory listing | ||
var dirListing []directoryItem | ||
dirr.ForEachLink(ctx, func(link *node.Link) error { | ||
// See comment above where originalUrlPath is declared. | ||
di := directoryItem{humanize.Bytes(link.Size), link.Name, gopath.Join(originalUrlPath, link.Name)} | ||
dirListing = append(dirListing, di) | ||
} | ||
return nil | ||
}) | ||
|
||
if !foundIndex { | ||
if r.Method != "HEAD" { | ||
// construct the correct back link | ||
// https://github.com/ipfs/go-ipfs/issues/1365 | ||
var backLink string = prefix + urlPath | ||
|
||
// don't go further up than /ipfs/$hash/ | ||
pathSplit := path.SplitList(backLink) | ||
switch { | ||
// keep backlink | ||
case len(pathSplit) == 3: // url: /ipfs/$hash | ||
|
||
// keep backlink | ||
case len(pathSplit) == 4 && pathSplit[3] == "": // url: /ipfs/$hash/ | ||
|
||
// add the correct link depending on wether the path ends with a slash | ||
default: | ||
if strings.HasSuffix(backLink, "/") { | ||
backLink += "./.." | ||
} else { | ||
backLink += "/.." | ||
} | ||
} | ||
// construct the correct back link | ||
// https://github.com/ipfs/go-ipfs/issues/1365 | ||
var backLink string = prefix + urlPath | ||
|
||
// strip /ipfs/$hash from backlink if IPNSHostnameOption touched the path. | ||
if ipnsHostname { | ||
backLink = prefix + "/" | ||
if len(pathSplit) > 5 { | ||
// also strip the trailing segment, because it's a backlink | ||
backLinkParts := pathSplit[3 : len(pathSplit)-2] | ||
backLink += path.Join(backLinkParts) + "/" | ||
} | ||
} | ||
// don't go further up than /ipfs/$hash/ | ||
pathSplit := path.SplitList(backLink) | ||
switch { | ||
// keep backlink | ||
case len(pathSplit) == 3: // url: /ipfs/$hash | ||
|
||
// See comment above where originalUrlPath is declared. | ||
tplData := listingTemplateData{ | ||
Listing: dirListing, | ||
Path: originalUrlPath, | ||
BackLink: backLink, | ||
} | ||
err := listingTemplate.Execute(w, tplData) | ||
if err != nil { | ||
internalWebError(w, err) | ||
return | ||
} | ||
// keep backlink | ||
case len(pathSplit) == 4 && pathSplit[3] == "": // url: /ipfs/$hash/ | ||
|
||
// add the correct link depending on wether the path ends with a slash | ||
default: | ||
if strings.HasSuffix(backLink, "/") { | ||
backLink += "./.." | ||
} else { | ||
backLink += "/.." | ||
} | ||
} | ||
|
||
// strip /ipfs/$hash from backlink if IPNSHostnameOption touched the path. | ||
if ipnsHostname { | ||
backLink = prefix + "/" | ||
if len(pathSplit) > 5 { | ||
// also strip the trailing segment, because it's a backlink | ||
backLinkParts := pathSplit[3 : len(pathSplit)-2] | ||
backLink += path.Join(backLinkParts) + "/" | ||
} | ||
} | ||
|
||
// See comment above where originalUrlPath is declared. | ||
tplData := listingTemplateData{ | ||
Listing: dirListing, | ||
Path: originalUrlPath, | ||
BackLink: backLink, | ||
} | ||
err = listingTemplate.Execute(w, tplData) | ||
if err != nil { | ||
internalWebError(w, err) | ||
return | ||
} | ||
} | ||
|
||
func (i *gatewayHandler) postHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we get the whole node here, we can meld this call and
i.api.Unixfs().Cat(ctx, resolvedPath)
further above into one