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:
commit
22faaadc32
4 changed files with 31 additions and 26 deletions
|
@ -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.",
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue