serve restic: fix error handling

* serve restic: return internal error if listing failed

If listing a remote failed, then rclone returned http status "not
found". This has become a problem since restic 0.16.0 which ignores "not
found"-errors while listing a directory.

Just return internal server error, if something unexpected happens while
listing a directory.

* serve restic: fix error handling if getting a file fails

If the call to `newObject` in `serveObject` fails, then rclone always
returned a "not found" error. This prevents restic from distinguishing
permanent "not found" errors from everything else.

Thus, only return "not found" if the object is not found and an internal
server error otherwise.
This commit is contained in:
Michael Eischer authored and GitHub committed 2024-01-29 17:54:23 +00:00
1 parent 6e4dd2ab96
commit ef2c5a1998
2 files changed
+63 -3

No files matched your search

+7 -3
View File
@@ -341,7 +341,11 @@ func (s *server) serveObject(w http.ResponseWriter, r *http.Request) {
o, err := s.newObject(r.Context(), remote)
if err != nil {
fs.Debugf(remote, "%s request error: %v", r.Method, err)
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
if errors.Is(err, fs.ErrorObjectNotFound) {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
} else {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
return
}
serve.Object(w, r, o)
@@ -404,7 +408,7 @@ func (s *server) deleteObject(w http.ResponseWriter, r *http.Request) {
if err := o.Remove(r.Context()); err != nil {
fs.Errorf(remote, "Delete request remove error: %v", err)
if err == fs.ErrorObjectNotFound {
if errors.Is(err, fs.ErrorObjectNotFound) {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
} else {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@@ -466,7 +470,7 @@ func (s *server) listObjects(w http.ResponseWriter, r *http.Request) {
if err != nil {
if !errors.Is(err, fs.ErrorDirNotFound) {
fs.Errorf(remote, "list failed: %#v %T", err, err)
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
}
+56
View File
@@ -5,6 +5,7 @@ package restic
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"os"
@@ -12,6 +13,8 @@ import (
"testing"
_ "github.com/rclone/rclone/backend/all"
"github.com/rclone/rclone/cmd"
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fstest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -114,3 +117,56 @@ func TestMakeRemote(t *testing.T) {
got.ServeHTTP(w, r)
}
}
type listErrorFs struct {
fs.Fs
}
func (f *listErrorFs) List(ctx context.Context, dir string) (entries fs.DirEntries, err error) {
return fs.DirEntries{}, errors.New("oops")
}
func TestListErrors(t *testing.T) {
ctx := context.Background()
// setup rclone with a local backend in a temporary directory
tempdir := t.TempDir()
opt := newOpt()
// make a new file system in the temp dir
f := &listErrorFs{Fs: cmd.NewFsSrc([]string{tempdir})}
s, err := newServer(ctx, f, &opt)
require.NoError(t, err)
router := s.Server.Router()
req := newRequest(t, "GET", "/test/snapshots/", nil)
checkRequest(t, router.ServeHTTP, req, []wantFunc{wantCode(http.StatusInternalServerError)})
}
type newObjectErrorFs struct {
fs.Fs
err error
}
func (f *newObjectErrorFs) NewObject(ctx context.Context, remote string) (fs.Object, error) {
return nil, f.err
}
func TestServeErrors(t *testing.T) {
ctx := context.Background()
// setup rclone with a local backend in a temporary directory
tempdir := t.TempDir()
opt := newOpt()
// make a new file system in the temp dir
f := &newObjectErrorFs{Fs: cmd.NewFsSrc([]string{tempdir})}
s, err := newServer(ctx, f, &opt)
require.NoError(t, err)
router := s.Server.Router()
f.err = errors.New("oops")
req := newRequest(t, "GET", "/test/config", nil)
checkRequest(t, router.ServeHTTP, req, []wantFunc{wantCode(http.StatusInternalServerError)})
f.err = fs.ErrorObjectNotFound
checkRequest(t, router.ServeHTTP, req, []wantFunc{wantCode(http.StatusNotFound)})
}