Skip to content

Commit f7bb4b3

Browse files
committed
gateway: implement ipip-0445
See ipfs/specs#445
1 parent 9c22812 commit f7bb4b3

File tree

3 files changed

+87
-10
lines changed

3 files changed

+87
-10
lines changed

gateway/blocks_backend.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,10 @@ func (bb *BlocksBackend) Head(ctx context.Context, path path.ImmutablePath) (Con
296296
var emptyRoot = []cid.Cid{cid.MustParse("bafkqaaa")}
297297

298298
func (bb *BlocksBackend) GetCAR(ctx context.Context, p path.ImmutablePath, params CarParams) (ContentPathMetadata, io.ReadCloser, error) {
299+
if params.SkipRawBlocks.Bool() && p.RootCid().Prefix().Codec == cid.Raw {
300+
301+
}
302+
299303
pathMetadata, err := bb.ResolvePath(ctx, p)
300304
if err != nil {
301305
rootCid, err := cid.Decode(strings.Split(p.String(), "/")[2])
@@ -312,8 +316,9 @@ func (bb *BlocksBackend) GetCAR(ctx context.Context, p path.ImmutablePath, param
312316
blockGetter := merkledag.NewDAGService(bb.blockService).Session(ctx)
313317

314318
blockGetter = &nodeGetterToCarExporer{
315-
ng: blockGetter,
316-
cw: cw,
319+
ng: blockGetter,
320+
cw: cw,
321+
skipRawBlocks: params.SkipRawBlocks.Bool(),
317322
}
318323

319324
// Setup the UnixFS resolver.
@@ -352,8 +357,9 @@ func (bb *BlocksBackend) GetCAR(ctx context.Context, p path.ImmutablePath, param
352357
blockGetter := merkledag.NewDAGService(bb.blockService).Session(ctx)
353358

354359
blockGetter = &nodeGetterToCarExporer{
355-
ng: blockGetter,
356-
cw: cw,
360+
ng: blockGetter,
361+
cw: cw,
362+
skipRawBlocks: params.SkipRawBlocks.Bool(),
357363
}
358364

359365
// Setup the UnixFS resolver.
@@ -732,8 +738,9 @@ func (bb *BlocksBackend) resolvePath(ctx context.Context, p path.Path) (path.Imm
732738
}
733739

734740
type nodeGetterToCarExporer struct {
735-
ng format.NodeGetter
736-
cw storage.WritableCar
741+
ng format.NodeGetter
742+
cw storage.WritableCar
743+
skipRawBlocks bool
737744
}
738745

739746
func (n *nodeGetterToCarExporer) Get(ctx context.Context, c cid.Cid) (format.Node, error) {
@@ -774,6 +781,19 @@ func (n *nodeGetterToCarExporer) GetMany(ctx context.Context, cids []cid.Cid) <-
774781
}
775782

776783
func (n *nodeGetterToCarExporer) trySendBlock(ctx context.Context, block blocks.Block) error {
784+
// FIXME(@Jorropo): this is very inneficient, we fetch all blocks even if we don't send them.
785+
// I've tried doing so using the ipld stack however the problem is that filtering on the
786+
// selector or traversal callback does not work because the unixfs reifier is ran before,
787+
// so trying to filter raw links do nothing because go-unixfsnode removed them already,
788+
// so we need to filter in a callback from unixfsnode but the reifier does not know a about
789+
// [traversal.SkipMe] making it a lost cause. I've looked into updating unixfsnode but this
790+
// much more work because there are no easy way to pass options or understand what the side
791+
// effects of this would be.
792+
// Abstractions everywhere yet a simple small behaviour change require rethinking everything :'(.
793+
// Will fix with boxo/unixfs.
794+
if n.skipRawBlocks && block.Cid().Prefix().Codec == cid.Raw {
795+
return nil
796+
}
777797
return n.cw.Put(ctx, block.Cid().KeyString(), block.RawData())
778798
}
779799

gateway/gateway.go

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,11 @@ type PublicGateway struct {
9595
}
9696

9797
type CarParams struct {
98-
Range *DagByteRange
99-
Scope DagScope
100-
Order DagOrder
101-
Duplicates DuplicateBlocksPolicy
98+
Range *DagByteRange
99+
Scope DagScope
100+
Order DagOrder
101+
Duplicates DuplicateBlocksPolicy
102+
SkipRawBlocks SkipRawBlocksPolicy
102103
}
103104

104105
// DagByteRange describes a range request within a UnixFS file. "From" and
@@ -211,6 +212,46 @@ func (d DuplicateBlocksPolicy) String() string {
211212
return ""
212213
}
213214

215+
// SkipRawBlocksPolicy represents the get parameter 'skip-raw-blocks' (IPIP-445)
216+
type SkipRawBlocksPolicy uint8
217+
218+
const (
219+
SkipRawBlocksImplicit SkipRawBlocksPolicy = iota // implicit default and not skip leaves
220+
SendRawBlocks // explicitely do not skip leaves
221+
SkipRawBlocks // explicitly skip leaves
222+
)
223+
224+
func NewSkipRawBlocksPolicy(v string) (SkipRawBlocksPolicy, error) {
225+
switch v {
226+
case "y":
227+
return SkipRawBlocks, nil
228+
case "n":
229+
return SendRawBlocks, nil
230+
case "":
231+
return SkipRawBlocksImplicit, nil
232+
}
233+
return 0, fmt.Errorf("unsupported skip-raw-blocks GET parameter: %q", v)
234+
}
235+
236+
func (d SkipRawBlocksPolicy) Bool() bool {
237+
// duplicates should be returned only when explicitly requested,
238+
// so any other state than SkipRawBlocksIncluded should return false
239+
return d == SkipRawBlocks
240+
}
241+
242+
func (d SkipRawBlocksPolicy) String() string {
243+
switch d {
244+
case SkipRawBlocksImplicit:
245+
return ""
246+
case SkipRawBlocks:
247+
return "y"
248+
case SendRawBlocks:
249+
return "n"
250+
default:
251+
return strconv.FormatUint(uint64(d), 10)
252+
}
253+
}
254+
214255
type ContentPathMetadata struct {
215256
PathSegmentRoots []cid.Cid
216257
LastSegment path.ImmutablePath

gateway/handler_car.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
const (
2323
carRangeBytesKey = "entity-bytes"
2424
carTerminalElementTypeKey = "dag-scope"
25+
carSkipRawBlocksTypeKey = "skip-raw-blocks"
2526
)
2627

2728
// serveCAR returns a CAR stream for specific DAG+selector
@@ -118,6 +119,7 @@ func buildCarParams(r *http.Request, contentTypeParams map[string]string) (CarPa
118119
queryParams := r.URL.Query()
119120
rangeStr, hasRange := queryParams.Get(carRangeBytesKey), queryParams.Has(carRangeBytesKey)
120121
scopeStr, hasScope := queryParams.Get(carTerminalElementTypeKey), queryParams.Has(carTerminalElementTypeKey)
122+
skipRawBlocksStr, hasSkipRawBlocks := queryParams.Get(carSkipRawBlocksTypeKey), queryParams.Has(carSkipRawBlocksTypeKey)
121123

122124
params := CarParams{}
123125
if hasRange {
@@ -141,6 +143,15 @@ func buildCarParams(r *http.Request, contentTypeParams map[string]string) (CarPa
141143
params.Scope = DagScopeAll
142144
}
143145

146+
if hasSkipRawBlocks {
147+
// skip leaves from IPIP-445
148+
skip, err := NewSkipRawBlocksPolicy(skipRawBlocksStr)
149+
if err != nil {
150+
return CarParams{}, err
151+
}
152+
params.SkipRawBlocks = skip
153+
}
154+
144155
// application/vnd.ipld.car content type parameters from Accept header
145156

146157
// version of CAR format
@@ -249,6 +260,11 @@ func getCarEtag(imPath path.ImmutablePath, params CarParams, rootCid cid.Cid) st
249260
h.WriteString("\x00dups=y")
250261
}
251262

263+
// 'skip-leaves' from IPIP-445 impact Etag only if 'y'
264+
if skip := params.SkipRawBlocks; skip == SkipRawBlocks {
265+
h.WriteString("\x00skip-leaves=y")
266+
}
267+
252268
if params.Range != nil {
253269
if params.Range.From != 0 || params.Range.To != nil {
254270
h.WriteString("\x00range=")

0 commit comments

Comments
 (0)