[#95] Supported get match headers

Supported If-Match and If-None-Match.

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2021-07-02 19:05:43 +03:00
parent daed0978a6
commit 568d7ac810
3 changed files with 130 additions and 7 deletions

View file

@ -16,6 +16,8 @@ import (
type getObjectArgs struct {
IfModifiedSince *time.Time
IfUnmodifiedSince *time.Time
IfMatch string
IfNoneMatch string
}
func fetchRangeHeader(headers http.Header, fullSize uint64) (*layer.RangeParams, error) {
@ -90,12 +92,9 @@ func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
return
}
if args.IfModifiedSince != nil && inf.Created.Before(*args.IfModifiedSince) {
w.WriteHeader(http.StatusNotModified)
return
}
if args.IfUnmodifiedSince != nil && inf.Created.After(*args.IfUnmodifiedSince) {
w.WriteHeader(http.StatusPreconditionFailed)
status := checkGetPreconditions(inf, args)
if status != http.StatusOK {
w.WriteHeader(status)
return
}
@ -119,9 +118,31 @@ func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
}
}
func checkGetPreconditions(inf *layer.ObjectInfo, args *getObjectArgs) int {
if len(args.IfMatch) > 0 && args.IfMatch != inf.HashSum {
return http.StatusPreconditionFailed
}
if len(args.IfNoneMatch) > 0 && args.IfNoneMatch == inf.HashSum {
return http.StatusNotModified
}
if args.IfModifiedSince != nil && inf.Created.Before(*args.IfModifiedSince) {
return http.StatusNotModified
}
if args.IfUnmodifiedSince != nil && inf.Created.After(*args.IfUnmodifiedSince) {
if len(args.IfMatch) == 0 {
return http.StatusPreconditionFailed
}
}
return http.StatusOK
}
func parseGetObjectArgs(headers http.Header) (*getObjectArgs, error) {
var err error
args := &getObjectArgs{}
args := &getObjectArgs{
IfMatch: headers.Get(api.IfMatch),
IfNoneMatch: headers.Get(api.IfNoneMatch),
}
if args.IfModifiedSince, err = parseHTTPTime(headers.Get(api.IfModifiedSince)); err != nil {
return nil, err

View file

@ -3,6 +3,7 @@ package handler
import (
"net/http"
"testing"
"time"
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
"github.com/stretchr/testify/require"
@ -39,3 +40,102 @@ func TestFetchRangeHeader(t *testing.T) {
require.Equal(t, tc.expected, params)
}
}
func newInfo(etag string, created time.Time) *layer.ObjectInfo {
return &layer.ObjectInfo{
HashSum: etag,
Created: created,
}
}
func TestPreconditions(t *testing.T) {
today := time.Now()
yesterday := today.Add(-24 * time.Hour)
etag := "etag"
etag2 := "etag2"
for _, tc := range []struct {
name string
info *layer.ObjectInfo
args *getObjectArgs
expected int
}{
{
name: "no conditions",
info: new(layer.ObjectInfo),
args: new(getObjectArgs),
expected: http.StatusOK,
},
{
name: "IfMatch true",
info: newInfo(etag, today),
args: &getObjectArgs{IfMatch: etag},
expected: http.StatusOK,
},
{
name: "IfMatch false",
info: newInfo(etag, today),
args: &getObjectArgs{IfMatch: etag2},
expected: http.StatusPreconditionFailed},
{
name: "IfNoneMatch true",
info: newInfo(etag, today),
args: &getObjectArgs{IfNoneMatch: etag2},
expected: http.StatusOK},
{
name: "IfNoneMatch false",
info: newInfo(etag, today),
args: &getObjectArgs{IfNoneMatch: etag},
expected: http.StatusNotModified},
{
name: "IfModifiedSince true",
info: newInfo(etag, today),
args: &getObjectArgs{IfModifiedSince: &yesterday},
expected: http.StatusOK},
{
name: "IfModifiedSince false",
info: newInfo(etag, yesterday),
args: &getObjectArgs{IfModifiedSince: &today},
expected: http.StatusNotModified},
{
name: "IfUnmodifiedSince true",
info: newInfo(etag, yesterday),
args: &getObjectArgs{IfUnmodifiedSince: &today},
expected: http.StatusOK},
{
name: "IfUnmodifiedSince false",
info: newInfo(etag, today),
args: &getObjectArgs{IfUnmodifiedSince: &yesterday},
expected: http.StatusPreconditionFailed},
{
name: "IfMatch true, IfUnmodifiedSince false",
info: newInfo(etag, today),
args: &getObjectArgs{IfMatch: etag, IfUnmodifiedSince: &yesterday},
expected: http.StatusOK,
},
{
name: "IfMatch false, IfUnmodifiedSince true",
info: newInfo(etag, yesterday),
args: &getObjectArgs{IfMatch: etag2, IfUnmodifiedSince: &today},
expected: http.StatusPreconditionFailed,
},
{
name: "IfNoneMatch false, IfModifiedSince true",
info: newInfo(etag, today),
args: &getObjectArgs{IfNoneMatch: etag, IfModifiedSince: &yesterday},
expected: http.StatusNotModified,
},
{
name: "IfNoneMatch true, IfModifiedSince false",
info: newInfo(etag, yesterday),
args: &getObjectArgs{IfNoneMatch: etag2, IfModifiedSince: &today},
expected: http.StatusNotModified,
},
} {
t.Run(tc.name, func(t *testing.T) {
actual := checkGetPreconditions(tc.info, tc.args)
require.Equal(t, tc.expected, actual)
})
}
}

View file

@ -24,6 +24,8 @@ const (
Action = "Action"
IfModifiedSince = "If-Modified-Since"
IfUnmodifiedSince = "If-Unmodified-Since"
IfMatch = "If-Match"
IfNoneMatch = "If-None-Match"
AmzCopyIfModifiedSince = "X-Amz-Copy-Source-If-Modified-Since"
AmzCopyIfUnmodifiedSince = "X-Amz-Copy-Source-If-Unmodified-Since"