Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gateway: tests for simpler superfluous namespace handling #572

Merged
merged 1 commit into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions gateway/gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,92 @@ func TestRedirects(t *testing.T) {
do(http.MethodGet)
do(http.MethodHead)
})

t.Run("Superfluous namespace", func(t *testing.T) {
t.Parallel()

backend, root := newMockBackend(t, "fixtures.car")
backend.namesys["/ipns/dnslink-gateway.com"] = newMockNamesysItem(path.FromCid(root), 0)
backend.namesys["/ipns/dnslink-website.com"] = newMockNamesysItem(path.FromCid(root), 0)

ts := newTestServerWithConfig(t, backend, Config{
NoDNSLink: false,
PublicGateways: map[string]*PublicGateway{
"dnslink-gateway.com": {
Paths: []string{"/ipfs", "/ipns"},
NoDNSLink: false,
DeserializedResponses: true,
},
"dnslink-website.com": {
Paths: []string{},
NoDNSLink: false,
DeserializedResponses: true,
},
"gateway.com": {
Paths: []string{"/ipfs"},
UseSubdomains: false,
NoDNSLink: true,
DeserializedResponses: true,
},
"subdomain-gateway.com": {
Paths: []string{"/ipfs", "/ipns"},
UseSubdomains: true,
NoDNSLink: true,
DeserializedResponses: true,
},
},
DeserializedResponses: true,
})

for _, test := range []struct {
host string
path string
status int
location string
}{
// Barebones gateway
{"", "/ipfs/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR", http.StatusMovedPermanently, "/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"},
{"", "/ipfs/ipns/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR", http.StatusMovedPermanently, "/ipns/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"},
{"", "/ipfs/ipns/dnslink.com", http.StatusMovedPermanently, "/ipns/dnslink.com"},

// DNSLink Gateway with /ipfs and /ipns enabled
{"dnslink-gateway.com", "/ipfs/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR", http.StatusMovedPermanently, "/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"},
{"dnslink-gateway.com", "/ipfs/ipns/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR", http.StatusMovedPermanently, "/ipns/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"},
{"dnslink-gateway.com", "/ipfs/ipns/dnslink.com", http.StatusMovedPermanently, "/ipns/dnslink.com"},

// DNSLink Gateway without /ipfs and /ipns
{"dnslink-website.com", "/ipfs/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR", http.StatusNotFound, ""},
{"dnslink-website.com", "/ipfs/ipns/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR", http.StatusNotFound, ""},
{"dnslink-website.com", "/ipfs/ipns/dnslink.com", http.StatusNotFound, ""},

// Public gateway
{"gateway.com", "/ipfs/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR", http.StatusMovedPermanently, "/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"},
{"gateway.com", "/ipfs/ipns/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR", http.StatusMovedPermanently, "/ipns/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"},
{"gateway.com", "/ipfs/ipns/dnslink.com", http.StatusMovedPermanently, "/ipns/dnslink.com"},

// Subdomain gateway
{"subdomain-gateway.com", "/ipfs/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR", http.StatusMovedPermanently, "/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"},
{"subdomain-gateway.com", "/ipfs/ipns/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR", http.StatusMovedPermanently, "/ipns/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"},
{"subdomain-gateway.com", "/ipfs/ipns/dnslink.com", http.StatusMovedPermanently, "/ipns/dnslink.com"},
} {
testName := ts.URL + test.path
if test.host != "" {
testName += " " + test.host
}

t.Run(testName, func(t *testing.T) {
req := mustNewRequest(t, http.MethodGet, ts.URL+test.path, nil)
req.Header.Set("Accept", "text/html")
if test.host != "" {
req.Host = test.host
}
resp := mustDoWithoutRedirect(t, req)
defer resp.Body.Close()
require.Equal(t, test.status, resp.StatusCode)
require.Equal(t, test.location, resp.Header.Get("Location"))
})
}
})
}

func TestDeserializedResponses(t *testing.T) {
Expand Down
36 changes: 1 addition & 35 deletions gateway/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"html/template"
"io"
"mime"
"net/http"
Expand Down Expand Up @@ -43,26 +42,6 @@ var (
noModtime = time.Unix(0, 0) // disables Last-Modified header if passed as modtime
)

// HTML-based redirect for errors which can be recovered from, but we want
// to provide hint to people that they should fix things on their end.
var redirectTemplate = template.Must(template.New("redirect").Parse(`<!DOCTYPE html>
<html>
<head>
<meta 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>`))

type redirectTemplateData struct {
RedirectURL string
SuggestedPath string
ErrorMsg string
}

// handler is a HTTP handler that serves IPFS objects (accessible by default at /ipfs/<path>)
// (it serves requests like GET /ipfs/QmVRzPKPzNtSrEzBFm2UZfxmPAgnaLke4DMcerbsGGSaFe/link)
type handler struct {
Expand Down Expand Up @@ -903,21 +882,8 @@ func (i *handler) handleSuperfluousNamespace(w http.ResponseWriter, r *http.Requ
q, _ := url.ParseQuery(r.URL.RawQuery)
intendedURL = intendedURL + "?" + q.Encode()
}
// return HTTP 400 (Bad Request) with HTML error page that:
// - points at correct canonical path via <link> header
// - displays human-readable error
// - redirects to intendedURL after a short delay

w.WriteHeader(http.StatusBadRequest)
err = redirectTemplate.Execute(w, redirectTemplateData{
RedirectURL: intendedURL,
SuggestedPath: intendedPath.String(),
ErrorMsg: fmt.Sprintf("invalid path: %q should be %q", r.URL.Path, intendedPath.String()),
})
if err != nil {
_, _ = w.Write([]byte(fmt.Sprintf("error during body generation: %v", err)))
}

http.Redirect(w, r, intendedURL, http.StatusMovedPermanently)
return true
}

Expand Down
Loading