Skip to content

Commit

Permalink
feat(gw): Cache-Control: only-if-cached
Browse files Browse the repository at this point in the history
  • Loading branch information
lidel authored and Jorropo committed Jul 7, 2022
1 parent fbd65e0 commit 58aaee0
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 7 deletions.
6 changes: 5 additions & 1 deletion core/corehttp/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,16 @@ func GatewayOption(writable bool, paths ...string) ServeOption {
"X-Ipfs-Roots",
}, headers[ACEHeadersName]...))

var gateway http.Handler = newGatewayHandler(GatewayConfig{
var gateway http.Handler
gateway, err = newGatewayHandler(GatewayConfig{
Headers: headers,
Writable: writable,
PathPrefixes: cfg.Gateway.PathPrefixes,
FastDirIndexThreshold: int(cfg.Gateway.FastDirIndexThreshold.WithDefault(100)),
}, api)
if err != nil {
return nil, err
}

gateway = otelhttp.NewHandler(gateway, "Gateway.Request")

Expand Down
46 changes: 40 additions & 6 deletions core/corehttp/gateway_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
path "github.com/ipfs/go-path"
"github.com/ipfs/go-path/resolver"
coreiface "github.com/ipfs/interface-go-ipfs-core"
options "github.com/ipfs/interface-go-ipfs-core/options"
ipath "github.com/ipfs/interface-go-ipfs-core/path"
routing "github.com/libp2p/go-libp2p-core/routing"
prometheus "github.com/prometheus/client_golang/prometheus"
Expand Down Expand Up @@ -66,8 +67,9 @@ type redirectTemplateData struct {
// gatewayHandler is a HTTP handler that serves IPFS objects (accessible by default at /ipfs/<path>)
// (it serves requests like GET /ipfs/QmVRzPKPzNtSrEzBFm2UZfxmPAgnaLke4DMcerbsGGSaFe/link)
type gatewayHandler struct {
config GatewayConfig
api coreiface.CoreAPI
config GatewayConfig
api coreiface.CoreAPI
offlineApi coreiface.CoreAPI

// generic metrics
firstContentBlockGetMetric *prometheus.HistogramVec
Expand Down Expand Up @@ -211,10 +213,15 @@ func newGatewayHistogramMetric(name string, help string) *prometheus.HistogramVe
return histogramMetric
}

func newGatewayHandler(c GatewayConfig, api coreiface.CoreAPI) *gatewayHandler {
func newGatewayHandler(c GatewayConfig, api coreiface.CoreAPI) (*gatewayHandler, error) {
offlineApi, err := api.WithOptions(options.Api.Offline(true))
if err != nil {
return nil, err
}
i := &gatewayHandler{
config: c,
api: api,
config: c,
api: api,
offlineApi: offlineApi,
// Improved Metrics
// ----------------------------
// Time till the first content block (bar in /ipfs/cid/foo/bar)
Expand Down Expand Up @@ -255,7 +262,7 @@ func newGatewayHandler(c GatewayConfig, api coreiface.CoreAPI) *gatewayHandler {
"The time to receive the first UnixFS node on a GET from the gateway.",
),
}
return i
return i, nil
}

func parseIpfsPath(p string) (cid.Cid, string, error) {
Expand Down Expand Up @@ -360,6 +367,11 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
}

contentPath := ipath.New(r.URL.Path)

if requestHandled := i.handleOnlyIfCached(w, r, contentPath, logger); requestHandled {
return
}

if requestHandled := handleSuperfluousNamespace(w, r, contentPath); requestHandled {
return
}
Expand Down Expand Up @@ -956,6 +968,28 @@ func debugStr(path string) string {
return q
}

// Detect 'Cache-Control: only-if-cached' in request and return data if it is already in the local datastore.
// https://github.com/ipfs/specs/blob/main/http-gateways/PATH_GATEWAY.md#cache-control-request-header
func (i *gatewayHandler) handleOnlyIfCached(w http.ResponseWriter, r *http.Request, contentPath ipath.Path, logger *zap.SugaredLogger) (requestHandled bool) {
if r.Header.Get("Cache-Control") == "only-if-cached" {
_, err := i.offlineApi.Block().Stat(r.Context(), contentPath)
if err != nil {
if r.Method == http.MethodHead {
w.WriteHeader(http.StatusPreconditionFailed)
return true
}
errMsg := fmt.Sprintf("%q not in local datastore", contentPath.String())
http.Error(w, errMsg, http.StatusPreconditionFailed)
return true
}
if r.Method == http.MethodHead {
w.WriteHeader(http.StatusOK)
return true
}
}
return false
}

func handleUnsupportedHeaders(r *http.Request) (err *requestError) {
// X-Ipfs-Gateway-Prefix was removed (https://github.com/ipfs/kubo/issues/7702)
// TODO: remove this after go-ipfs 0.13 ships
Expand Down
22 changes: 22 additions & 0 deletions test/sharness/t0116-gateway-cache.sh
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,28 @@ test_expect_success "Prepare IPNS unixfs content path for testing" '
cat curl_ipns_file_output
'

# Cache-Control: only-if-cached
test_expect_success "HEAD for /ipfs/ with only-if-cached succeeds when in local datastore" '
curl -sv -I -H "Cache-Control: only-if-cached" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/root4/index.html" > curl_onlyifcached_postitive_head 2>&1 &&
cat curl_onlyifcached_postitive_head &&
grep "< HTTP/1.1 200 OK" curl_onlyifcached_postitive_head
'
test_expect_success "HEAD for /ipfs/ with only-if-cached fails when not in local datastore" '
curl -sv -I -H "Cache-Control: only-if-cached" "http://127.0.0.1:$GWAY_PORT/ipfs/$(date | ipfs add --only-hash -Q)" > curl_onlyifcached_negative_head 2>&1 &&
cat curl_onlyifcached_negative_head &&
grep "< HTTP/1.1 412 Precondition Failed" curl_onlyifcached_negative_head
'
test_expect_success "GET for /ipfs/ with only-if-cached succeeds when in local datastore" '
curl -svX GET -H "Cache-Control: only-if-cached" "http://127.0.0.1:$GWAY_PORT/ipfs/$ROOT1_CID/root2/root3/root4/index.html" >/dev/null 2>curl_onlyifcached_postitive_out &&
cat curl_onlyifcached_postitive_out &&
grep "< HTTP/1.1 200 OK" curl_onlyifcached_postitive_out
'
test_expect_success "GET for /ipfs/ with only-if-cached fails when not in local datastore" '
curl -svX GET -H "Cache-Control: only-if-cached" "http://127.0.0.1:$GWAY_PORT/ipfs/$(date | ipfs add --only-hash -Q)" >/dev/null 2>curl_onlyifcached_negative_out &&
cat curl_onlyifcached_negative_out &&
grep "< HTTP/1.1 412 Precondition Failed" curl_onlyifcached_negative_out
'

# X-Ipfs-Path

## dir generated listing
Expand Down

0 comments on commit 58aaee0

Please sign in to comment.