refactor: show error, delay redirect
This implements error page that does not hide the problem, but still redirects to a valid path after short delay: https://github.com/ipfs/go-ipfs/pull/7930#issuecomment-786882748
Marcin Rataj committed
Feb 27, 2021 at 00:48 UTC
dae7387584ed15beebac70e32b878113cdb904b6
2 files changed
+37
-11
core/corehttp/gateway_handler.go
+29
-5
@@ -3,6 +3,7 @@ package corehttp
3
import (
4
"context"
5
"fmt"
6
+ "html/template"
7
"io"
8
"mime"
9
"net/http"
@@ -36,6 +37,16 @@ const (
37
38
var onlyAscii = regexp.MustCompile("[[:^ascii:]]")
39
40
+// HTML-based redirect for errors which can be recovered from, but we want
41
+// to provide hint to people that they should fix things on their end.
42
+var redirectTemplate = template.Must(template.New("redirect").Parse(`<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta http-equiv="refresh" content="10;url={{.RedirectURL}}" /><link rel="canonical" href="{{.RedirectURL}}" /></head><body><pre>{{.ErrorMsg}}</pre><pre>(if a redirect does not happen in 10 seconds, use "{{.SuggestedPath}}" instead)</pre></body></html>`))
43
+
44
+type redirectTemplateData struct {
45
+ RedirectURL string
46
+ SuggestedPath string
47
+ ErrorMsg string
48
+}
49
+
50
// gatewayHandler is a HTTP handler that serves IPFS objects (accessible by default at /ipfs/<path>)
51
// (it serves requests like GET /ipfs/QmVRzPKPzNtSrEzBFm2UZfxmPAgnaLke4DMcerbsGGSaFe/link)
52
type gatewayHandler struct {
@@ -216,19 +227,32 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
227
}
228
229
parsedPath := ipath.New(urlPath)
219
- if err := parsedPath.IsValid(); err != nil {
220
- // Attempt to fix redundant /ipfs/ namespace as long resulting
230
+ if pathErr := parsedPath.IsValid(); pathErr != nil {
231
+ // Attempt to fix redundant /ipfs/ namespace as long as resulting
232
// 'intended' path is valid. This is in case gremlins were tickled
233
// wrong way and user ended up at /ipfs/ipfs/{cid} or /ipfs/ipns/{id}
234
// like in bafybeien3m7mdn6imm425vc2s22erzyhbvk5n3ofzgikkhmdkh5cuqbpbq
235
// :^))
236
intendedPath := ipath.New(strings.TrimPrefix(urlPath, "/ipfs"))
226
- if err2 := intendedPath.IsValid(); err2 == nil {
237
+ if err := intendedPath.IsValid(); err == nil {
238
intendedURL := strings.Replace(r.URL.String(), urlPath, intendedPath.String(), 1)
228
- http.Redirect(w, r, intendedURL, http.StatusMovedPermanently)
239
+ // return HTML that
240
+ // - points at correct canonical path via <link> header
241
+ // - displays error
242
+ // - redirects to intendedURL after a delay
243
+ err = redirectTemplate.Execute(w, redirectTemplateData{
244
+ RedirectURL: intendedURL,
245
+ SuggestedPath: intendedPath.String(),
246
+ ErrorMsg: pathErr.Error(),
247
+ })
248
+ if err != nil {
249
+ internalWebError(w, err)
250
+ return
251
+ }
252
return
253
}
231
- webError(w, "invalid ipfs path", err, http.StatusBadRequest)
254
+ // unable to fix path, returning error
255
+ webError(w, "invalid ipfs path", pathErr, http.StatusBadRequest)
256
return
257
}
258
test/sharness/t0110-gateway.sh
+8
-6
@@ -84,10 +84,11 @@ test_expect_success "GET IPFS nonexistent file returns code expected (404)" '
84
test_curl_resp_http_code "http://127.0.0.1:$port/ipfs/$HASH2/pleaseDontAddMe" "HTTP/1.1 404 Not Found"
85
'
86
87
-test_expect_success "GET /ipfs/ipfs/{cid} returns redirect to the valid path" "
88
- curl -sI -o response_with_double_ipfs_ns \"http://127.0.0.1:$port/ipfs/ipfs/bafkqaaa?query=to-remember\" &&
89
- test_should_contain \"Location: /ipfs/bafkqaaa?query=to-remember\" response_with_double_ipfs_ns
90
-"
87
+test_expect_success "GET /ipfs/ipfs/{cid} returns redirect to the valid path" '
88
+ curl -sD - "http://127.0.0.1:$port/ipfs/ipfs/bafkqaaa?query=to-remember" > response_with_double_ipfs_ns &&
89
+ test_should_contain "<meta http-equiv=\"refresh\" content=\"10;url=/ipfs/bafkqaaa?query=to-remember\" />" response_with_double_ipfs_ns &&
90
+ test_should_contain "<link rel=\"canonical\" href=\"/ipfs/bafkqaaa?query=to-remember\" />" response_with_double_ipfs_ns
91
+'
92
93
test_expect_failure "GET IPNS path succeeds" '
94
ipfs name publish --allow-offline "$HASH" &&
@@ -102,8 +103,9 @@ test_expect_failure "GET IPNS path output looks good" '
103
104
test_expect_success "GET /ipfs/ipns/{peerid} returns redirect to the valid path" '
105
PEERID=$(ipfs config Identity.PeerID) &&
105
- curl -sI -o response_with_ipfs_ipns_ns "http://127.0.0.1:$port/ipfs/ipns/${PEERID}?query=to-remember" &&
106
- test_should_contain "Location: /ipns/${PEERID}?query=to-remember" response_with_ipfs_ipns_ns
106
+ curl -sD - "http://127.0.0.1:$port/ipfs/ipns/${PEERID}?query=to-remember" > response_with_ipfs_ipns_ns &&
107
+ test_should_contain "<meta http-equiv=\"refresh\" content=\"10;url=/ipns/${PEERID}?query=to-remember\" />" response_with_ipfs_ipns_ns &&
108
+ test_should_contain "<link rel=\"canonical\" href=\"/ipns/${PEERID}?query=to-remember\" />" response_with_ipfs_ipns_ns
109
'
110
111
test_expect_success "GET invalid IPFS path errors" '