Skip to content
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
14 changes: 14 additions & 0 deletions pkg/api/bytes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/ethersphere/bee/v2/pkg/log"
mockbatchstore "github.com/ethersphere/bee/v2/pkg/postage/batchstore/mock"
mockpost "github.com/ethersphere/bee/v2/pkg/postage/mock"
"github.com/ethersphere/bee/v2/pkg/storage/inmemchunkstore"
mockstorer "github.com/ethersphere/bee/v2/pkg/storer/mock"
"github.com/ethersphere/bee/v2/pkg/swarm"
"gitlab.com/nolash/go-mockbytes"
Expand Down Expand Up @@ -622,3 +623,16 @@ func TestBytesHeadErrorsMatchGet(t *testing.T) {
})
}
}

// TestBytesGetHandlerNoPeers verifies that a retrieval failure caused by having no available peers
func TestBytesGetHandlerNoPeers(t *testing.T) {
t.Parallel()

client, _, _, _ := newTestServer(t, testServerOptions{
Storer: mockstorer.NewWithChunkStore(noPeersChunkStore{inmemchunkstore.New()}),
Post: mockpost.New(mockpost.WithAcceptAll()),
})

ref := swarm.MustParseHexAddress("0000000000000000000000000000000000000000000000000000000000000001")
jsonhttptest.Request(t, client, http.MethodGet, "/bytes/"+ref.String(), http.StatusServiceUnavailable)
}
10 changes: 9 additions & 1 deletion pkg/api/bzz.go
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,8 @@ FETCH:
}

jsonhttp.NotFound(w, "path address not found")
} else if errors.Is(err, topology.ErrNotFound) {
jsonhttp.ServiceUnavailable(w, "content could not be retrieved because no usable peer was available")
} else {
jsonhttp.NotFound(w, nil)
}
Expand Down Expand Up @@ -768,12 +770,18 @@ func (s *Service) downloadHandler(logger log.Logger, w http.ResponseWriter, r *h
reader, l, err = joiner.New(ctx, s.storer.Download(cache), s.storer.Cache(), reference, rLevel)
}
if err != nil {
if errors.Is(err, storage.ErrNotFound) || errors.Is(err, topology.ErrNotFound) {
if errors.Is(err, storage.ErrNotFound) {
logger.Debug("api download: not found ", "address", reference, "error", err)
logger.Error(nil, err.Error())
jsonhttp.NotFound(w, nil)
return
}
if errors.Is(err, topology.ErrNotFound) {
logger.Debug("api download: no peers available", "address", reference, "error", err)
logger.Error(nil, err.Error())
jsonhttp.ServiceUnavailable(w, "content could not be retrieved because no usable peer was available")
return
}
logger.Debug("api download: unexpected error", "address", reference, "error", err)
logger.Error(nil, "api download: unexpected error")
jsonhttp.InternalServerError(w, "joiner failed")
Expand Down
16 changes: 10 additions & 6 deletions pkg/api/chunk.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/ethersphere/bee/v2/pkg/postage"
"github.com/ethersphere/bee/v2/pkg/storage"
"github.com/ethersphere/bee/v2/pkg/swarm"
"github.com/ethersphere/bee/v2/pkg/topology"
"github.com/gorilla/mux"
)

Expand Down Expand Up @@ -256,15 +257,18 @@ func (s *Service) chunkGetHandler(w http.ResponseWriter, r *http.Request) {

chunk, err := s.storer.Download(cache).Get(r.Context(), address)
if err != nil {
if errors.Is(err, storage.ErrNotFound) {
switch {
case errors.Is(err, storage.ErrNotFound):
loggerV1.Debug("chunk not found", "address", address)
jsonhttp.NotFound(w, "chunk not found")
return

case errors.Is(err, topology.ErrNotFound):
loggerV1.Debug("no peers available to retrieve chunk", "address", address)
jsonhttp.ServiceUnavailable(w, "chunk could not be retrieved because no usable peer was available")
default:
logger.Debug("read chunk failed", "chunk_address", address, "error", err)
logger.Error(nil, "read chunk failed")
jsonhttp.InternalServerError(w, "read chunk failed")
}
logger.Debug("read chunk failed", "chunk_address", address, "error", err)
logger.Error(nil, "read chunk failed")
jsonhttp.InternalServerError(w, "read chunk failed")
return
}
w.Header().Set(ContentTypeHeader, "binary/octet-stream")
Expand Down
28 changes: 28 additions & 0 deletions pkg/api/chunk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import (
mockbatchstore "github.com/ethersphere/bee/v2/pkg/postage/batchstore/mock"
mockpost "github.com/ethersphere/bee/v2/pkg/postage/mock"
"github.com/ethersphere/bee/v2/pkg/spinlock"
"github.com/ethersphere/bee/v2/pkg/storage/inmemchunkstore"
mockstorer "github.com/ethersphere/bee/v2/pkg/storer/mock"
"github.com/ethersphere/bee/v2/pkg/topology"

"github.com/ethersphere/bee/v2/pkg/api"
"github.com/ethersphere/bee/v2/pkg/jsonhttp"
Expand All @@ -30,6 +32,17 @@ import (
"github.com/ethersphere/bee/v2/pkg/swarm"
)

// noPeersChunkStore simulates a netstore whose network retrieval failed
// because no peers were available, as opposed to the chunk genuinely
// not existing anywhere.
type noPeersChunkStore struct {
*inmemchunkstore.ChunkStore
}

func (noPeersChunkStore) Get(context.Context, swarm.Address) (swarm.Chunk, error) {
return nil, topology.ErrNotFound
}

// nolint:paralleltest,tparallel
// TestChunkUploadDownload uploads a chunk to an API that verifies the chunk according
// to a given validator, then tries to download the uploaded data.
Expand Down Expand Up @@ -138,6 +151,21 @@ func TestChunkHasHandler(t *testing.T) {
})
}

// nolint:paralleltest,tparallel
func TestChunkGetHandlerNoPeers(t *testing.T) {
storerMock := mockstorer.NewWithChunkStore(noPeersChunkStore{inmemchunkstore.New()})
testServer, _, _, _ := newTestServer(t, testServerOptions{
Storer: storerMock,
})

jsonhttptest.Request(t, testServer, http.MethodGet, "/chunks/"+swarm.MustParseHexAddress("aabbcc").String(), http.StatusServiceUnavailable,
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: "chunk could not be retrieved because no usable peer was available",
Code: http.StatusServiceUnavailable,
}),
)
}

func TestChunkHandlersInvalidInputs(t *testing.T) {
t.Parallel()

Expand Down
Loading