From 021ef4341854cb364d8f23649427efc38087dd4f Mon Sep 17 00:00:00 2001 From: Lars Gierth Date: Mon, 16 Nov 2015 07:00:14 +0100 Subject: [PATCH] gateway: add path prefix for directory listings License: MIT Signed-off-by: Lars Gierth --- core/corehttp/gateway_handler.go | 20 +++++++---- core/corehttp/gateway_test.go | 59 ++++++++++++++++++++++++++++++-- core/corehttp/ipns_hostname.go | 2 +- 3 files changed, 72 insertions(+), 9 deletions(-) diff --git a/core/corehttp/gateway_handler.go b/core/corehttp/gateway_handler.go index d9864c05146..59c57e43773 100644 --- a/core/corehttp/gateway_handler.go +++ b/core/corehttp/gateway_handler.go @@ -92,16 +92,24 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request urlPath := r.URL.Path + // If the gateway is behind a reverse proxy and mounted at a sub-path, + // the prefix header can be set to signal this sub-path. + // It will be prepended to links in directory listings and the index.html redirect. + prefix := "" + if prefixHdr := r.Header["X-Ipfs-Gateway-Prefix"]; len(prefixHdr) > 0 { + log.Debugf("X-Ipfs-Gateway-Prefix: %s", prefixHdr[0]) + prefix = prefixHdr[0] + } + // IPNSHostnameOption might have constructed an IPNS path using the Host header. // In this case, we need the original path for constructing redirects // and links that match the requested URL. // For example, http://example.net would become /ipns/example.net, and // the redirects and links would end up as http://example.net/ipns/example.net - originalUrlPath := urlPath + originalUrlPath := prefix + urlPath ipnsHostname := false - hdr := r.Header["X-IPNS-Original-Path"] - if len(hdr) > 0 { - originalUrlPath = hdr[0] + if hdr := r.Header["X-Ipns-Original-Path"]; len(hdr) > 0 { + originalUrlPath = prefix + hdr[0] ipnsHostname = true } @@ -211,7 +219,7 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request if r.Method != "HEAD" { // construct the correct back link // https://github.com/ipfs/go-ipfs/issues/1365 - var backLink string = urlPath + var backLink string = prefix + urlPath // don't go further up than /ipfs/$hash/ pathSplit := strings.Split(backLink, "/") @@ -233,7 +241,7 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request // strip /ipfs/$hash from backlink if IPNSHostnameOption touched the path. if ipnsHostname { - backLink = "/" + backLink = prefix + "/" if len(pathSplit) > 5 { // also strip the trailing segment, because it's a backlink backLinkParts := pathSplit[3 : len(pathSplit)-2] diff --git a/core/corehttp/gateway_test.go b/core/corehttp/gateway_test.go index 9c258cbaeaa..75b7120e37f 100644 --- a/core/corehttp/gateway_test.go +++ b/core/corehttp/gateway_test.go @@ -213,6 +213,30 @@ func TestIPNSHostnameRedirect(t *testing.T) { } else if hdr[0] != "/foo/" { t.Errorf("location header is %v, expected /foo/", hdr[0]) } + + // make request with prefix to directory containing index.html + req, err = http.NewRequest("GET", ts.URL+"/foo", nil) + if err != nil { + t.Fatal(err) + } + req.Host = "example.net" + req.Header.Set("X-Ipfs-Gateway-Prefix", "/prefix") + + res, err = doWithoutRedirect(req) + if err != nil { + t.Fatal(err) + } + + // expect 302 redirect to same path, but with prefix and trailing slash + if res.StatusCode != 302 { + t.Errorf("status is %d, expected 302", res.StatusCode) + } + hdr = res.Header["Location"] + if len(hdr) < 1 { + t.Errorf("location header not present") + } else if hdr[0] != "/prefix/foo/" { + t.Errorf("location header is %v, expected /prefix/foo/", hdr[0]) + } } func TestIPNSHostnameBacklinks(t *testing.T) { @@ -282,7 +306,7 @@ func TestIPNSHostnameBacklinks(t *testing.T) { t.Fatalf("expected file in directory listing") } - // make request to directory listing + // make request to directory listing at root req, err = http.NewRequest("GET", ts.URL, nil) if err != nil { t.Fatal(err) @@ -294,7 +318,7 @@ func TestIPNSHostnameBacklinks(t *testing.T) { t.Fatal(err) } - // expect correct backlinks + // expect correct backlinks at root body, err = ioutil.ReadAll(res.Body) if err != nil { t.Fatalf("error reading response: %s", err) @@ -341,4 +365,35 @@ func TestIPNSHostnameBacklinks(t *testing.T) { if !strings.Contains(s, "") { t.Fatalf("expected file in directory listing") } + + // make request to directory listing with prefix + req, err = http.NewRequest("GET", ts.URL, nil) + if err != nil { + t.Fatal(err) + } + req.Host = "example.net" + req.Header.Set("X-Ipfs-Gateway-Prefix", "/prefix") + + res, err = doWithoutRedirect(req) + if err != nil { + t.Fatal(err) + } + + // expect correct backlinks with prefix + body, err = ioutil.ReadAll(res.Body) + if err != nil { + t.Fatalf("error reading response: %s", err) + } + s = string(body) + t.Logf("body: %s\n", string(body)) + + if !strings.Contains(s, "Index of /prefix") { + t.Fatalf("expected a path in directory listing") + } + if !strings.Contains(s, "") { + t.Fatalf("expected backlink in directory listing") + } + if !strings.Contains(s, "") { + t.Fatalf("expected file in directory listing") + } } diff --git a/core/corehttp/ipns_hostname.go b/core/corehttp/ipns_hostname.go index 94faccd5db3..dd6a64d5f5a 100644 --- a/core/corehttp/ipns_hostname.go +++ b/core/corehttp/ipns_hostname.go @@ -24,7 +24,7 @@ func IPNSHostnameOption() ServeOption { if len(host) > 0 && isd.IsDomain(host) { name := "/ipns/" + host if _, err := n.Namesys.Resolve(ctx, name); err == nil { - r.Header["X-IPNS-Original-Path"] = []string{r.URL.Path} + r.Header["X-Ipns-Original-Path"] = []string{r.URL.Path} r.URL.Path = name + r.URL.Path } }