From 7be44741f67d06a7251d785f3a6bd7c912e9a967 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Thu, 8 Jun 2023 10:33:09 +0200 Subject: [PATCH] feat: use last segment cid as root - this also allows to return the PathMetadata, which also allows to set the correct X-Ipfs-Root header. --- gateway/blocks_backend.go | 18 +++++++++--------- gateway/gateway.go | 2 +- gateway/gateway_test.go | 2 +- gateway/handler_car.go | 7 ++----- gateway/handler_test.go | 6 +++--- gateway/metrics.go | 8 +++----- 6 files changed, 19 insertions(+), 24 deletions(-) diff --git a/gateway/blocks_backend.go b/gateway/blocks_backend.go index bacb90896..9586352c9 100644 --- a/gateway/blocks_backend.go +++ b/gateway/blocks_backend.go @@ -232,20 +232,20 @@ func (bb *BlocksBackend) Head(ctx context.Context, path ImmutablePath) (ContentP return md, fileNode, nil } -func (bb *BlocksBackend) GetCAR(ctx context.Context, p ImmutablePath, params CarParams) (io.ReadCloser, error) { +func (bb *BlocksBackend) GetCAR(ctx context.Context, p ImmutablePath, params CarParams) (ContentPathMetadata, io.ReadCloser, error) { + pathMetadata, err := bb.ResolvePath(ctx, p) + if err != nil { + return ContentPathMetadata{}, nil, err + } + contentPathStr := p.String() if !strings.HasPrefix(contentPathStr, "/ipfs/") { - return nil, fmt.Errorf("path does not have /ipfs/ prefix") - } - firstSegment, _, _ := strings.Cut(contentPathStr[6:], "/") - rootCid, err := cid.Decode(firstSegment) - if err != nil { - return nil, err + return ContentPathMetadata{}, nil, fmt.Errorf("path does not have /ipfs/ prefix") } r, w := io.Pipe() go func() { - cw, err := storage.NewWritable(w, []cid.Cid{rootCid}, car.WriteAsCarV1(true)) + cw, err := storage.NewWritable(w, []cid.Cid{pathMetadata.LastSegment.Cid()}, car.WriteAsCarV1(true)) if err != nil { // io.PipeWriter.CloseWithError always returns nil. _ = w.CloseWithError(err) @@ -275,7 +275,7 @@ func (bb *BlocksBackend) GetCAR(ctx context.Context, p ImmutablePath, params Car _ = w.CloseWithError(carWriteErr) }() - return r, nil + return pathMetadata, r, nil } // walkGatewaySimpleSelector walks the subgraph described by the path and terminal element parameters diff --git a/gateway/gateway.go b/gateway/gateway.go index 358453b73..cc0babba1 100644 --- a/gateway/gateway.go +++ b/gateway/gateway.go @@ -279,7 +279,7 @@ type IPFSBackend interface { // GetCAR returns a CAR file for the given immutable path. It returns an error // if there was an issue before the CAR streaming begins. - GetCAR(context.Context, ImmutablePath, CarParams) (io.ReadCloser, error) + GetCAR(context.Context, ImmutablePath, CarParams) (ContentPathMetadata, io.ReadCloser, error) // IsCached returns whether or not the path exists locally. IsCached(context.Context, path.Path) bool diff --git a/gateway/gateway_test.go b/gateway/gateway_test.go index 355bcde13..236ae7772 100644 --- a/gateway/gateway_test.go +++ b/gateway/gateway_test.go @@ -124,7 +124,7 @@ func (mb *mockBackend) Head(ctx context.Context, immutablePath ImmutablePath) (C return mb.gw.Head(ctx, immutablePath) } -func (mb *mockBackend) GetCAR(ctx context.Context, immutablePath ImmutablePath, params CarParams) (io.ReadCloser, error) { +func (mb *mockBackend) GetCAR(ctx context.Context, immutablePath ImmutablePath, params CarParams) (ContentPathMetadata, io.ReadCloser, error) { return mb.gw.GetCAR(ctx, immutablePath, params) } diff --git a/gateway/handler_car.go b/gateway/handler_car.go index 458110de0..a8be3ded9 100644 --- a/gateway/handler_car.go +++ b/gateway/handler_car.go @@ -52,10 +52,6 @@ func (i *handler) serveCAR(ctx context.Context, w http.ResponseWriter, r *http.R return false } - // TODO(hacdias): this is incorrect and should have the full list of CIDs. - // https://github.com/ipfs/boxo/issues/221 - w.Header().Set("X-Ipfs-Roots", rootCid.String()) - // Set Content-Disposition var name string if urlFilename := r.URL.Query().Get("filename"); urlFilename != "" { @@ -83,11 +79,12 @@ func (i *handler) serveCAR(ctx context.Context, w http.ResponseWriter, r *http.R return false } - carFile, err := i.backend.GetCAR(ctx, imPath, params) + md, carFile, err := i.backend.GetCAR(ctx, imPath, params) if !i.handleRequestErrors(w, r, contentPath, err) { return false } defer carFile.Close() + setIpfsRootsHeader(w, md) // Make it clear we don't support range-requests over a car stream // Partial downloads and resumes should be handled using requests for diff --git a/gateway/handler_test.go b/gateway/handler_test.go index ba9510674..28229a901 100644 --- a/gateway/handler_test.go +++ b/gateway/handler_test.go @@ -61,8 +61,8 @@ func (mb *errorMockBackend) Head(ctx context.Context, path ImmutablePath) (Conte return ContentPathMetadata{}, nil, mb.err } -func (mb *errorMockBackend) GetCAR(ctx context.Context, path ImmutablePath, params CarParams) (io.ReadCloser, error) { - return nil, mb.err +func (mb *errorMockBackend) GetCAR(ctx context.Context, path ImmutablePath, params CarParams) (ContentPathMetadata, io.ReadCloser, error) { + return ContentPathMetadata{}, nil, mb.err } func (mb *errorMockBackend) ResolveMutable(ctx context.Context, path ipath.Path) (ImmutablePath, error) { @@ -173,7 +173,7 @@ func (mb *panicMockBackend) Head(ctx context.Context, immutablePath ImmutablePat panic("i am panicking") } -func (mb *panicMockBackend) GetCAR(ctx context.Context, immutablePath ImmutablePath, params CarParams) (io.ReadCloser, error) { +func (mb *panicMockBackend) GetCAR(ctx context.Context, immutablePath ImmutablePath, params CarParams) (ContentPathMetadata, io.ReadCloser, error) { panic("i am panicking") } diff --git a/gateway/metrics.go b/gateway/metrics.go index bd4ca00bc..69e81425f 100644 --- a/gateway/metrics.go +++ b/gateway/metrics.go @@ -120,17 +120,15 @@ func (b *ipfsBackendWithMetrics) ResolvePath(ctx context.Context, path Immutable return md, err } -func (b *ipfsBackendWithMetrics) GetCAR(ctx context.Context, path ImmutablePath, params CarParams) (io.ReadCloser, error) { +func (b *ipfsBackendWithMetrics) GetCAR(ctx context.Context, path ImmutablePath, params CarParams) (ContentPathMetadata, io.ReadCloser, error) { begin := time.Now() name := "IPFSBackend.GetCAR" ctx, span := spanTrace(ctx, name, trace.WithAttributes(attribute.String("path", path.String()))) defer span.End() - rc, err := b.backend.GetCAR(ctx, path, params) - - // TODO: handle errCh + md, rc, err := b.backend.GetCAR(ctx, path, params) b.updateBackendCallMetric(name, err, begin) - return rc, err + return md, rc, err } func (b *ipfsBackendWithMetrics) IsCached(ctx context.Context, path path.Path) bool {