@@ -337,15 +337,7 @@ func (s *Service) bzzDownloadHandler(w http.ResponseWriter, r *http.Request) {
337337 paths .Path = strings .TrimRight (paths .Path , "/" ) + "/" // NOTE: leave one slash if there was some.
338338 }
339339
340- queries := struct {
341- FeedLegacyResolve bool `map:"swarm-feed-legacy-resolve"`
342- }{}
343- if response := s .mapStructure (r .URL .Query (), & queries ); response != nil {
344- response ("invalid query params" , logger , w )
345- return
346- }
347-
348- s .serveReference (logger , address , paths .Path , w , r , false , queries .FeedLegacyResolve )
340+ s .serveReference (logger , address , paths .Path , w , r , false )
349341}
350342
351343func (s * Service ) bzzHeadHandler (w http.ResponseWriter , r * http.Request ) {
@@ -360,14 +352,6 @@ func (s *Service) bzzHeadHandler(w http.ResponseWriter, r *http.Request) {
360352 return
361353 }
362354
363- queries := struct {
364- FeedLegacyResolve bool `map:"swarm-feed-legacy-resolve"`
365- }{}
366- if response := s .mapStructure (r .URL .Query (), & queries ); response != nil {
367- response ("invalid query params" , logger , w )
368- return
369- }
370-
371355 address := paths .Address
372356 if v := getAddressFromContext (r .Context ()); ! v .IsZero () {
373357 address = v
@@ -377,10 +361,119 @@ func (s *Service) bzzHeadHandler(w http.ResponseWriter, r *http.Request) {
377361 paths .Path = strings .TrimRight (paths .Path , "/" ) + "/" // NOTE: leave one slash if there was some.
378362 }
379363
380- s .serveReference (logger , address , paths .Path , w , r , true , queries . FeedLegacyResolve )
364+ s .serveReference (logger , address , paths .Path , w , r , true )
381365}
382366
383- func (s * Service ) serveReference (logger log.Logger , address swarm.Address , pathVar string , w http.ResponseWriter , r * http.Request , headerOnly bool , feedLegacyResolve bool ) {
367+ type getWrappedResult struct {
368+ ch swarm.Chunk
369+ v1 bool // indicates whether the feed that was resolved is v1. false if v2
370+ err error
371+ }
372+
373+ // resolveFeed races the resolution of both types of feeds.
374+ // figure out if its a v1 or v2 chunk.
375+ // it returns the first correct feed found, the type found ("v1" or "v2") or an error.
376+ func (s * Service ) resolveFeed (ctx context.Context , getter storage.Getter , ch swarm.Chunk ) (swarm.Chunk , string , error ) {
377+ innerCtx , cancel := context .WithCancel (ctx )
378+ defer cancel ()
379+ getWrapped := func (v1 bool ) chan getWrappedResult {
380+ ret := make (chan getWrappedResult )
381+ go func () {
382+ wc , err := feeds .GetWrappedChunk (innerCtx , getter , ch , v1 )
383+ if err != nil {
384+ select {
385+ case ret <- getWrappedResult {nil , v1 , err }:
386+ return
387+ case <- innerCtx .Done ():
388+ return
389+ }
390+ }
391+
392+ // here we just check whether the address is retrievable.
393+ // if it returns an error we send that over the channel, otherwise
394+ // we send the wc chunk back to the caller so that the feed can be
395+ // dereferenced.
396+ _ , err = getter .Get (innerCtx , wc .Address ())
397+ if err != nil {
398+ select {
399+ case ret <- getWrappedResult {wc , v1 , err }:
400+ return
401+ case <- innerCtx .Done ():
402+ return
403+ }
404+ }
405+ select {
406+ case ret <- getWrappedResult {wc , v1 , nil }:
407+ return
408+ case <- innerCtx .Done ():
409+ return
410+ }
411+ }()
412+ return ret
413+ }
414+ isV1 , err := feeds .IsV1Payload (ch )
415+ if err != nil {
416+ return nil , "" , err
417+ }
418+ // if we have v1 length, it means there's ambiguity so we
419+ // should fetch both feed versions. if the length isn't v1
420+ // then we should only try to fetch v2.
421+ var (
422+ v1C , v2C chan getWrappedResult
423+ both = false
424+ )
425+ if isV1 {
426+ both = true
427+ v1C = getWrapped (true )
428+ v2C = getWrapped (false )
429+ } else {
430+ v2C = getWrapped (false )
431+ }
432+
433+ // closure to handle processing one channel then the other.
434+ // the "resolving" parameter is meant to tell the closure which feed type is in the result struct
435+ // which in turns allows it to return which feed type was resolved.
436+ processChanOutput := func (resolving string , result getWrappedResult , other chan getWrappedResult ) (swarm.Chunk , string , error ) {
437+ defer cancel ()
438+ if ! both {
439+ if resolving == "v2" {
440+ return result .ch , resolving , nil
441+ }
442+ return result .ch , resolving , result .err
443+ }
444+ // both are being checked. if there's no err return the chunk
445+ // otherwise wait for the other channel
446+ if result .err == nil {
447+ return result .ch , resolving , nil
448+ }
449+ if resolving == "v1" {
450+ resolving = "v2"
451+ } else {
452+ resolving = "v1"
453+ }
454+ // wait for the other one
455+ select {
456+ case result := <- other :
457+ if ! result .v1 {
458+ // resolving v2
459+ return result .ch , resolving , nil
460+ }
461+ return result .ch , resolving , result .err
462+ case <- innerCtx .Done ():
463+ return nil , "" , ctx .Err ()
464+ }
465+ }
466+ select {
467+ case v1r := <- v1C :
468+ return processChanOutput ("v1" , v1r , v2C )
469+ case v2r := <- v2C :
470+ return processChanOutput ("v2" , v2r , v1C )
471+ case <- innerCtx .Done ():
472+ return nil , "" , ctx .Err ()
473+ }
474+ }
475+
476+ func (s * Service ) serveReference (logger log.Logger , address swarm.Address , pathVar string , w http.ResponseWriter , r * http.Request , headerOnly bool ) {
384477 loggerV1 := logger .V (1 ).Build ()
385478
386479 headers := struct {
@@ -415,7 +508,6 @@ func (s *Service) serveReference(logger log.Logger, address swarm.Address, pathV
415508 jsonhttp .BadRequest (w , "could not parse headers" )
416509 return
417510 }
418-
419511FETCH:
420512 // read manifest entry
421513 m , err := manifest .NewDefaultManifestReference (
@@ -449,7 +541,8 @@ FETCH:
449541 jsonhttp .NotFound (w , "no update found" )
450542 return
451543 }
452- wc , err := feeds .GetWrappedChunk (ctx , s .storer .Download (cache ), ch , feedLegacyResolve )
544+
545+ wc , feedVer , err := s .resolveFeed (ctx , s .storer .Download (cache ), ch )
453546 if err != nil {
454547 if errors .Is (err , feeds .ErrNotLegacyPayload ) {
455548 logger .Debug ("bzz: download: feed is not a legacy payload" )
@@ -468,10 +561,10 @@ FETCH:
468561 jsonhttp .InternalServerError (w , "mapStructure feed update" )
469562 return
470563 }
564+
471565 address = wc .Address ()
472566 // modify ls and init with non-existing wrapped chunk
473567 ls = loadsave .NewReadonlyWithRootCh (s .storer .Download (cache ), s .storer .Cache (), wc , rLevel )
474-
475568 feedDereferenced = true
476569 curBytes , err := cur .MarshalBinary ()
477570 if err != nil {
@@ -482,6 +575,7 @@ FETCH:
482575 }
483576
484577 w .Header ().Set (SwarmFeedIndexHeader , hex .EncodeToString (curBytes ))
578+ w .Header ().Set (SwarmFeedResolvedVersionHeader , feedVer )
485579 // this header might be overriding others. handle with care. in the future
486580 // we should implement an append functionality for this specific header,
487581 // since different parts of handlers might be overriding others' values
@@ -490,7 +584,6 @@ FETCH:
490584 goto FETCH
491585 }
492586 }
493-
494587 if pathVar == "" {
495588 loggerV1 .Debug ("bzz download: handle empty path" , "address" , address )
496589
@@ -505,6 +598,7 @@ FETCH:
505598 return
506599 }
507600 }
601+
508602 logger .Debug ("bzz download: address not found or incorrect" , "address" , address , "path" , pathVar )
509603 logger .Error (nil , "address not found or incorrect" )
510604 jsonhttp .NotFound (w , "address not found or incorrect" )
0 commit comments