@cryptotaxi247 / kubo / commits / 021ef4341

gateway: add path prefix for directory listings

License: MIT Signed-off-by: Lars Gierth <larsg@systemli.org>

Lars Gierth committed Nov 16, 2015 at 07:00 UTC 021ef4341854cb364d8f23649427efc38087dd4f
3 files changed +72 -9
core/corehttp/gateway_handler.go
+14 -6
@@ -92,16 +92,24 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
92
93 urlPath := r.URL.Path
94
95 + // If the gateway is behind a reverse proxy and mounted at a sub-path,
96 + // the prefix header can be set to signal this sub-path.
97 + // It will be prepended to links in directory listings and the index.html redirect.
98 + prefix := ""
99 + if prefixHdr := r.Header["X-Ipfs-Gateway-Prefix"]; len(prefixHdr) > 0 {
100 + log.Debugf("X-Ipfs-Gateway-Prefix: %s", prefixHdr[0])
101 + prefix = prefixHdr[0]
102 + }
103 +
104 // IPNSHostnameOption might have constructed an IPNS path using the Host header.
105 // In this case, we need the original path for constructing redirects
106 // and links that match the requested URL.
107 // For example, http://example.net would become /ipns/example.net, and
108 // the redirects and links would end up as http://example.net/ipns/example.net
100 - originalUrlPath := urlPath
109 + originalUrlPath := prefix + urlPath
110 ipnsHostname := false
102 - hdr := r.Header["X-IPNS-Original-Path"]
103 - if len(hdr) > 0 {
104 - originalUrlPath = hdr[0]
111 + if hdr := r.Header["X-Ipns-Original-Path"]; len(hdr) > 0 {
112 + originalUrlPath = prefix + hdr[0]
113 ipnsHostname = true
114 }
115
@@ -211,7 +219,7 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
219 if r.Method != "HEAD" {
220 // construct the correct back link
221 // https://github.com/ipfs/go-ipfs/issues/1365
214 - var backLink string = urlPath
222 + var backLink string = prefix + urlPath
223
224 // don't go further up than /ipfs/$hash/
225 pathSplit := strings.Split(backLink, "/")
@@ -233,7 +241,7 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
241
242 // strip /ipfs/$hash from backlink if IPNSHostnameOption touched the path.
243 if ipnsHostname {
236 - backLink = "/"
244 + backLink = prefix + "/"
245 if len(pathSplit) > 5 {
246 // also strip the trailing segment, because it's a backlink
247 backLinkParts := pathSplit[3 : len(pathSplit)-2]
core/corehttp/gateway_test.go
+57 -2
@@ -213,6 +213,30 @@ func TestIPNSHostnameRedirect(t *testing.T) {
213 } else if hdr[0] != "/foo/" {
214 t.Errorf("location header is %v, expected /foo/", hdr[0])
215 }
216 +
217 + // make request with prefix to directory containing index.html
218 + req, err = http.NewRequest("GET", ts.URL+"/foo", nil)
219 + if err != nil {
220 + t.Fatal(err)
221 + }
222 + req.Host = "example.net"
223 + req.Header.Set("X-Ipfs-Gateway-Prefix", "/prefix")
224 +
225 + res, err = doWithoutRedirect(req)
226 + if err != nil {
227 + t.Fatal(err)
228 + }
229 +
230 + // expect 302 redirect to same path, but with prefix and trailing slash
231 + if res.StatusCode != 302 {
232 + t.Errorf("status is %d, expected 302", res.StatusCode)
233 + }
234 + hdr = res.Header["Location"]
235 + if len(hdr) < 1 {
236 + t.Errorf("location header not present")
237 + } else if hdr[0] != "/prefix/foo/" {
238 + t.Errorf("location header is %v, expected /prefix/foo/", hdr[0])
239 + }
240 }
241
242 func TestIPNSHostnameBacklinks(t *testing.T) {
@@ -282,7 +306,7 @@ func TestIPNSHostnameBacklinks(t *testing.T) {
306 t.Fatalf("expected file in directory listing")
307 }
308
285 - // make request to directory listing
309 + // make request to directory listing at root
310 req, err = http.NewRequest("GET", ts.URL, nil)
311 if err != nil {
312 t.Fatal(err)
@@ -294,7 +318,7 @@ func TestIPNSHostnameBacklinks(t *testing.T) {
318 t.Fatal(err)
319 }
320
297 - // expect correct backlinks
321 + // expect correct backlinks at root
322 body, err = ioutil.ReadAll(res.Body)
323 if err != nil {
324 t.Fatalf("error reading response: %s", err)
@@ -341,4 +365,35 @@ func TestIPNSHostnameBacklinks(t *testing.T) {
365 if !strings.Contains(s, "<a href=\"/foo/bar/file.txt\">") {
366 t.Fatalf("expected file in directory listing")
367 }
368 +
369 + // make request to directory listing with prefix
370 + req, err = http.NewRequest("GET", ts.URL, nil)
371 + if err != nil {
372 + t.Fatal(err)
373 + }
374 + req.Host = "example.net"
375 + req.Header.Set("X-Ipfs-Gateway-Prefix", "/prefix")
376 +
377 + res, err = doWithoutRedirect(req)
378 + if err != nil {
379 + t.Fatal(err)
380 + }
381 +
382 + // expect correct backlinks with prefix
383 + body, err = ioutil.ReadAll(res.Body)
384 + if err != nil {
385 + t.Fatalf("error reading response: %s", err)
386 + }
387 + s = string(body)
388 + t.Logf("body: %s\n", string(body))
389 +
390 + if !strings.Contains(s, "Index of /prefix") {
391 + t.Fatalf("expected a path in directory listing")
392 + }
393 + if !strings.Contains(s, "<a href=\"/prefix/\">") {
394 + t.Fatalf("expected backlink in directory listing")
395 + }
396 + if !strings.Contains(s, "<a href=\"/prefix/file.txt\">") {
397 + t.Fatalf("expected file in directory listing")
398 + }
399 }
core/corehttp/ipns_hostname.go
+1 -1
@@ -24,7 +24,7 @@ func IPNSHostnameOption() ServeOption {
24 if len(host) > 0 && isd.IsDomain(host) {
25 name := "/ipns/" + host
26 if _, err := n.Namesys.Resolve(ctx, name); err == nil {
27 - r.Header["X-IPNS-Original-Path"] = []string{r.URL.Path}
27 + r.Header["X-Ipns-Original-Path"] = []string{r.URL.Path}
28 r.URL.Path = name + r.URL.Path
29 }
30 }