| 1 | package corehttp |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "errors" |
| 6 | "fmt" |
| 7 | "net" |
| 8 | "net/http" |
| 9 | "time" |
| 10 | |
| 11 | "github.com/ipfs/boxo/gateway" |
| 12 | "github.com/ipfs/boxo/ipns" |
| 13 | "github.com/ipfs/boxo/routing/http/server" |
| 14 | "github.com/ipfs/boxo/routing/http/types" |
| 15 | "github.com/ipfs/boxo/routing/http/types/iter" |
| 16 | cid "github.com/ipfs/go-cid" |
| 17 | core "github.com/ipfs/kubo/core" |
| 18 | dht "github.com/libp2p/go-libp2p-kad-dht" |
| 19 | "github.com/libp2p/go-libp2p-kad-dht/dual" |
| 20 | "github.com/libp2p/go-libp2p-kad-dht/fullrt" |
| 21 | "github.com/libp2p/go-libp2p/core/peer" |
| 22 | "github.com/libp2p/go-libp2p/core/routing" |
| 23 | ) |
| 24 | |
| 25 | func RoutingOption() ServeOption { |
| 26 | return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) { |
| 27 | _, headers, err := getGatewayConfig(n) |
| 28 | if err != nil { |
| 29 | return nil, err |
| 30 | } |
| 31 | |
| 32 | handler := server.Handler(&contentRouter{n}) |
| 33 | handler = gateway.NewHeaders(headers).ApplyCors().Wrap(handler) |
| 34 | mux.Handle("/routing/v1/", handler) |
| 35 | return mux, nil |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | type contentRouter struct { |
| 40 | n *core.IpfsNode |
| 41 | } |
| 42 | |
| 43 | func (r *contentRouter) FindProviders(ctx context.Context, key cid.Cid, limit int) (iter.ResultIter[types.Record], error) { |
| 44 | ctx, cancel := context.WithCancel(ctx) |
| 45 | ch := r.n.Routing.FindProvidersAsync(ctx, key, limit) |
| 46 | return iter.ToResultIter[types.Record](&peerChanIter{ |
| 47 | ch: ch, |
| 48 | cancel: cancel, |
| 49 | }), nil |
| 50 | } |
| 51 | |
| 52 | // nolint deprecated |
| 53 | func (r *contentRouter) ProvideBitswap(ctx context.Context, req *server.BitswapWriteProvideRequest) (time.Duration, error) { |
| 54 | return 0, routing.ErrNotSupported |
| 55 | } |
| 56 | |
| 57 | func (r *contentRouter) FindPeers(ctx context.Context, pid peer.ID, limit int) (iter.ResultIter[*types.PeerRecord], error) { |
| 58 | ctx, cancel := context.WithCancel(ctx) |
| 59 | defer cancel() |
| 60 | |
| 61 | addr, err := r.n.Routing.FindPeer(ctx, pid) |
| 62 | if err != nil { |
| 63 | return nil, err |
| 64 | } |
| 65 | |
| 66 | rec := &types.PeerRecord{ |
| 67 | Schema: types.SchemaPeer, |
| 68 | ID: &addr.ID, |
| 69 | } |
| 70 | |
| 71 | for _, addr := range addr.Addrs { |
| 72 | rec.Addrs = append(rec.Addrs, types.Multiaddr{Multiaddr: addr}) |
| 73 | } |
| 74 | |
| 75 | return iter.ToResultIter[*types.PeerRecord](iter.FromSlice[*types.PeerRecord]([]*types.PeerRecord{rec})), nil |
| 76 | } |
| 77 | |
| 78 | func (r *contentRouter) GetIPNS(ctx context.Context, name ipns.Name) (*ipns.Record, error) { |
| 79 | ctx, cancel := context.WithCancel(ctx) |
| 80 | defer cancel() |
| 81 | |
| 82 | raw, err := r.n.Routing.GetValue(ctx, string(name.RoutingKey())) |
| 83 | if err != nil { |
| 84 | return nil, err |
| 85 | } |
| 86 | |
| 87 | return ipns.UnmarshalRecord(raw) |
| 88 | } |
| 89 | |
| 90 | func (r *contentRouter) PutIPNS(ctx context.Context, name ipns.Name, record *ipns.Record) error { |
| 91 | ctx, cancel := context.WithCancel(ctx) |
| 92 | defer cancel() |
| 93 | |
| 94 | raw, err := ipns.MarshalRecord(record) |
| 95 | if err != nil { |
| 96 | return err |
| 97 | } |
| 98 | |
| 99 | // The caller guarantees that name matches the record. This is double checked |
| 100 | // by the internals of PutValue. |
| 101 | return r.n.Routing.PutValue(ctx, string(name.RoutingKey()), raw) |
| 102 | } |
| 103 | |
| 104 | func (r *contentRouter) GetClosestPeers(ctx context.Context, key cid.Cid) (iter.ResultIter[*types.PeerRecord], error) { |
| 105 | // Per the spec, if the peer ID is empty, we should use self. |
| 106 | if key == cid.Undef { |
| 107 | return nil, errors.New("GetClosestPeers key is undefined") |
| 108 | } |
| 109 | |
| 110 | keyStr := string(key.Hash()) |
| 111 | var peers []peer.ID |
| 112 | var err error |
| 113 | |
| 114 | if r.n.DHTClient == nil { |
| 115 | return nil, fmt.Errorf("GetClosestPeers not supported: DHT is not available") |
| 116 | } |
| 117 | |
| 118 | switch dhtClient := r.n.DHTClient.(type) { |
| 119 | case *dual.DHT: |
| 120 | // Only use WAN DHT for public HTTP Routing API. |
| 121 | // LAN DHT contains private network peers that should not be exposed publicly. |
| 122 | if dhtClient.WAN == nil { |
| 123 | return nil, fmt.Errorf("GetClosestPeers not supported: WAN DHT is not available") |
| 124 | } |
| 125 | peers, err = dhtClient.WAN.GetClosestPeers(ctx, keyStr) |
| 126 | case *fullrt.FullRT: |
| 127 | peers, err = dhtClient.GetClosestPeers(ctx, keyStr) |
| 128 | case *dht.IpfsDHT: |
| 129 | peers, err = dhtClient.GetClosestPeers(ctx, keyStr) |
| 130 | default: |
| 131 | return nil, fmt.Errorf("GetClosestPeers not supported for DHT type %T", r.n.DHTClient) |
| 132 | } |
| 133 | |
| 134 | if err != nil { |
| 135 | return nil, err |
| 136 | } |
| 137 | |
| 138 | // We have some DHT-closest peers. Find addresses for them. |
| 139 | // The addresses should be in the peerstore. |
| 140 | records := make([]*types.PeerRecord, 0, len(peers)) |
| 141 | for _, p := range peers { |
| 142 | addrs := r.n.Peerstore.Addrs(p) |
| 143 | rAddrs := make([]types.Multiaddr, len(addrs)) |
| 144 | for i, addr := range addrs { |
| 145 | rAddrs[i] = types.Multiaddr{Multiaddr: addr} |
| 146 | } |
| 147 | record := types.PeerRecord{ |
| 148 | ID: &p, |
| 149 | Schema: types.SchemaPeer, |
| 150 | Addrs: rAddrs, |
| 151 | } |
| 152 | records = append(records, &record) |
| 153 | } |
| 154 | |
| 155 | return iter.ToResultIter(iter.FromSlice(records)), nil |
| 156 | } |
| 157 | |
| 158 | type peerChanIter struct { |
| 159 | ch <-chan peer.AddrInfo |
| 160 | cancel context.CancelFunc |
| 161 | next *peer.AddrInfo |
| 162 | } |
| 163 | |
| 164 | func (it *peerChanIter) Next() bool { |
| 165 | addr, ok := <-it.ch |
| 166 | if ok { |
| 167 | it.next = &addr |
| 168 | return true |
| 169 | } |
| 170 | it.next = nil |
| 171 | return false |
| 172 | } |
| 173 | |
| 174 | func (it *peerChanIter) Val() types.Record { |
| 175 | if it.next == nil { |
| 176 | return nil |
| 177 | } |
| 178 | |
| 179 | rec := &types.PeerRecord{ |
| 180 | Schema: types.SchemaPeer, |
| 181 | ID: &it.next.ID, |
| 182 | } |
| 183 | |
| 184 | for _, addr := range it.next.Addrs { |
| 185 | rec.Addrs = append(rec.Addrs, types.Multiaddr{Multiaddr: addr}) |
| 186 | } |
| 187 | |
| 188 | return rec |
| 189 | } |
| 190 | |
| 191 | func (it *peerChanIter) Close() error { |
| 192 | it.cancel() |
| 193 | return nil |
| 194 | } |