mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-26 12:57:44 +08:00
feat(gw): Cache-Control: only-if-cached
This implements the only-if-cached behavior documented in specs: https://github.com/ipfs/specs/blob/main/http-gateways/PATH_GATEWAY.md#cache-control-request-header https://github.com/ipfs/specs/blob/main/http-gateways/PATH_GATEWAY.md#only-if-cached-head-behavior
This commit is contained in:
parent
fbd65e0c8d
commit
58aaee00f8
@ -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")
|
||||
|
||||
|
||||
@ -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"
|
||||
@ -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
|
||||
@ -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)
|
||||
@ -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) {
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user