diff --git a/pkg/api/api.go b/pkg/api/api.go index e3419ccdb38..418744ad621 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -43,6 +43,7 @@ import ( "github.com/ethersphere/bee/v2/pkg/pss" "github.com/ethersphere/bee/v2/pkg/resolver" "github.com/ethersphere/bee/v2/pkg/resolver/client/ens" + "github.com/ethersphere/bee/v2/pkg/resolver/multiresolver" "github.com/ethersphere/bee/v2/pkg/sctx" "github.com/ethersphere/bee/v2/pkg/settlement" "github.com/ethersphere/bee/v2/pkg/settlement/swap" @@ -109,9 +110,8 @@ const ( ) const ( - multiPartFormData = "multipart/form-data" - contentTypeTar = "application/x-tar" - boolHeaderSetValue = "true" + multiPartFormData = "multipart/form-data" + contentTypeTar = "application/x-tar" ) var ( @@ -449,6 +449,10 @@ func (s *Service) resolveNameOrAddress(str string) (swarm.Address, error) { return addr, nil } + if errors.Is(err, multiresolver.ErrResolverService) || errors.Is(err, resolver.ErrServiceNotAvailable) { + return swarm.ZeroAddress, err + } + return swarm.ZeroAddress, fmt.Errorf("%w: %w", errInvalidNameOrAddress, err) } @@ -651,13 +655,23 @@ func (s *Service) mapStructure(input, output interface{}) func(string, log.Logge Message: msg, Code: http.StatusBadRequest, } + hasServiceUnavailable := false for _, err := range merr.Errors { + if errors.Is(err, resolver.ErrServiceNotAvailable) { + hasServiceUnavailable = true + resp.Reasons = append(resp.Reasons, jsonhttp.Reason{ + Field: "address", + Error: err.Error(), + }) + continue + } var perr *parseError if errors.As(err, &perr) { resp.Reasons = append(resp.Reasons, jsonhttp.Reason{ Field: perr.Entry, Error: perr.Cause.Error(), }) + continue } var verr *validationError if errors.As(err, &verr) { @@ -667,7 +681,14 @@ func (s *Service) mapStructure(input, output interface{}) func(string, log.Logge }) } } - jsonhttp.BadRequest(w, resp) + + if hasServiceUnavailable { + resp.Message = "service unavailable" + resp.Code = http.StatusServiceUnavailable + jsonhttp.ServiceUnavailable(w, resp) + } else { + jsonhttp.BadRequest(w, resp) + } } } diff --git a/pkg/resolver/client/ens/ens.go b/pkg/resolver/client/ens/ens.go index 38c7432e979..b921ad8c9b7 100644 --- a/pkg/resolver/client/ens/ens.go +++ b/pkg/resolver/client/ens/ens.go @@ -167,10 +167,10 @@ func wrapDial(endpoint, contractAddr string) (*ethclient.Client, *goens.Registry } func wrapResolve(registry *goens.Registry, _ common.Address, name string) (string, error) { - // Ensure the name is registered. ownerAddress, err := registry.Owner(name) + // it returns error only if the service is not available if err != nil { - return "", fmt.Errorf("owner: %w: %w", err, resolver.ErrNotFound) + return "", fmt.Errorf("%w: %w", resolver.ErrServiceNotAvailable, err) } // If the name is not registered, return an error. @@ -181,12 +181,16 @@ func wrapResolve(registry *goens.Registry, _ common.Address, name string) (strin // Obtain the resolver for this domain name. ensR, err := registry.Resolver(name) if err != nil { - return "", fmt.Errorf("resolver: %w: %w", err, resolver.ErrServiceNotAvailable) + return "", fmt.Errorf("%w: %w", resolver.ErrServiceNotAvailable, err) } // Try and read out the content hash record. ch, err := ensR.Contenthash() if err != nil { + // Check if it's a service error (rate limiting, network issues) + if strings.Contains(err.Error(), "429") || strings.Contains(err.Error(), "rate limit") { + return "", fmt.Errorf("%w: %w", resolver.ErrServiceNotAvailable, err) + } return "", fmt.Errorf("contenthash: %w: %w", err, resolver.ErrInvalidContentHash) } diff --git a/pkg/resolver/multiresolver/multiresolver.go b/pkg/resolver/multiresolver/multiresolver.go index e91f5155f28..87cfda56d16 100644 --- a/pkg/resolver/multiresolver/multiresolver.go +++ b/pkg/resolver/multiresolver/multiresolver.go @@ -14,6 +14,7 @@ import ( "github.com/ethersphere/bee/v2/pkg/resolver" "github.com/ethersphere/bee/v2/pkg/resolver/cidv1" "github.com/ethersphere/bee/v2/pkg/resolver/client/ens" + "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/hashicorp/go-multierror" ) @@ -35,6 +36,8 @@ var ( ErrResolverChainFailed = errors.New("resolver chain failed") // ErrCloseFailed denotes that closing the multiresolver failed. ErrCloseFailed = errors.New("close failed") + // ErrResolverService denotes that no resolver service is configured for the requested name or the resolver service is not available. + ErrResolverService = errors.New("cannot communicate with the resolver or no resolver service configured") ) type resolverMap map[string][]resolver.Interface @@ -160,6 +163,9 @@ func (mr *MultiResolver) Resolve(name string) (addr resolver.Address, err error) // If no resolver chain is found, switch to the default chain. if len(chain) == 0 { chain = mr.resolvers[""] + if len(chain) == 1 && tld != "" { // only the CID resolver is defined + return swarm.ZeroAddress, resolver.ErrServiceNotAvailable + } } var errs *multierror.Error diff --git a/pkg/resolver/multiresolver/multiresolver_test.go b/pkg/resolver/multiresolver/multiresolver_test.go index c1f9ba0311a..8c242004696 100644 --- a/pkg/resolver/multiresolver/multiresolver_test.go +++ b/pkg/resolver/multiresolver/multiresolver_test.go @@ -217,7 +217,7 @@ func TestResolve(t *testing.T) { }, { // Switch to the default chain: - name: "this.empty", + name: "defaultChainIsCidTriggerItWithoutTld", wantAdr: addr, }, { diff --git a/pkg/resolver/resolver.go b/pkg/resolver/resolver.go index 043dcaf87e0..e289d454671 100644 --- a/pkg/resolver/resolver.go +++ b/pkg/resolver/resolver.go @@ -20,7 +20,7 @@ var ( // ErrNotFound denotes that given name was not found ErrNotFound = errors.New("not found") // ErrServiceNotAvailable denotes that remote ENS service is not available - ErrServiceNotAvailable = errors.New("not available") + ErrServiceNotAvailable = errors.New("ENS service is not available") // ErrInvalidContentHash denotes that the value of the response contenthash record is not valid. ErrInvalidContentHash = errors.New("invalid swarm content hash") )