Skip to content

Commit

Permalink
Separate encrypted and non-encrypted paths (#150)
Browse files Browse the repository at this point in the history
* Do not rely on dbl-sha2-256 codec to determine if querying for encrypted or non-encrypted multihash. Use URL path instead.

Requires ipni/dhstore#84

Fixes #127
  • Loading branch information
gammazero authored Sep 9, 2023
1 parent fe317b3 commit 4aff8e5
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 150 deletions.
32 changes: 11 additions & 21 deletions delegated_translator.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ import (
"net/http"
"net/url"
"path"
"strings"

"github.com/filecoin-project/index-provider/metadata"
"github.com/ipfs/go-cid"
"github.com/ipni/go-libipni/find/model"
"github.com/ipni/go-libipni/metadata"
"github.com/ipni/indexstar/metrics"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/multiformats/go-multiaddr"
Expand All @@ -27,13 +26,15 @@ const (
unknownSchema = unknownProtocol
)

type findFunc func(ctx context.Context, method, source string, req *url.URL, body []byte, mh multihash.Multihash) (int, []byte)
type findFunc func(ctx context.Context, method, source string, req *url.URL, body []byte, mh multihash.Multihash, encrypted bool) (int, []byte)

func NewDelegatedTranslator(backend findFunc) (http.Handler, error) {
finder := delegatedTranslator{backend}
m := http.NewServeMux()
m.HandleFunc("/providers", finder.provide)
m.HandleFunc("/providers/", finder.find)
m.HandleFunc("/encrypted/providers", finder.provide)
m.HandleFunc("/providers/", func(w http.ResponseWriter, r *http.Request) { finder.find(w, r, false) })
m.HandleFunc("/encrypted/providers/", func(w http.ResponseWriter, r *http.Request) { finder.find(w, r, true) })
return m, nil
}

Expand All @@ -60,7 +61,7 @@ func (dt *delegatedTranslator) provide(w http.ResponseWriter, r *http.Request) {
}
}

func (dt *delegatedTranslator) find(w http.ResponseWriter, r *http.Request) {
func (dt *delegatedTranslator) find(w http.ResponseWriter, r *http.Request, encrypted bool) {
_ = stats.RecordWithOptions(context.Background(),
stats.WithTags(tag.Insert(metrics.Method, r.Method)),
stats.WithMeasurements(metrics.HttpDelegatedRoutingMethod.M(1)))
Expand All @@ -87,28 +88,17 @@ func (dt *delegatedTranslator) find(w http.ResponseWriter, r *http.Request) {
return
}

// Translate URL by mapping `/providers/{CID}` to `/cid/{CID}`.
cidUrlParam := strings.TrimPrefix(r.URL.Path, "/providers/")
// TODO: replace with URL.JoinPath once upgraded to go 1.19
findByCid := path.Join("/cid", cidUrlParam)
if err != nil {
http.Error(w, "", http.StatusBadRequest)
return
}
uri, err := url.ParseRequestURI(findByCid)
if err != nil {
http.Error(w, "", http.StatusInternalServerError)
return
}

// Get the CID resource from the last element in the URL path.
cidUrlParam := path.Base(r.URL.Path)
c, err := cid.Decode(cidUrlParam)
if err != nil {
http.Error(w, "", http.StatusBadRequest)
return
}

rcode, resp := dt.be(r.Context(), http.MethodGet, findMethodDelegated, uri, []byte{}, c.Hash())

// Translate URL by mapping `/providers/{CID}` to `/cid/{CID}`.
uri := r.URL.JoinPath("../cid", cidUrlParam)
rcode, resp := dt.be(r.Context(), http.MethodGet, findMethodDelegated, uri, []byte{}, c.Hash(), encrypted)
if rcode != http.StatusOK {
http.Error(w, "", rcode)
return
Expand Down
38 changes: 14 additions & 24 deletions find.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const (
findMethodDelegated = "delegated-v1"
)

func (s *server) findCid(w http.ResponseWriter, r *http.Request) {
func (s *server) findCid(w http.ResponseWriter, r *http.Request, encrypted bool) {
switch r.Method {
case http.MethodOptions:
discardBody(r)
Expand All @@ -39,41 +39,39 @@ func (s *server) findCid(w http.ResponseWriter, r *http.Request) {
discardBody(r)
http.Error(w, "invalid cid: "+err.Error(), http.StatusBadRequest)
}
s.find(w, r, c.Hash())
s.find(w, r, c.Hash(), encrypted)
default:
discardBody(r)
http.Error(w, "", http.StatusNotFound)
}
}

func (s *server) findMultihash(w http.ResponseWriter, r *http.Request) {
func (s *server) findMultihash(w http.ResponseWriter, r *http.Request, encrypted bool) {
switch r.Method {
case http.MethodOptions:
discardBody(r)
handleIPNIOptions(w, true)
case http.MethodPost:
s.find(w, r, nil)
s.find(w, r, nil, encrypted)
default:
discardBody(r)
http.Error(w, "", http.StatusNotFound)
}
}

func (s *server) findMultihashSubtree(w http.ResponseWriter, r *http.Request) {
func (s *server) findMultihashSubtree(w http.ResponseWriter, r *http.Request, encrypted bool) {
switch r.Method {
case http.MethodOptions:
discardBody(r)
handleIPNIOptions(w, false)
case http.MethodGet:

smh := strings.TrimPrefix(path.Base(r.URL.Path), "multihash/")
smh := path.Base(r.URL.Path)
mh, err := multihash.FromB58String(smh)
if err != nil {
discardBody(r)
http.Error(w, "invalid multihash: "+err.Error(), http.StatusBadRequest)
}

s.find(w, r, mh)
s.find(w, r, mh, encrypted)
default:
discardBody(r)
http.Error(w, "", http.StatusNotFound)
Expand Down Expand Up @@ -167,7 +165,7 @@ func (s *server) findMetadataSubtree(w http.ResponseWriter, r *http.Request) {
http.Error(w, "", http.StatusNotFound)
}

func (s *server) find(w http.ResponseWriter, r *http.Request, mh multihash.Multihash) {
func (s *server) find(w http.ResponseWriter, r *http.Request, mh multihash.Multihash, encrypted bool) {
acc, err := getAccepts(r)
if err != nil {
discardBody(r)
Expand All @@ -193,7 +191,7 @@ func (s *server) find(w http.ResponseWriter, r *http.Request, mh multihash.Multi
http.Error(w, "", http.StatusBadRequest)
return
}
rcode, resp := s.doFind(r.Context(), r.Method, findMethodOrig, r.URL, rb, mh)
rcode, resp := s.doFind(r.Context(), r.Method, findMethodOrig, r.URL, rb, mh, encrypted)
if rcode != http.StatusOK {
http.Error(w, "", rcode)
return
Expand All @@ -211,15 +209,15 @@ func (s *server) find(w http.ResponseWriter, r *http.Request, mh multihash.Multi
// JSON unless only unsupported media types are specified.
switch {
case acc.ndjson:
s.doFindNDJson(ctx, w, findMethodOrig, r.URL, false, mh)
s.doFindNDJson(ctx, w, findMethodOrig, r.URL, false, mh, encrypted)
case acc.json || acc.any || !acc.acceptHeaderFound:
if s.translateNonStreaming {
s.doFindNDJson(ctx, w, findMethodOrig, r.URL, true, mh)
s.doFindNDJson(ctx, w, findMethodOrig, r.URL, true, mh, encrypted)
return
}
// In a case where the request has no `Accept` header at all, be forgiving and respond with
// JSON.
rcode, resp := s.doFind(ctx, r.Method, findMethodOrig, r.URL, rb, mh)
rcode, resp := s.doFind(ctx, r.Method, findMethodOrig, r.URL, rb, mh, encrypted)
if rcode != http.StatusOK {
http.Error(w, "", rcode)
return
Expand All @@ -231,7 +229,7 @@ func (s *server) find(w http.ResponseWriter, r *http.Request, mh multihash.Multi
}
}

func (s *server) doFind(ctx context.Context, method, source string, req *url.URL, body []byte, mh multihash.Multihash) (int, []byte) {
func (s *server) doFind(ctx context.Context, method, source string, req *url.URL, body []byte, mh multihash.Multihash, encrypted bool) (int, []byte) {
start := time.Now()
latencyTags := []tag.Mutator{tag.Insert(metrics.Method, method)}
loadTags := []tag.Mutator{tag.Insert(metrics.Method, source)}
Expand All @@ -244,12 +242,6 @@ func (s *server) doFind(ctx context.Context, method, source string, req *url.URL
stats.WithMeasurements(metrics.FindLoad.M(1)))
}()

dmh, err := multihash.Decode(mh)
if err != nil {
log.Warnw("failed to decode multihash", "err", err)
return http.StatusInternalServerError, nil
}

// sgResponse is a struct that exists to capture the backend that the response has been received from
type sgResponse struct {
rsp *model.FindResponse
Expand All @@ -269,9 +261,7 @@ func (s *server) doFind(ctx context.Context, method, source string, req *url.URL
// forward double hashed requests to double hashed backends only and regular requests to regular backends
_, isDhBackend := b.(dhBackend)
_, isProvidersBackend := b.(providersBackend)
if (dmh.Code == multihash.DBL_SHA2_256 && !isDhBackend) ||
(dmh.Code != multihash.DBL_SHA2_256 && isDhBackend) ||
isProvidersBackend {
if (encrypted != isDhBackend) || isProvidersBackend {
return nil, nil
}

Expand Down
16 changes: 4 additions & 12 deletions find_ndjson.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import (
"sync/atomic"
"time"

"github.com/filecoin-project/index-provider/metadata"
"github.com/ipni/go-libipni/find/model"
"github.com/ipni/go-libipni/metadata"
"github.com/ipni/indexstar/metrics"
"github.com/mercari/go-circuitbreaker"
"github.com/multiformats/go-multicodec"
Expand Down Expand Up @@ -134,7 +134,7 @@ func (rs *resultStats) reportMetrics(method string) {
}
}

func (s *server) doFindNDJson(ctx context.Context, w http.ResponseWriter, source string, req *url.URL, translateNonStreaming bool, mh multihash.Multihash) {
func (s *server) doFindNDJson(ctx context.Context, w http.ResponseWriter, source string, req *url.URL, translateNonStreaming bool, mh multihash.Multihash, encrypted bool) {
start := time.Now()
latencyTags := []tag.Mutator{tag.Insert(metrics.Method, http.MethodGet)}
loadTags := []tag.Mutator{tag.Insert(metrics.Method, source)}
Expand All @@ -146,13 +146,7 @@ func (s *server) doFindNDJson(ctx context.Context, w http.ResponseWriter, source
stats.WithTags(loadTags...),
stats.WithMeasurements(metrics.FindLoad.M(1)))
}()
dmh, err := multihash.Decode(mh)
if err != nil {
msg := "Cannot decode multihash"
log.Errorw(msg, "err", err)
http.Error(w, msg, http.StatusBadRequest)
return
}

var maxWait time.Duration
if translateNonStreaming {
maxWait = config.Server.ResultMaxWait
Expand All @@ -179,9 +173,7 @@ func (s *server) doFindNDJson(ctx context.Context, w http.ResponseWriter, source
// forward double hashed requests to double hashed backends only and regular requests to regular backends
_, isDhBackend := b.(dhBackend)
_, isProvidersBackend := b.(providersBackend)
if (dmh.Code == multihash.DBL_SHA2_256 && !isDhBackend) ||
(dmh.Code != multihash.DBL_SHA2_256 && isDhBackend) ||
isProvidersBackend {
if (encrypted != isDhBackend) || isProvidersBackend {
return nil, nil
}

Expand Down
21 changes: 9 additions & 12 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,18 @@ go 1.20

require (
contrib.go.opencensus.io/exporter/prometheus v0.4.0
github.com/filecoin-project/index-provider v0.9.1
github.com/ipfs/go-cid v0.4.1
github.com/ipfs/go-log/v2 v2.5.1
github.com/ipni/go-libipni v0.3.1
github.com/libp2p/go-libp2p v0.29.0
github.com/ipni/go-libipni v0.5.0
github.com/libp2p/go-libp2p v0.31.0
github.com/mercari/go-circuitbreaker v0.0.2
github.com/mitchellh/go-homedir v1.1.0
github.com/multiformats/go-multiaddr v0.10.1
github.com/multiformats/go-multiaddr v0.11.0
github.com/multiformats/go-multicodec v0.9.0
github.com/multiformats/go-multihash v0.2.3
github.com/prometheus/client_golang v1.14.0
github.com/stretchr/testify v1.8.4
github.com/urfave/cli/v2 v2.16.3
github.com/urfave/cli/v2 v2.25.7
go.opencensus.io v0.23.0
)

Expand All @@ -33,8 +32,7 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/ipld/go-car/v2 v2.6.0 // indirect
github.com/ipld/go-ipld-prime v0.20.0 // indirect
github.com/ipld/go-ipld-prime v0.21.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
Expand All @@ -54,12 +52,11 @@ require (
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.11.0 // indirect
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect
golang.org/x/sys v0.10.0 // indirect
go.uber.org/zap v1.25.0 // indirect
golang.org/x/crypto v0.12.0 // indirect
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect
golang.org/x/sys v0.11.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
Loading

0 comments on commit 4aff8e5

Please sign in to comment.