Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gateway: implement IPIP-0445 (skip-raw-blocks option) #502

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 26 additions & 6 deletions gateway/blocks_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,10 @@
var emptyRoot = []cid.Cid{cid.MustParse("bafkqaaa")}

func (bb *BlocksBackend) GetCAR(ctx context.Context, p path.ImmutablePath, params CarParams) (ContentPathMetadata, io.ReadCloser, error) {
if params.SkipRawBlocks.Bool() && p.RootCid().Prefix().Codec == cid.Raw {

Check failure on line 299 in gateway/blocks_backend.go

View workflow job for this annotation

GitHub Actions / go-check / All

empty branch (SA9003)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs 400:

A Gateway MUST return HTTP error 400 Bad Request when skip-raw-blocks=y is sent for a content path with a root CID with the raw multicodec.

}

Check warning on line 301 in gateway/blocks_backend.go

View check run for this annotation

Codecov / codecov/patch

gateway/blocks_backend.go#L300-L301

Added lines #L300 - L301 were not covered by tests

pathMetadata, err := bb.ResolvePath(ctx, p)
if err != nil {
rootCid, err := cid.Decode(strings.Split(p.String(), "/")[2])
Expand All @@ -312,8 +316,9 @@
blockGetter := merkledag.NewDAGService(bb.blockService).Session(ctx)

blockGetter = &nodeGetterToCarExporer{
ng: blockGetter,
cw: cw,
ng: blockGetter,
cw: cw,
skipRawBlocks: params.SkipRawBlocks.Bool(),

Check warning on line 321 in gateway/blocks_backend.go

View check run for this annotation

Codecov / codecov/patch

gateway/blocks_backend.go#L319-L321

Added lines #L319 - L321 were not covered by tests
}

// Setup the UnixFS resolver.
Expand Down Expand Up @@ -352,8 +357,9 @@
blockGetter := merkledag.NewDAGService(bb.blockService).Session(ctx)

blockGetter = &nodeGetterToCarExporer{
ng: blockGetter,
cw: cw,
ng: blockGetter,
cw: cw,
skipRawBlocks: params.SkipRawBlocks.Bool(),
}

// Setup the UnixFS resolver.
Expand Down Expand Up @@ -732,8 +738,9 @@
}

type nodeGetterToCarExporer struct {
ng format.NodeGetter
cw storage.WritableCar
ng format.NodeGetter
cw storage.WritableCar
skipRawBlocks bool
}

func (n *nodeGetterToCarExporer) Get(ctx context.Context, c cid.Cid) (format.Node, error) {
Expand Down Expand Up @@ -774,6 +781,19 @@
}

func (n *nodeGetterToCarExporer) trySendBlock(ctx context.Context, block blocks.Block) error {
// FIXME(@Jorropo): this is very inneficient, we fetch all blocks even if we don't send them.
// I've tried doing so using the ipld stack however the problem is that filtering on the
// selector or traversal callback does not work because the unixfs reifier is ran before,
// so trying to filter raw links do nothing because go-unixfsnode removed them already,
// so we need to filter in a callback from unixfsnode but the reifier does not know a about
// [traversal.SkipMe] making it a lost cause. I've looked into updating unixfsnode but this
// much more work because there are no easy way to pass options or understand what the side
// effects of this would be.
// Abstractions everywhere yet a simple small behaviour change require rethinking everything :'(.
// Will fix with boxo/unixfs.
Comment on lines +784 to +793
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😞

if n.skipRawBlocks && block.Cid().Prefix().Codec == cid.Raw {
return nil
}

Check warning on line 796 in gateway/blocks_backend.go

View check run for this annotation

Codecov / codecov/patch

gateway/blocks_backend.go#L795-L796

Added lines #L795 - L796 were not covered by tests
return n.cw.Put(ctx, block.Cid().KeyString(), block.RawData())
}

Expand Down
49 changes: 45 additions & 4 deletions gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,11 @@
}

type CarParams struct {
Range *DagByteRange
Scope DagScope
Order DagOrder
Duplicates DuplicateBlocksPolicy
Range *DagByteRange
Scope DagScope
Order DagOrder
Duplicates DuplicateBlocksPolicy
SkipRawBlocks SkipRawBlocksPolicy
}

// DagByteRange describes a range request within a UnixFS file. "From" and
Expand Down Expand Up @@ -211,6 +212,46 @@
return ""
}

// SkipRawBlocksPolicy represents the get parameter 'skip-raw-blocks' (IPIP-445)
type SkipRawBlocksPolicy uint8

const (
SkipRawBlocksImplicit SkipRawBlocksPolicy = iota // implicit default and not skip leaves
SendRawBlocks // explicitely do not skip leaves
SkipRawBlocks // explicitly skip leaves
)

func NewSkipRawBlocksPolicy(v string) (SkipRawBlocksPolicy, error) {
switch v {
case "y":
return SkipRawBlocks, nil
case "n":
return SendRawBlocks, nil
case "":
return SkipRawBlocksImplicit, nil

Check warning on line 231 in gateway/gateway.go

View check run for this annotation

Codecov / codecov/patch

gateway/gateway.go#L224-L231

Added lines #L224 - L231 were not covered by tests
}
return 0, fmt.Errorf("unsupported skip-raw-blocks GET parameter: %q", v)

Check warning on line 233 in gateway/gateway.go

View check run for this annotation

Codecov / codecov/patch

gateway/gateway.go#L233

Added line #L233 was not covered by tests
}

func (d SkipRawBlocksPolicy) Bool() bool {
// duplicates should be returned only when explicitly requested,
// so any other state than SkipRawBlocksIncluded should return false
return d == SkipRawBlocks
}

func (d SkipRawBlocksPolicy) String() string {
switch d {
case SkipRawBlocksImplicit:
return ""
case SkipRawBlocks:
return "y"
case SendRawBlocks:
return "n"
default:
return strconv.FormatUint(uint64(d), 10)

Check warning on line 251 in gateway/gateway.go

View check run for this annotation

Codecov / codecov/patch

gateway/gateway.go#L242-L251

Added lines #L242 - L251 were not covered by tests
}
}

type ContentPathMetadata struct {
PathSegmentRoots []cid.Cid
LastSegment path.ImmutablePath
Expand Down
16 changes: 16 additions & 0 deletions gateway/handler_car.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
const (
carRangeBytesKey = "entity-bytes"
carTerminalElementTypeKey = "dag-scope"
carSkipRawBlocksTypeKey = "skip-raw-blocks"
)

// serveCAR returns a CAR stream for specific DAG+selector
Expand Down Expand Up @@ -118,6 +119,7 @@
queryParams := r.URL.Query()
rangeStr, hasRange := queryParams.Get(carRangeBytesKey), queryParams.Has(carRangeBytesKey)
scopeStr, hasScope := queryParams.Get(carTerminalElementTypeKey), queryParams.Has(carTerminalElementTypeKey)
skipRawBlocksStr, hasSkipRawBlocks := queryParams.Get(carSkipRawBlocksTypeKey), queryParams.Has(carSkipRawBlocksTypeKey)

params := CarParams{}
if hasRange {
Expand All @@ -141,6 +143,15 @@
params.Scope = DagScopeAll
}

if hasSkipRawBlocks {
// skip leaves from IPIP-445
skip, err := NewSkipRawBlocksPolicy(skipRawBlocksStr)
if err != nil {
return CarParams{}, err
}
params.SkipRawBlocks = skip

Check warning on line 152 in gateway/handler_car.go

View check run for this annotation

Codecov / codecov/patch

gateway/handler_car.go#L147-L152

Added lines #L147 - L152 were not covered by tests
}

// application/vnd.ipld.car content type parameters from Accept header

// version of CAR format
Expand Down Expand Up @@ -249,6 +260,11 @@
h.WriteString("\x00dups=y")
}

// 'skip-leaves' from IPIP-445 impact Etag only if 'y'
if skip := params.SkipRawBlocks; skip == SkipRawBlocks {
h.WriteString("\x00skip-leaves=y")
}

Check warning on line 266 in gateway/handler_car.go

View check run for this annotation

Codecov / codecov/patch

gateway/handler_car.go#L265-L266

Added lines #L265 - L266 were not covered by tests

if params.Range != nil {
if params.Range.From != 0 || params.Range.To != nil {
h.WriteString("\x00range=")
Expand Down
Loading