From b4895ebd76225b41866dba121d4bde0c37376837 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sat, 11 May 2024 00:03:42 +0200 Subject: [PATCH] rest: rework error reporting and report too short files --- internal/backend/rest/rest.go | 72 ++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/internal/backend/rest/rest.go b/internal/backend/rest/rest.go index d8171d90e..5b59b8e4f 100644 --- a/internal/backend/rest/rest.go +++ b/internal/backend/rest/rest.go @@ -30,6 +30,20 @@ type Backend struct { layout.Layout } +// restError is returned whenever the server returns a non-successful HTTP status. +type restError struct { + backend.Handle + StatusCode int + Status string +} + +func (e *restError) Error() string { + if e.StatusCode == http.StatusNotFound && e.Handle.Type.String() != "invalid" { + return fmt.Sprintf("%v does not exist", e.Handle) + } + return fmt.Sprintf("unexpected HTTP response (%v): %v", e.StatusCode, e.Status) +} + func NewFactory() location.Factory { return location.NewHTTPBackendFactory("rest", ParseConfig, StripPassword, Create, Open) } @@ -96,7 +110,7 @@ func Create(ctx context.Context, cfg Config, rt http.RoundTripper) (*Backend, er } if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("server response unexpected: %v (%v)", resp.Status, resp.StatusCode) + return nil, &restError{backend.Handle{}, resp.StatusCode, resp.Status} } return be, nil @@ -150,26 +164,31 @@ func (b *Backend) Save(ctx context.Context, h backend.Handle, rd backend.RewindR } if resp.StatusCode != http.StatusOK { - return errors.Errorf("server response unexpected: %v (%v)", resp.Status, resp.StatusCode) + return &restError{h, resp.StatusCode, resp.Status} } return nil } -// notExistError is returned whenever the requested file does not exist on the -// server. -type notExistError struct { - backend.Handle -} - -func (e *notExistError) Error() string { - return fmt.Sprintf("%v does not exist", e.Handle) -} - // IsNotExist returns true if the error was caused by a non-existing file. func (b *Backend) IsNotExist(err error) bool { - var e *notExistError - return errors.As(err, &e) + var e *restError + return errors.As(err, &e) && e.StatusCode == http.StatusNotFound +} + +func (b *Backend) IsPermanentError(err error) bool { + if b.IsNotExist(err) { + return true + } + + var rerr *restError + if errors.As(err, &rerr) { + if rerr.StatusCode == http.StatusRequestedRangeNotSatisfiable || rerr.StatusCode == http.StatusUnauthorized || rerr.StatusCode == http.StatusForbidden { + return true + } + } + + return false } // Load runs fn with a reader that yields the contents of the file at h at the @@ -221,14 +240,13 @@ func (b *Backend) openReader(ctx context.Context, h backend.Handle, length int, return nil, errors.Wrap(err, "client.Do") } - if resp.StatusCode == http.StatusNotFound { - _ = drainAndClose(resp) - return nil, ¬ExistError{h} - } - if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent { _ = drainAndClose(resp) - return nil, errors.Errorf("unexpected HTTP response (%v): %v", resp.StatusCode, resp.Status) + return nil, &restError{h, resp.StatusCode, resp.Status} + } + + if length > 0 && resp.ContentLength != int64(length) { + return nil, &restError{h, http.StatusRequestedRangeNotSatisfiable, "partial out of bounds read"} } return resp.Body, nil @@ -251,12 +269,8 @@ func (b *Backend) Stat(ctx context.Context, h backend.Handle) (backend.FileInfo, return backend.FileInfo{}, err } - if resp.StatusCode == http.StatusNotFound { - return backend.FileInfo{}, ¬ExistError{h} - } - if resp.StatusCode != http.StatusOK { - return backend.FileInfo{}, errors.Errorf("unexpected HTTP response (%v): %v", resp.StatusCode, resp.Status) + return backend.FileInfo{}, &restError{h, resp.StatusCode, resp.Status} } if resp.ContentLength < 0 { @@ -288,12 +302,8 @@ func (b *Backend) Remove(ctx context.Context, h backend.Handle) error { return err } - if resp.StatusCode == http.StatusNotFound { - return ¬ExistError{h} - } - if resp.StatusCode != http.StatusOK { - return errors.Errorf("blob not removed, server response: %v (%v)", resp.Status, resp.StatusCode) + return &restError{h, resp.StatusCode, resp.Status} } return nil @@ -330,7 +340,7 @@ func (b *Backend) List(ctx context.Context, t backend.FileType, fn func(backend. if resp.StatusCode != http.StatusOK { _ = drainAndClose(resp) - return errors.Errorf("List failed, server response: %v (%v)", resp.Status, resp.StatusCode) + return &restError{backend.Handle{Type: t}, resp.StatusCode, resp.Status} } if resp.Header.Get("Content-Type") == ContentTypeV2 {