Skip to content
This repository has been archived by the owner on Jun 20, 2024. It is now read-only.

Commit

Permalink
feat: add proxy routing via /api/v0/routing/get
Browse files Browse the repository at this point in the history
  • Loading branch information
hacdias committed Feb 6, 2023
1 parent f3281b0 commit 3d0e987
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 14 deletions.
5 changes: 2 additions & 3 deletions gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type bifrostGateway struct {
namesys namesys.NameSystem
}

func newBifrostGateway(blockService blockservice.BlockService) (*bifrostGateway, error) {
func newBifrostGateway(blockService blockservice.BlockService, routing routing.ValueStore) (*bifrostGateway, error) {
// Setup the DAG services, which use the CAR block store.
dagService := merkledag.NewDAGService(blockService)

Expand All @@ -54,8 +54,7 @@ func newBifrostGateway(blockService blockservice.BlockService) (*bifrostGateway,
resolver := resolver.NewBasicResolver(fetcher)

// Setup name system for DNSLink and IPNS resolution.
// TODO: NoOpRouting must be replaced by something that can resolve IPNS names.
namesys, err := namesys.NewNameSystem(&NoOpRouting{})
namesys, err := namesys.NewNameSystem(routing)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ go 1.19

require (
github.com/filecoin-saturn/caboose v0.0.0-20230202180001-ec33a82edca8
github.com/gogo/protobuf v1.3.2
github.com/ipfs/go-blockservice v0.5.0
github.com/ipfs/go-cid v0.3.2
github.com/ipfs/go-fetcher v1.6.1
github.com/ipfs/go-ipfs-blockstore v1.2.0
github.com/ipfs/go-ipfs-exchange-offline v0.3.0
github.com/ipfs/go-ipld-format v0.4.0
github.com/ipfs/go-ipns v0.3.0
github.com/ipfs/go-libipfs v0.4.1-0.20230202091244-302b2799386d
github.com/ipfs/go-merkledag v0.9.0
github.com/ipfs/go-namesys v0.7.0
Expand All @@ -36,7 +38,6 @@ require (
github.com/gabriel-vasile/mimetype v1.4.1 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/uuid v1.3.0 // indirect
Expand All @@ -54,7 +55,6 @@ require (
github.com/ipfs/go-ipfs-util v0.0.2 // indirect
github.com/ipfs/go-ipld-cbor v0.0.6 // indirect
github.com/ipfs/go-ipld-legacy v0.1.1 // indirect
github.com/ipfs/go-ipns v0.3.0 // indirect
github.com/ipfs/go-log v1.0.5 // indirect
github.com/ipfs/go-log/v2 v2.5.1 // indirect
github.com/ipfs/go-metrics-interface v0.0.1 // indirect
Expand Down
6 changes: 3 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ func makeGatewayHandler(saturnOrchestrator, saturnLogger string, kuboRPC []strin
blockService := blockservice.New(blockStore, offline.Exchange(blockStore))

// // Sets up the routing system, which will proxy the IPNS routing requests to the given gateway.
// routing := newProxyRouting(*gatewayUrlPtr, nil)
routing := newProxyRouting(kuboRPC, nil)

// Creates the gateway with the block service and the routing.
gwAPI, err := newBifrostGateway(blockService)
gwAPI, err := newBifrostGateway(blockService, routing)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -151,7 +151,7 @@ func newAPIHandler(endpoints []string) http.Handler {
rand := rand.New(s)

return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// TODO: naive redirection. How to choose better?
// Naively choose one of the Kubo RPC clients.
endpoint := endpoints[rand.Intn(len(endpoints))]
http.Redirect(w, r, endpoint+r.URL.Path+"?"+r.URL.RawQuery, http.StatusFound)
})
Expand Down
131 changes: 125 additions & 6 deletions routing.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,139 @@ package main

import (
"context"
"encoding/base64"
"fmt"
"io"
"math/rand"
"net/http"
"net/url"
"strings"
"time"

"github.com/gogo/protobuf/proto"
"github.com/ipfs/go-ipns"
ipns_pb "github.com/ipfs/go-ipns/pb"
ic "github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/routing"
)

type NoOpRouting struct{}
type proxyRouting struct {
kuboRPC []string
httpClient *http.Client
rand *rand.Rand
}

func newProxyRouting(kuboRPC []string, client *http.Client) routing.ValueStore {
if client == nil {
client = http.DefaultClient
}

s := rand.NewSource(time.Now().Unix())
rand := rand.New(s)

func (r *NoOpRouting) PutValue(context.Context, string, []byte, ...routing.Option) error {
return &proxyRouting{
kuboRPC: kuboRPC,
httpClient: client,
rand: rand,
}
}

func (ps *proxyRouting) PutValue(context.Context, string, []byte, ...routing.Option) error {
return routing.ErrNotSupported
}

func (r *NoOpRouting) GetValue(context.Context, string, ...routing.Option) ([]byte, error) {
return nil, routing.ErrNotSupported
func (ps *proxyRouting) GetValue(ctx context.Context, k string, opts ...routing.Option) ([]byte, error) {
return ps.fetch(ctx, k)
}

func (r *NoOpRouting) SearchValue(context.Context, string, ...routing.Option) (<-chan []byte, error) {
return nil, routing.ErrNotSupported
func (ps *proxyRouting) SearchValue(ctx context.Context, k string, opts ...routing.Option) (<-chan []byte, error) {
if !strings.HasPrefix(k, "/ipns/") {
return nil, routing.ErrNotSupported
}

ch := make(chan []byte)

go func() {
v, err := ps.fetch(ctx, k)
if err != nil {
close(ch)
} else {
ch <- v
close(ch)
}
}()

return ch, nil
}

func (ps *proxyRouting) fetch(ctx context.Context, key string) ([]byte, error) {
key = strings.TrimPrefix(key, "/ipns/")
id, err := peer.IDFromBytes([]byte(key))
if err != nil {
return nil, err
}

key = "/ipns/" + peer.ToCid(id).String()

// Naively choose one of the Kubo RPC clients.
endpoint := ps.kuboRPC[rand.Intn(len(ps.kuboRPC))]

u, err := url.Parse(fmt.Sprintf("%s/api/v0/routing/get?arg=%s", endpoint, key))
if err != nil {
return nil, err
}

resp, err := ps.httpClient.Do(&http.Request{
Method: http.MethodPost,
URL: u,
})
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status from remote gateway: %s", resp.Status)
}

rb, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

b64 := string(rb)
b64 = strings.TrimSpace(b64)
b64 = strings.TrimPrefix(b64, "\"")
b64 = strings.TrimSuffix(b64, "\"")

rb, err = base64.StdEncoding.DecodeString(b64)
if err != nil {
return nil, err
}

var entry ipns_pb.IpnsEntry
err = proto.Unmarshal(rb, &entry)
if err != nil {
return nil, err
}

pub, err := id.ExtractPublicKey()
if err != nil {
// Make sure it works with all those RSA that cannot be embedded into the
// Peer ID.
if len(entry.PubKey) > 0 {
pub, err = ic.UnmarshalPublicKey(entry.PubKey)
}
}
if err != nil {
return nil, err
}

err = ipns.Validate(pub, &entry)
if err != nil {
return nil, err
}

return rb, nil
}

0 comments on commit 3d0e987

Please sign in to comment.