fix(gateway): correct breadcrumbs on dnslink site

This commit is contained in:
Marcin Rataj 2020-09-25 22:26:44 +02:00
parent a6c1598612
commit cd1feb3af4
No known key found for this signature in database
GPG Key ID: 222B6784D5A79E42
4 changed files with 44 additions and 15 deletions

View File

@ -379,8 +379,9 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
hash := resolvedPath.Cid().String()
// Storage for gateway URL to be used when linking to other rootIDs. This
// will be blank unless subdomain resolution is being used for this request.
// Gateway root URL to be used when linking to other rootIDs.
// This will be blank unless subdomain or DNSLink resolution is being used
// for this request.
var gwURL string
// Get gateway hostname and build gateway URL.
@ -396,11 +397,15 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
Listing: dirListing,
Size: size,
Path: urlPath,
Breadcrumbs: breadcrumbs(urlPath),
Breadcrumbs: breadcrumbs(urlPath, gwURL),
BackLink: backLink,
Hash: hash,
}
// TODO: remove logging below
// tplDataJSON, _ := json.MarshalIndent(tplData, "", " ")
// fmt.Println(string(tplDataJSON))
err = listingTemplate.Execute(w, tplData)
if err != nil {
internalWebError(w, err)

View File

@ -34,7 +34,7 @@ type breadcrumb struct {
Path string
}
func breadcrumbs(urlPath string) []breadcrumb {
func breadcrumbs(urlPath string, gwRootURL string) []breadcrumb {
var ret []breadcrumb
p, err := ipfspath.ParsePath(urlPath)
@ -42,8 +42,9 @@ func breadcrumbs(urlPath string) []breadcrumb {
// No breadcrumbs, fallback to bare Path in template
return ret
}
segs := p.Segments()
ns := segs[0]
contentRoot := segs[1]
for i, seg := range segs {
if i == 0 {
ret = append(ret, breadcrumb{Name: seg})
@ -55,6 +56,20 @@ func breadcrumbs(urlPath string) []breadcrumb {
}
}
// Drop the /ipns/<fqdn> prefix from breadcrumb Paths when directory listing
// on a DNSLink website (loaded due to Host header in HTTP request).
// Necessary because gwRootURL won't have a public gateway mounted.
if ns == "ipns" && (("//" + contentRoot) == gwRootURL) {
prefix := "/ipns/" + contentRoot
for i, crumb := range ret {
if strings.HasPrefix(crumb.Path, prefix) {
ret[i].Path = strings.Replace(crumb.Path, prefix, "", 1)
}
}
// Make contentRoot breadcrumb link to the website root
ret[1].Path = "/"
}
return ret
}

View File

@ -391,6 +391,8 @@ func TestIPNSHostnameRedirect(t *testing.T) {
}
}
// Test directory listing on DNSLink website
// (scenario when Host header is the same as URL hostname)
func TestIPNSHostnameBacklinks(t *testing.T) {
ns := mockNamesys{}
ts, api, ctx := newTestServerAndNode(t, ns)
@ -445,7 +447,7 @@ func TestIPNSHostnameBacklinks(t *testing.T) {
s := string(body)
t.Logf("body: %s\n", string(body))
if !matchPathOrBreadcrumbs(s, "/ipns/<a href=\"/ipns/example.net\">example.net</a>/<a href=\"/ipns/example.net/foo%3F%20%23%3C%27\">foo? #&lt;&#39;</a>") {
if !matchPathOrBreadcrumbs(s, "/ipns/<a href=\"//example.net/\">example.net</a>/<a href=\"//example.net/foo%3F%20%23%3C%27\">foo? #&lt;&#39;</a>") {
t.Fatalf("expected a path in directory listing")
}
if !strings.Contains(s, "<a href=\"/foo%3F%20%23%3C%27/./..\">") {
@ -511,7 +513,7 @@ func TestIPNSHostnameBacklinks(t *testing.T) {
s = string(body)
t.Logf("body: %s\n", string(body))
if !matchPathOrBreadcrumbs(s, "/ipns/<a href=\"/ipns/example.net\">example.net</a>/<a href=\"/ipns/example.net/foo%3F%20%23%3C%27\">foo? #&lt;&#39;</a>/<a href=\"/ipns/example.net/foo%3F%20%23%3C%27/bar\">bar</a>") {
if !matchPathOrBreadcrumbs(s, "/ipns/<a href=\"//example.net/\">example.net</a>/<a href=\"//example.net/foo%3F%20%23%3C%27\">foo? #&lt;&#39;</a>/<a href=\"//example.net/foo%3F%20%23%3C%27/bar\">bar</a>") {
t.Fatalf("expected a path in directory listing")
}
if !strings.Contains(s, "<a href=\"/foo%3F%20%23%3C%27/bar/./..\">") {
@ -545,7 +547,7 @@ func TestIPNSHostnameBacklinks(t *testing.T) {
s = string(body)
t.Logf("body: %s\n", string(body))
if !matchPathOrBreadcrumbs(s, "/ipns/<a href=\"/ipns/example.net\">example.net</a>") {
if !matchPathOrBreadcrumbs(s, "/ipns/<a href=\"//example.net/\">example.net</a>") {
t.Fatalf("expected a path in directory listing")
}
if !strings.Contains(s, "<a href=\"/good-prefix/\">") {

View File

@ -129,7 +129,7 @@ func HostnameOption() ServeOption {
if !gw.NoDNSLink && isDNSLinkRequest(r.Context(), coreAPI, host) {
// rewrite path and handle as DNSLink
r.URL.Path = "/ipns/" + stripPort(host) + r.URL.Path
childMux.ServeHTTP(w, r)
childMux.ServeHTTP(w, withHostnameContext(r, host))
return
}
@ -143,10 +143,6 @@ func HostnameOption() ServeOption {
if gw, hostname, ns, rootID, ok := knownSubdomainDetails(host, knownGateways); ok {
// Looks like we're using a known gateway in subdomain mode.
// Add gateway hostname context for linking to other root ids.
// Example: localhost/ipfs/{cid}
ctx := context.WithValue(r.Context(), "gw-hostname", hostname)
// Assemble original path prefix.
pathPrefix := "/" + ns + "/" + rootID
@ -201,7 +197,7 @@ func HostnameOption() ServeOption {
r.URL.Path = pathPrefix + r.URL.Path
// Serve path request
childMux.ServeHTTP(w, r.WithContext(ctx))
childMux.ServeHTTP(w, withHostnameContext(r, hostname))
return
}
// We don't have a known gateway. Fallback on DNSLink lookup
@ -213,7 +209,7 @@ func HostnameOption() ServeOption {
if !cfg.Gateway.NoDNSLink && isDNSLinkRequest(r.Context(), coreAPI, host) {
// rewrite path and handle as DNSLink
r.URL.Path = "/ipns/" + stripPort(host) + r.URL.Path
childMux.ServeHTTP(w, r)
childMux.ServeHTTP(w, withHostnameContext(r, host))
return
}
@ -234,6 +230,17 @@ type wildcardHost struct {
spec *config.GatewaySpec
}
// Extends request context to include hostname of a canonical gateway root
// (subdomain root or dnslink fqdn)
func withHostnameContext(r *http.Request, hostname string) *http.Request {
// This is required for links on directory listing pages to work correctly
// on subdomain and dnslink gateways. While DNSlink could read value from
// Host header, subdomain gateways have more comples rules (knownSubdomainDetails)
// More: https://github.com/ipfs/dir-index-html/issues/42
ctx := context.WithValue(r.Context(), "gw-hostname", hostname)
return r.WithContext(ctx)
}
func prepareKnownGateways(publicGateways map[string]*config.GatewaySpec) gatewayHosts {
var hosts gatewayHosts