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
}