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

Commit 34e97fb

Browse files
committed
update testing backend
1 parent 386e3c1 commit 34e97fb

File tree

2 files changed

+253
-13
lines changed

2 files changed

+253
-13
lines changed

backend/handlers.go

Lines changed: 239 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,29 @@ import (
44
"bytes"
55
"context"
66
"fmt"
7+
bsfetcher "github.com/ipfs/go-fetcher/impl/blockservice"
8+
format "github.com/ipfs/go-ipld-format"
9+
"github.com/ipfs/go-libipfs/blocks"
10+
"github.com/ipfs/go-libipfs/files"
11+
"github.com/ipfs/go-libipfs/gateway"
12+
"github.com/ipfs/go-merkledag"
13+
"github.com/ipfs/go-path"
14+
"github.com/ipfs/go-path/resolver"
715
unixfile "github.com/ipfs/go-unixfs/file"
16+
"github.com/ipfs/go-unixfsnode"
817
"github.com/ipld/go-car"
918
"github.com/ipld/go-car/util"
19+
dagpb "github.com/ipld/go-codec-dagpb"
20+
"github.com/ipld/go-ipld-prime"
21+
"github.com/ipld/go-ipld-prime/node/basicnode"
22+
"github.com/ipld/go-ipld-prime/schema"
1023
"io"
1124
"net/http"
12-
"os"
25+
"net/url"
1326
"runtime/debug"
1427
"strconv"
1528
"strings"
29+
"sync"
1630
"time"
1731

1832
"github.com/prometheus/client_golang/prometheus"
@@ -76,7 +90,7 @@ func makeGatewayCARHandler(bsrv blockservice.BlockService, port int) (*http.Serv
7690
if formatParam := r.URL.Query().Get("format"); formatParam != "" {
7791
isCar = formatParam == "car"
7892
if !isCar {
79-
http.Error(w, "only raw format supported", http.StatusBadRequest)
93+
http.Error(w, "only car format supported", http.StatusBadRequest)
8094
return
8195
}
8296
} else {
@@ -104,7 +118,7 @@ func makeGatewayCARHandler(bsrv blockservice.BlockService, port int) (*http.Serv
104118
return
105119
}
106120

107-
carStream, err := simpleSelectorToCar(contentPath)
121+
carStream, err := simpleSelectorToCar(ctx, bsrv, contentPath.String(), r.URL.Query())
108122
if err != nil {
109123
http.Error(w, "only the ipfs names is supported", http.StatusBadRequest)
110124
return
@@ -147,8 +161,8 @@ func makeGatewayCARHandler(bsrv blockservice.BlockService, port int) (*http.Serv
147161
}, nil
148162
}
149163

150-
func simpleSelectorToCar(ipfsPath ipath.Path) (io.ReadCloser, error) {
151-
pathSegs := strings.Split(ipfsPath.String(), "/")
164+
func simpleSelectorToCar(ctx context.Context, bsrv blockservice.BlockService, p string, params url.Values) (io.ReadCloser, error) {
165+
pathSegs := strings.Split(p, "/")
152166
if len(pathSegs) < 3 || !(pathSegs[0] == "" && pathSegs[1] == "ipfs") {
153167
return nil, fmt.Errorf("invalid path")
154168
}
@@ -159,6 +173,11 @@ func simpleSelectorToCar(ipfsPath ipath.Path) (io.ReadCloser, error) {
159173
return nil, err
160174
}
161175

176+
ipfspath, err := path.ParsePath(p)
177+
if err != nil {
178+
return nil, err
179+
}
180+
162181
r, w := io.Pipe()
163182
// Setup header for the output car
164183
err = car.WriteHeader(&car.CarHeader{
@@ -169,20 +188,230 @@ func simpleSelectorToCar(ipfsPath ipath.Path) (io.ReadCloser, error) {
169188
return nil, fmt.Errorf("writing car header: %w", err)
170189
}
171190

191+
rangeStr, hasRange := params.Get("bytes"), params.Has("bytes")
192+
depthStr, hasDepth := params.Get("depth"), params.Has("depth")
193+
194+
if hasDepth && !(depthStr == "0" || depthStr == "1" || depthStr == "all") {
195+
return nil, fmt.Errorf("depth type: %s not supported", depthStr)
196+
}
197+
var getRange *gateway.GetRange
198+
if hasRange {
199+
getRange, err = rangeStrToGetRange(rangeStr)
200+
if err != nil {
201+
return nil, err
202+
}
203+
}
204+
172205
go func() {
173206
defer w.Close()
174-
remainingPath := pathSegs[1:]
175-
unixfile.NewUnixfsFile()
207+
blockGetter := merkledag.NewDAGService(bsrv).Session(ctx)
208+
blockGetter = &nodeGetterToCarExporer{
209+
ng: blockGetter,
210+
w: w,
211+
mhSet: make(map[string]struct{}),
212+
}
213+
dsrv := merkledag.NewReadOnlyDagService(blockGetter)
214+
215+
// Setup the UnixFS resolver.
216+
fetcherConfig := bsfetcher.NewFetcherConfig(bsrv)
217+
fetcherConfig.PrototypeChooser = dagpb.AddSupportToChooser(func(lnk ipld.Link, lnkCtx ipld.LinkContext) (ipld.NodePrototype, error) {
218+
if tlnkNd, ok := lnkCtx.LinkNode.(schema.TypedLinkNode); ok {
219+
return tlnkNd.LinkTargetNodePrototype(), nil
220+
}
221+
return basicnode.Prototype.Any, nil
222+
})
223+
fetcher := fetcherConfig.WithReifier(unixfsnode.Reify)
224+
r := resolver.NewBasicResolver(fetcher)
225+
226+
lastCid, remainder, err := r.ResolveToLastNode(ctx, ipfspath)
227+
if err != nil {
228+
goLog.Error(err)
229+
return
230+
}
231+
232+
if hasDepth && depthStr == "0" {
233+
return
234+
}
235+
236+
lastCidNode, err := dsrv.Get(ctx, lastCid)
237+
if err != nil {
238+
goLog.Error(err)
239+
return
240+
}
176241

177-
err = util.LdWrite(os.Stdout, block.Cid().Bytes(), block.RawData()) // write to the output car
242+
ufsNode, err := unixfile.NewUnixfsFile(ctx, dsrv, lastCidNode)
178243
if err != nil {
179-
return fmt.Errorf("writing to output car: %w", err)
244+
// It's not UnixFS
245+
246+
// If it's all fetch the graph recursively
247+
if depthStr == "all" {
248+
if err := merkledag.FetchGraph(ctx, lastCid, dsrv); err != nil {
249+
goLog.Error(err)
250+
}
251+
return
252+
}
253+
254+
//if not then either this is an error (which we can't report) or this is the last block for us to return
255+
return
256+
}
257+
if f, ok := ufsNode.(files.File); ok {
258+
if len(remainder) > 0 {
259+
// this is an error, so we're done
260+
return
261+
}
262+
if hasRange {
263+
// TODO: testing + check off by one errors
264+
var numToRead int64
265+
if *getRange.To < 0 {
266+
size, err := f.Seek(0, io.SeekEnd)
267+
if err != nil {
268+
return
269+
}
270+
numToRead = (size - *getRange.To) - int64(getRange.From)
271+
} else {
272+
numToRead = int64(getRange.From) - *getRange.To
273+
}
274+
275+
if _, err := f.Seek(int64(getRange.From), io.SeekStart); err != nil {
276+
return
277+
}
278+
_, _ = io.CopyN(io.Discard, f, numToRead)
279+
return
280+
}
281+
} else if d, ok := ufsNode.(files.Directory); ok {
282+
if depthStr == "1" {
283+
for d.Entries().Next() {
284+
}
285+
return
286+
}
287+
if depthStr == "all" {
288+
// TODO: being lazy here
289+
w, err := files.NewTarWriter(io.Discard)
290+
if err != nil {
291+
goLog.Error(fmt.Errorf("could not create tar write %w", err))
292+
return
293+
}
294+
if err := w.WriteFile(d, "tmp"); err != nil {
295+
goLog.Error(err)
296+
return
297+
}
298+
return
299+
}
300+
} else {
301+
return
180302
}
181303
}()
182-
_ = rootCid
183304
return r, nil
184305
}
185306

307+
type nodeGetterToCarExporer struct {
308+
ng format.NodeGetter
309+
w io.Writer
310+
311+
lk sync.RWMutex
312+
mhSet map[string]struct{}
313+
}
314+
315+
func (n *nodeGetterToCarExporer) Get(ctx context.Context, c cid.Cid) (format.Node, error) {
316+
nd, err := n.ng.Get(ctx, c)
317+
if err != nil {
318+
return nil, err
319+
}
320+
321+
if err := n.trySendBlock(nd); err != nil {
322+
return nil, err
323+
}
324+
325+
return nd, nil
326+
}
327+
328+
func (n *nodeGetterToCarExporer) GetMany(ctx context.Context, cids []cid.Cid) <-chan *format.NodeOption {
329+
ndCh := n.ng.GetMany(ctx, cids)
330+
outCh := make(chan *format.NodeOption)
331+
go func() {
332+
defer close(outCh)
333+
for nd := range ndCh {
334+
if nd.Err == nil {
335+
if err := n.trySendBlock(nd.Node); err != nil {
336+
select {
337+
case outCh <- &format.NodeOption{Err: err}:
338+
case <-ctx.Done():
339+
}
340+
return
341+
}
342+
select {
343+
case outCh <- nd:
344+
case <-ctx.Done():
345+
}
346+
}
347+
}
348+
}()
349+
return outCh
350+
}
351+
352+
func (n *nodeGetterToCarExporer) trySendBlock(block blocks.Block) error {
353+
h := string(block.Cid().Hash())
354+
n.lk.RLock()
355+
_, found := n.mhSet[h]
356+
n.lk.RUnlock()
357+
if !found {
358+
doSend := false
359+
n.lk.Lock()
360+
_, found := n.mhSet[h]
361+
if !found {
362+
doSend = true
363+
n.mhSet[h] = struct{}{}
364+
}
365+
n.lk.Unlock()
366+
if doSend {
367+
err := util.LdWrite(n.w, block.Cid().Bytes(), block.RawData()) // write to the output car
368+
if err != nil {
369+
return fmt.Errorf("writing to output car: %w", err)
370+
}
371+
}
372+
}
373+
return nil
374+
}
375+
376+
var _ format.NodeGetter = (*nodeGetterToCarExporer)(nil)
377+
378+
func rangeStrToGetRange(rangeStr string) (*gateway.GetRange, error) {
379+
rangeElems := strings.Split(rangeStr, ":")
380+
if len(rangeElems) > 2 {
381+
return nil, fmt.Errorf("invalid range")
382+
}
383+
first, err := strconv.ParseUint(rangeElems[0], 10, 64)
384+
if err != nil {
385+
return nil, err
386+
}
387+
388+
if rangeElems[1] == "*" {
389+
return &gateway.GetRange{
390+
From: first,
391+
To: nil,
392+
}, nil
393+
}
394+
395+
second, err := strconv.ParseInt(rangeElems[1], 10, 64)
396+
if err != nil {
397+
return nil, err
398+
}
399+
400+
if second < 0 {
401+
// TODO: fix, might also require a fix in boxo/gateway
402+
return nil, fmt.Errorf("unsupported")
403+
}
404+
405+
if uint64(second) < first {
406+
return nil, fmt.Errorf("invalid range")
407+
}
408+
409+
return &gateway.GetRange{
410+
From: first,
411+
To: &second,
412+
}, nil
413+
}
414+
186415
func makeGatewayBlockHandler(bsrv blockservice.BlockService, port int) (*http.Server, error) {
187416
mux := http.NewServeMux()
188417
mux.HandleFunc("/ipfs/", func(w http.ResponseWriter, r *http.Request) {

backend/main.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func init() {
3434
rootCmd.Flags().Int("gateway-port", 8081, "gateway port")
3535
rootCmd.Flags().Int("metrics-port", 8041, "metrics port")
3636
rootCmd.Flags().String("car-blockstore", "", "a CAR file to use for serving data instead of network requests")
37+
golog.SetLogLevel("bifrost-gateway-backend", "debug")
3738
}
3839

3940
var rootCmd = &cobra.Command{
@@ -80,9 +81,19 @@ var rootCmd = &cobra.Command{
8081

8182
log.Printf("Starting %s %s", name, version)
8283

83-
gatewaySrv, err := makeGatewayBlockHandler(bsrv, gatewayPort)
84-
if err != nil {
85-
return err
84+
var gatewaySrv *http.Server
85+
var err error
86+
87+
if true {
88+
gatewaySrv, err = makeGatewayCARHandler(bsrv, gatewayPort)
89+
if err != nil {
90+
return err
91+
}
92+
} else {
93+
gatewaySrv, err = makeGatewayBlockHandler(bsrv, gatewayPort)
94+
if err != nil {
95+
return err
96+
}
8697
}
8798

8899
metricsSrv, err := makeMetricsHandler(metricsPort)

0 commit comments

Comments
 (0)