@cryptotaxi247 / kubo / commits / cd1feb3af

fix(gateway): correct breadcrumbs on dnslink site

Marcin Rataj committed Sep 25, 2020 at 22:26 UTC cd1feb3af4e42a835870aca96243b55760ab5f04
4 files changed +44 -15
core/corehttp/gateway_handler.go
+8 -3
@@ -379,8 +379,9 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
379
380 hash := resolvedPath.Cid().String()
381
382 - // Storage for gateway URL to be used when linking to other rootIDs. This
383 - // will be blank unless subdomain resolution is being used for this request.
382 + // Gateway root URL to be used when linking to other rootIDs.
383 + // This will be blank unless subdomain or DNSLink resolution is being used
384 + // for this request.
385 var gwURL string
386
387 // Get gateway hostname and build gateway URL.
@@ -396,11 +397,15 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
397 Listing: dirListing,
398 Size: size,
399 Path: urlPath,
399 - Breadcrumbs: breadcrumbs(urlPath),
400 + Breadcrumbs: breadcrumbs(urlPath, gwURL),
401 BackLink: backLink,
402 Hash: hash,
403 }
404
405 + // TODO: remove logging below
406 + // tplDataJSON, _ := json.MarshalIndent(tplData, "", " ")
407 + // fmt.Println(string(tplDataJSON))
408 +
409 err = listingTemplate.Execute(w, tplData)
410 if err != nil {
411 internalWebError(w, err)
core/corehttp/gateway_indexPage.go
+17 -2
@@ -34,7 +34,7 @@ type breadcrumb struct {
34 Path string
35 }
36
37 -func breadcrumbs(urlPath string) []breadcrumb {
37 +func breadcrumbs(urlPath string, gwRootURL string) []breadcrumb {
38 var ret []breadcrumb
39
40 p, err := ipfspath.ParsePath(urlPath)
@@ -42,8 +42,9 @@ func breadcrumbs(urlPath string) []breadcrumb {
42 // No breadcrumbs, fallback to bare Path in template
43 return ret
44 }
45 -
45 segs := p.Segments()
46 + ns := segs[0]
47 + contentRoot := segs[1]
48 for i, seg := range segs {
49 if i == 0 {
50 ret = append(ret, breadcrumb{Name: seg})
@@ -55,6 +56,20 @@ func breadcrumbs(urlPath string) []breadcrumb {
56 }
57 }
58
59 + // Drop the /ipns/<fqdn> prefix from breadcrumb Paths when directory listing
60 + // on a DNSLink website (loaded due to Host header in HTTP request).
61 + // Necessary because gwRootURL won't have a public gateway mounted.
62 + if ns == "ipns" && (("//" + contentRoot) == gwRootURL) {
63 + prefix := "/ipns/" + contentRoot
64 + for i, crumb := range ret {
65 + if strings.HasPrefix(crumb.Path, prefix) {
66 + ret[i].Path = strings.Replace(crumb.Path, prefix, "", 1)
67 + }
68 + }
69 + // Make contentRoot breadcrumb link to the website root
70 + ret[1].Path = "/"
71 + }
72 +
73 return ret
74 }
75
core/corehttp/gateway_test.go
+5 -3
@@ -391,6 +391,8 @@ func TestIPNSHostnameRedirect(t *testing.T) {
391 }
392 }
393
394 +// Test directory listing on DNSLink website
395 +// (scenario when Host header is the same as URL hostname)
396 func TestIPNSHostnameBacklinks(t *testing.T) {
397 ns := mockNamesys{}
398 ts, api, ctx := newTestServerAndNode(t, ns)
@@ -445,7 +447,7 @@ func TestIPNSHostnameBacklinks(t *testing.T) {
447 s := string(body)
448 t.Logf("body: %s\n", string(body))
449
448 - 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>") {
450 + 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>") {
451 t.Fatalf("expected a path in directory listing")
452 }
453 if !strings.Contains(s, "<a href=\"/foo%3F%20%23%3C%27/./..\">") {
@@ -511,7 +513,7 @@ func TestIPNSHostnameBacklinks(t *testing.T) {
513 s = string(body)
514 t.Logf("body: %s\n", string(body))
515
514 - 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>") {
516 + 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>") {
517 t.Fatalf("expected a path in directory listing")
518 }
519 if !strings.Contains(s, "<a href=\"/foo%3F%20%23%3C%27/bar/./..\">") {
@@ -545,7 +547,7 @@ func TestIPNSHostnameBacklinks(t *testing.T) {
547 s = string(body)
548 t.Logf("body: %s\n", string(body))
549
548 - if !matchPathOrBreadcrumbs(s, "/ipns/<a href=\"/ipns/example.net\">example.net</a>") {
550 + if !matchPathOrBreadcrumbs(s, "/ipns/<a href=\"//example.net/\">example.net</a>") {
551 t.Fatalf("expected a path in directory listing")
552 }
553 if !strings.Contains(s, "<a href=\"/good-prefix/\">") {
core/corehttp/hostname.go
+14 -7
@@ -129,7 +129,7 @@ func HostnameOption() ServeOption {
129 if !gw.NoDNSLink && isDNSLinkRequest(r.Context(), coreAPI, host) {
130 // rewrite path and handle as DNSLink
131 r.URL.Path = "/ipns/" + stripPort(host) + r.URL.Path
132 - childMux.ServeHTTP(w, r)
132 + childMux.ServeHTTP(w, withHostnameContext(r, host))
133 return
134 }
135
@@ -143,10 +143,6 @@ func HostnameOption() ServeOption {
143 if gw, hostname, ns, rootID, ok := knownSubdomainDetails(host, knownGateways); ok {
144 // Looks like we're using a known gateway in subdomain mode.
145
146 - // Add gateway hostname context for linking to other root ids.
147 - // Example: localhost/ipfs/{cid}
148 - ctx := context.WithValue(r.Context(), "gw-hostname", hostname)
149 -
146 // Assemble original path prefix.
147 pathPrefix := "/" + ns + "/" + rootID
148
@@ -201,7 +197,7 @@ func HostnameOption() ServeOption {
197 r.URL.Path = pathPrefix + r.URL.Path
198
199 // Serve path request
204 - childMux.ServeHTTP(w, r.WithContext(ctx))
200 + childMux.ServeHTTP(w, withHostnameContext(r, hostname))
201 return
202 }
203 // We don't have a known gateway. Fallback on DNSLink lookup
@@ -213,7 +209,7 @@ func HostnameOption() ServeOption {
209 if !cfg.Gateway.NoDNSLink && isDNSLinkRequest(r.Context(), coreAPI, host) {
210 // rewrite path and handle as DNSLink
211 r.URL.Path = "/ipns/" + stripPort(host) + r.URL.Path
216 - childMux.ServeHTTP(w, r)
212 + childMux.ServeHTTP(w, withHostnameContext(r, host))
213 return
214 }
215
@@ -234,6 +230,17 @@ type wildcardHost struct {
230 spec *config.GatewaySpec
231 }
232
233 +// Extends request context to include hostname of a canonical gateway root
234 +// (subdomain root or dnslink fqdn)
235 +func withHostnameContext(r *http.Request, hostname string) *http.Request {
236 + // This is required for links on directory listing pages to work correctly
237 + // on subdomain and dnslink gateways. While DNSlink could read value from
238 + // Host header, subdomain gateways have more comples rules (knownSubdomainDetails)
239 + // More: https://github.com/ipfs/dir-index-html/issues/42
240 + ctx := context.WithValue(r.Context(), "gw-hostname", hostname)
241 + return r.WithContext(ctx)
242 +}
243 +
244 func prepareKnownGateways(publicGateways map[string]*config.GatewaySpec) gatewayHosts {
245 var hosts gatewayHosts
246