Skip to content

Commit

Permalink
fix: redirects around codecs
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias committed May 31, 2023
1 parent 4eeb5ca commit 946d2c5
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 7 deletions.
57 changes: 53 additions & 4 deletions gateway/gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -745,12 +745,61 @@ func TestIpnsTrustlessMode(t *testing.T) {
}

func TestDagJsonCborPreview(t *testing.T) {
ts, _, root := newTestServerAndNode(t, nil)
api, root := newMockAPI(t)

ts := newTestServerWithConfig(t, api, Config{
Headers: map[string][]string{},
NoDNSLink: false,
PublicGateways: map[string]*Specification{
"example.com": {
Paths: []string{"/ipfs", "/ipns"},
UseSubdomains: true,
DeserializedResponses: true,
},
},
DeserializedResponses: true,
})
t.Logf("test server url: %s", ts.URL)
url := path.Join([]string{"/ipfs", root.String(), t.Name(), "example"})

t.Run("Strings Are Escaped", func(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, ts.URL+url, nil)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

resolvedPath, err := api.resolvePathNoRootsReturned(ctx, ipath.Join(ipath.IpfsPath(root), t.Name(), "example"))
assert.NoError(t, err)

cidStr := resolvedPath.Cid().String()

t.Run("path gateway normalizes to trailing slash", func(t *testing.T) {
t.Parallel()

req, err := http.NewRequest(http.MethodGet, ts.URL+"/ipfs/"+cidStr, nil)
req.Header.Add("Accept", "text/html")
assert.NoError(t, err)

res, err := doWithoutRedirect(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusMovedPermanently, res.StatusCode)
assert.Equal(t, "/ipfs/"+cidStr+"/", res.Header.Get("Location"))
})

t.Run("subdomain gateway correctly redirects", func(t *testing.T) {
t.Parallel()

req, err := http.NewRequest(http.MethodGet, ts.URL+"/ipfs/"+cidStr, nil)
req.Header.Add("Accept", "text/html")
req.Host = "example.com"
assert.NoError(t, err)

res, err := doWithoutRedirect(req)
assert.NoError(t, err)
assert.Equal(t, http.StatusMovedPermanently, res.StatusCode)
assert.Equal(t, "http://"+cidStr+".ipfs.example.com/", res.Header.Get("Location"))
})

t.Run("preview strings are correctly escaped", func(t *testing.T) {
t.Parallel()

req, err := http.NewRequest(http.MethodGet, ts.URL+resolvedPath.String()+"/", nil)
req.Header.Add("Accept", "text/html")
assert.NoError(t, err)

Expand Down
34 changes: 34 additions & 0 deletions gateway/handler_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"

Expand Down Expand Up @@ -153,6 +154,33 @@ func (i *handler) renderCodec(ctx context.Context, w http.ResponseWriter, r *htt
}

func (i *handler) serveCodecHTML(ctx context.Context, w http.ResponseWriter, r *http.Request, blockCid cid.Cid, blockData io.ReadSeekCloser, resolvedPath ipath.Resolved, contentPath ipath.Path) bool {
// If a redirect is setup (e.g. subdomains), do it and do not render the HTML.
if w.Header().Get("Location") != "" {
w.WriteHeader(http.StatusMovedPermanently)
return true
}

// WithHostname may have constructed an IPFS (or IPNS) path using the Host header.
// In this case, we need the original path for constructing the redirect.
requestURI, err := url.ParseRequestURI(r.RequestURI)
if err != nil {
i.webError(w, r, fmt.Errorf("failed to parse request path: %w", err), http.StatusInternalServerError)
return false
}

// Ensure HTML rendering is in a path that ends with trailing slash.
if requestURI.Path[len(requestURI.Path)-1] != '/' {
suffix := "/"
// preserve query parameters
if r.URL.RawQuery != "" {
suffix = suffix + "?" + r.URL.RawQuery
}
// /ipfs/cid/foo?bar must be redirected to /ipfs/cid/foo/?bar
redirectURL := requestURI.Path + suffix
http.Redirect(w, r, redirectURL, http.StatusMovedPermanently)
return true
}

// A HTML directory index will be presented, be sure to set the correct
// type instead of relying on autodetection (which may fail).
w.Header().Set("Content-Type", "text/html")
Expand Down Expand Up @@ -212,6 +240,9 @@ func parseNode(blockCid cid.Cid, blockData io.ReadSeekCloser) *assets.ParsedNode

// serveCodecRaw returns the raw block without any conversion
func (i *handler) serveCodecRaw(ctx context.Context, w http.ResponseWriter, r *http.Request, blockData io.ReadSeekCloser, contentPath ipath.Path, name string, modtime, begin time.Time) bool {
// Special fix around redirects.
w = &statusResponseWriter{w}

// ServeContent will take care of
// If-None-Match+Etag, Content-Length and range requests
_, dataSent, _ := ServeContent(w, r, name, modtime, blockData)
Expand All @@ -226,6 +257,9 @@ func (i *handler) serveCodecRaw(ctx context.Context, w http.ResponseWriter, r *h

// serveCodecConverted returns payload converted to codec specified in toCodec
func (i *handler) serveCodecConverted(ctx context.Context, w http.ResponseWriter, r *http.Request, blockCid cid.Cid, blockData io.ReadSeekCloser, contentPath ipath.Path, toCodec mc.Code, modtime, begin time.Time) bool {
// Special fix around redirects.
w = &statusResponseWriter{w}

codec := blockCid.Prefix().Codec
decoder, err := multicodec.LookupDecoder(codec)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions gateway/handler_unixfs_dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ func (i *handler) serveDirectory(ctx context.Context, w http.ResponseWriter, r *
ctx, span := spanTrace(ctx, "Handler.ServeDirectory", trace.WithAttributes(attribute.String("path", resolvedPath.String())))
defer span.End()

// HostnameOption might have constructed an IPNS/IPFS path using the Host header.
// In this case, we need the original path for constructing redirects
// and links that match the requested URL.
// WithHostname might have constructed an IPNS/IPFS 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
requestURI, err := url.ParseRequestURI(r.RequestURI)
Expand Down

0 comments on commit 946d2c5

Please sign in to comment.