Marcin Rataj 2022-07-05 15:39:44 +02:00 committed by Jorropo
parent fbd65e0c8d
commit 58aaee00f8
3 changed files with 67 additions and 7 deletions

View File

@ -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")

View File

@ -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

View File

@ -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