@cryptotaxi247 / kubo / commits / dae738758

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" '