Skip to content
75 changes: 71 additions & 4 deletions pkg/api/bzz.go
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,73 @@
s.serveReference(logger, address, paths.Path, w, r, true, queries.FeedLegacyResolve)
}

type getWrappedResult struct {
ch swarm.Chunk
err error
}

// resolveFeed races the resolution of both types of feeds. it returns the first correct feed found or an error.
func (s *Service) resolveFeed(ctx context.Context, getter storage.Getter, ch swarm.Chunk) (swarm.Chunk, error) {
innerCtx, cancel := context.WithCancel(ctx)

Check failure on line 390 in pkg/api/bzz.go

View workflow job for this annotation

GitHub Actions / Lint

lostcancel: the cancel function is not used on all paths (possible context leak) (govet)
getWrapped := func(v1 bool) chan getWrappedResult {
ret := make(chan getWrappedResult)
go func() {
wc, err := feeds.GetWrappedChunk(innerCtx, getter, ch, v1)
if err != nil {
select {
case ret <- getWrappedResult{nil, err}:
return
case <-innerCtx.Done():
return
}
}

// here we just check whether the address is retrievable.
// if it returns an error we send that over the channel, otherwise
// we send the wc chunk back to the caller so that the feed can be
// dereferenced.
_, err = getter.Get(innerCtx, wc.Address())
Copy link
Contributor

@martinconic martinconic Dec 3, 2025

Choose a reason for hiding this comment

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

Isn't this redundant, as in GetWrappedChunk we have a getter.Get(...) that returns nil, err if err, or wc, nil ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately no. GetWrappedChunk only calls Get on the legacy payload, not on a v2 type chunk. Also here, notice that we send the error on the channel, but also whether it is a v1 or v2 payload. Because v2 cannot technically error, we must short-circuit the error and return the payload anyway.

if err != nil {
select {
case ret <- getWrappedResult{nil, err}:
return
case <-innerCtx.Done():
return
}
}
select {
case ret <- getWrappedResult{wc, nil}:
return
case <-innerCtx.Done():
return
}
}()
return ret
}

v1 := getWrapped(true)
v2 := getWrapped(false)

select {
case v1r := <-v1:
if v1r.ch != nil {
cancel()
return v1r.ch, nil
}
// wait for the other one
v2r := <-v2
return v2r.ch, v2r.err

Check failure on line 438 in pkg/api/bzz.go

View workflow job for this annotation

GitHub Actions / Lint

lostcancel: this return statement may be reached without using the cancel var defined on line 390 (govet)
case v2r := <-v2:
if v2r.ch != nil {
cancel()
return v2r.ch, nil
}
// wait for the other one
v1r := <-v1
return v1r.ch, v1r.err
}
}

func (s *Service) serveReference(logger log.Logger, address swarm.Address, pathVar string, w http.ResponseWriter, r *http.Request, headerOnly bool, feedLegacyResolve bool) {
loggerV1 := logger.V(1).Build()

Expand Down Expand Up @@ -415,7 +482,6 @@
jsonhttp.BadRequest(w, "could not parse headers")
return
}

FETCH:
// read manifest entry
m, err := manifest.NewDefaultManifestReference(
Expand Down Expand Up @@ -449,7 +515,8 @@
jsonhttp.NotFound(w, "no update found")
return
}
wc, err := feeds.GetWrappedChunk(ctx, s.storer.Download(cache), ch, feedLegacyResolve)

wc, err := s.resolveFeed(ctx, s.storer.Download(cache), ch)
if err != nil {
if errors.Is(err, feeds.ErrNotLegacyPayload) {
logger.Debug("bzz: download: feed is not a legacy payload")
Expand All @@ -468,10 +535,10 @@
jsonhttp.InternalServerError(w, "mapStructure feed update")
return
}

address = wc.Address()
// modify ls and init with non-existing wrapped chunk
ls = loadsave.NewReadonlyWithRootCh(s.storer.Download(cache), s.storer.Cache(), wc, rLevel)

feedDereferenced = true
curBytes, err := cur.MarshalBinary()
if err != nil {
Expand All @@ -490,7 +557,6 @@
goto FETCH
}
}

if pathVar == "" {
loggerV1.Debug("bzz download: handle empty path", "address", address)

Expand All @@ -505,6 +571,7 @@
return
}
}

logger.Debug("bzz download: address not found or incorrect", "address", address, "path", pathVar)
logger.Error(nil, "address not found or incorrect")
jsonhttp.NotFound(w, "address not found or incorrect")
Expand Down
6 changes: 3 additions & 3 deletions pkg/api/bzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,7 @@ func TestFeedIndirection(t *testing.T) {
Feeds: factory,
})

jsonhttptest.Request(t, client, http.MethodGet, bzzDownloadResource(manifRef.String(), "", true), http.StatusOK,
jsonhttptest.Request(t, client, http.MethodGet, bzzDownloadResource(manifRef.String(), "", false), http.StatusOK,
jsonhttptest.WithExpectedResponse(updateData),
jsonhttptest.WithExpectedContentLength(len(updateData)),
jsonhttptest.WithExpectedResponseHeader(api.AccessControlExposeHeaders, api.SwarmFeedIndexHeader),
Expand All @@ -855,7 +855,7 @@ func TestFeedIndirection(t *testing.T) {
jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "text/html; charset=utf-8"),
)

jsonhttptest.Request(t, client, http.MethodGet, bzzDownloadResource(manifRef.String(), "", false), http.StatusNotFound)
// jsonhttptest.Request(t, client, http.MethodGet, bzzDownloadResource(manifRef.String(), "", false), http.StatusNotFound)
})

t.Run("wrapped feed", func(t *testing.T) {
Expand Down Expand Up @@ -885,7 +885,7 @@ func TestFeedIndirection(t *testing.T) {
jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "text/html; charset=utf-8"),
)

jsonhttptest.Request(t, client, http.MethodGet, bzzDownloadResource(manifRef.String(), "", true), http.StatusBadRequest)
// jsonhttptest.Request(t, client, http.MethodGet, bzzDownloadResource(manifRef.String(), "", true), http.StatusBadRequest)
})
}

Expand Down
Loading