Merge pull request #161 from nspcc-dev/feature/158-s3_tests_conditional_headers

[#158] Handled s3 errors on conditional headers
This commit is contained in:
Alex Vanin 2021-07-16 09:39:17 +03:00 committed by GitHub
commit 22faaadc32
4 changed files with 31 additions and 26 deletions

View file

@ -62,6 +62,7 @@ const (
ErrNoSuchVersion ErrNoSuchVersion
ErrNotImplemented ErrNotImplemented
ErrPreconditionFailed ErrPreconditionFailed
ErrNotModified
ErrRequestTimeTooSkewed ErrRequestTimeTooSkewed
ErrSignatureDoesNotMatch ErrSignatureDoesNotMatch
ErrMethodNotAllowed ErrMethodNotAllowed
@ -494,6 +495,11 @@ var errorCodes = errorCodeMap{
Description: "At least one of the pre-conditions you specified did not hold", Description: "At least one of the pre-conditions you specified did not hold",
HTTPStatusCode: http.StatusPreconditionFailed, HTTPStatusCode: http.StatusPreconditionFailed,
}, },
ErrNotModified: {
Code: "NotModified",
Description: "The resource was not changed.",
HTTPStatusCode: http.StatusNotModified,
},
ErrRequestTimeTooSkewed: { ErrRequestTimeTooSkewed: {
Code: "RequestTimeTooSkewed", Code: "RequestTimeTooSkewed",
Description: "The difference between the request time and the server's time is too large.", Description: "The difference between the request time and the server's time is too large.",

View file

@ -73,9 +73,8 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
status := checkPreconditions(inf, args.Conditional) if err = checkPreconditions(inf, args.Conditional); err != nil {
if status != http.StatusOK { api.WriteErrorResponse(r.Context(), w, api.GetAPIError(api.ErrPreconditionFailed), r.URL)
w.WriteHeader(status)
return return
} }

View file

@ -96,9 +96,8 @@ func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
status := checkPreconditions(inf, args.Conditional) if err = checkPreconditions(inf, args.Conditional); err != nil {
if status != http.StatusOK { api.WriteErrorResponse(r.Context(), w, err, r.URL)
w.WriteHeader(status)
return return
} }
@ -122,23 +121,23 @@ func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
} }
} }
func checkPreconditions(inf *layer.ObjectInfo, args *conditionalArgs) int { func checkPreconditions(inf *layer.ObjectInfo, args *conditionalArgs) error {
if len(args.IfMatch) > 0 && args.IfMatch != inf.HashSum { if len(args.IfMatch) > 0 && args.IfMatch != inf.HashSum {
return http.StatusPreconditionFailed return api.GetAPIError(api.ErrPreconditionFailed)
} }
if len(args.IfNoneMatch) > 0 && args.IfNoneMatch == inf.HashSum { if len(args.IfNoneMatch) > 0 && args.IfNoneMatch == inf.HashSum {
return http.StatusNotModified return api.GetAPIError(api.ErrNotModified)
} }
if args.IfModifiedSince != nil && inf.Created.Before(*args.IfModifiedSince) { if args.IfModifiedSince != nil && inf.Created.Before(*args.IfModifiedSince) {
return http.StatusNotModified return api.GetAPIError(api.ErrNotModified)
} }
if args.IfUnmodifiedSince != nil && inf.Created.After(*args.IfUnmodifiedSince) { if args.IfUnmodifiedSince != nil && inf.Created.After(*args.IfUnmodifiedSince) {
if len(args.IfMatch) == 0 { if len(args.IfMatch) == 0 {
return http.StatusPreconditionFailed return api.GetAPIError(api.ErrPreconditionFailed)
} }
} }
return http.StatusOK return nil
} }
func parseGetObjectArgs(headers http.Header) (*getObjectArgs, error) { func parseGetObjectArgs(headers http.Header) (*getObjectArgs, error) {

View file

@ -5,6 +5,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/nspcc-dev/neofs-s3-gw/api"
"github.com/nspcc-dev/neofs-s3-gw/api/layer" "github.com/nspcc-dev/neofs-s3-gw/api/layer"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -58,79 +59,79 @@ func TestPreconditions(t *testing.T) {
name string name string
info *layer.ObjectInfo info *layer.ObjectInfo
args *conditionalArgs args *conditionalArgs
expected int expected error
}{ }{
{ {
name: "no conditions", name: "no conditions",
info: new(layer.ObjectInfo), info: new(layer.ObjectInfo),
args: new(conditionalArgs), args: new(conditionalArgs),
expected: http.StatusOK, expected: nil,
}, },
{ {
name: "IfMatch true", name: "IfMatch true",
info: newInfo(etag, today), info: newInfo(etag, today),
args: &conditionalArgs{IfMatch: etag}, args: &conditionalArgs{IfMatch: etag},
expected: http.StatusOK, expected: nil,
}, },
{ {
name: "IfMatch false", name: "IfMatch false",
info: newInfo(etag, today), info: newInfo(etag, today),
args: &conditionalArgs{IfMatch: etag2}, args: &conditionalArgs{IfMatch: etag2},
expected: http.StatusPreconditionFailed}, expected: api.GetAPIError(api.ErrPreconditionFailed)},
{ {
name: "IfNoneMatch true", name: "IfNoneMatch true",
info: newInfo(etag, today), info: newInfo(etag, today),
args: &conditionalArgs{IfNoneMatch: etag2}, args: &conditionalArgs{IfNoneMatch: etag2},
expected: http.StatusOK}, expected: nil},
{ {
name: "IfNoneMatch false", name: "IfNoneMatch false",
info: newInfo(etag, today), info: newInfo(etag, today),
args: &conditionalArgs{IfNoneMatch: etag}, args: &conditionalArgs{IfNoneMatch: etag},
expected: http.StatusNotModified}, expected: api.GetAPIError(api.ErrNotModified)},
{ {
name: "IfModifiedSince true", name: "IfModifiedSince true",
info: newInfo(etag, today), info: newInfo(etag, today),
args: &conditionalArgs{IfModifiedSince: &yesterday}, args: &conditionalArgs{IfModifiedSince: &yesterday},
expected: http.StatusOK}, expected: nil},
{ {
name: "IfModifiedSince false", name: "IfModifiedSince false",
info: newInfo(etag, yesterday), info: newInfo(etag, yesterday),
args: &conditionalArgs{IfModifiedSince: &today}, args: &conditionalArgs{IfModifiedSince: &today},
expected: http.StatusNotModified}, expected: api.GetAPIError(api.ErrNotModified)},
{ {
name: "IfUnmodifiedSince true", name: "IfUnmodifiedSince true",
info: newInfo(etag, yesterday), info: newInfo(etag, yesterday),
args: &conditionalArgs{IfUnmodifiedSince: &today}, args: &conditionalArgs{IfUnmodifiedSince: &today},
expected: http.StatusOK}, expected: nil},
{ {
name: "IfUnmodifiedSince false", name: "IfUnmodifiedSince false",
info: newInfo(etag, today), info: newInfo(etag, today),
args: &conditionalArgs{IfUnmodifiedSince: &yesterday}, args: &conditionalArgs{IfUnmodifiedSince: &yesterday},
expected: http.StatusPreconditionFailed}, expected: api.GetAPIError(api.ErrPreconditionFailed)},
{ {
name: "IfMatch true, IfUnmodifiedSince false", name: "IfMatch true, IfUnmodifiedSince false",
info: newInfo(etag, today), info: newInfo(etag, today),
args: &conditionalArgs{IfMatch: etag, IfUnmodifiedSince: &yesterday}, args: &conditionalArgs{IfMatch: etag, IfUnmodifiedSince: &yesterday},
expected: http.StatusOK, expected: nil,
}, },
{ {
name: "IfMatch false, IfUnmodifiedSince true", name: "IfMatch false, IfUnmodifiedSince true",
info: newInfo(etag, yesterday), info: newInfo(etag, yesterday),
args: &conditionalArgs{IfMatch: etag2, IfUnmodifiedSince: &today}, args: &conditionalArgs{IfMatch: etag2, IfUnmodifiedSince: &today},
expected: http.StatusPreconditionFailed, expected: api.GetAPIError(api.ErrPreconditionFailed),
}, },
{ {
name: "IfNoneMatch false, IfModifiedSince true", name: "IfNoneMatch false, IfModifiedSince true",
info: newInfo(etag, today), info: newInfo(etag, today),
args: &conditionalArgs{IfNoneMatch: etag, IfModifiedSince: &yesterday}, args: &conditionalArgs{IfNoneMatch: etag, IfModifiedSince: &yesterday},
expected: http.StatusNotModified, expected: api.GetAPIError(api.ErrNotModified),
}, },
{ {
name: "IfNoneMatch true, IfModifiedSince false", name: "IfNoneMatch true, IfModifiedSince false",
info: newInfo(etag, yesterday), info: newInfo(etag, yesterday),
args: &conditionalArgs{IfNoneMatch: etag2, IfModifiedSince: &today}, args: &conditionalArgs{IfNoneMatch: etag2, IfModifiedSince: &today},
expected: http.StatusNotModified, expected: api.GetAPIError(api.ErrNotModified),
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {