[#94] GetObject support conditional headers

Supported If-Modified-Since and If-Unmodified-Since.

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2021-06-30 18:10:11 +03:00
parent 7ebcd5af8f
commit da4eca5da5
2 changed files with 45 additions and 1 deletions

View file

@ -5,6 +5,7 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/nspcc-dev/neofs-s3-gw/api" "github.com/nspcc-dev/neofs-s3-gw/api"
@ -12,6 +13,11 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
) )
type getObjectArgs struct {
IfModifiedSince time.Time
IfUnmodifiedSince time.Time
}
func fetchRangeHeader(headers http.Header, fullSize uint64) (*layer.RangeParams, error) { func fetchRangeHeader(headers http.Header, fullSize uint64) (*layer.RangeParams, error) {
const prefix = "bytes=" const prefix = "bytes="
rangeHeader := headers.Get("Range") rangeHeader := headers.Get("Range")
@ -73,10 +79,25 @@ func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
rid = api.GetRequestID(r.Context()) rid = api.GetRequestID(r.Context())
) )
args, err := parseGetObjectArgs(r.Header)
if err != nil {
return
}
if inf, err = h.obj.GetObjectInfo(r.Context(), bkt, obj); err != nil { if inf, err = h.obj.GetObjectInfo(r.Context(), bkt, obj); err != nil {
writeError(w, r, h.log, "could not find object", rid, bkt, obj, err) writeError(w, r, h.log, "could not find object", rid, bkt, obj, err)
return return
} }
if !args.IfModifiedSince.IsZero() && inf.Created.Before(args.IfModifiedSince) {
w.WriteHeader(http.StatusNotModified)
return
}
if !args.IfUnmodifiedSince.IsZero() && inf.Created.After(args.IfUnmodifiedSince) {
w.WriteHeader(http.StatusPreconditionFailed)
return
}
if params, err = fetchRangeHeader(r.Header, uint64(inf.Size)); err != nil { if params, err = fetchRangeHeader(r.Header, uint64(inf.Size)); err != nil {
writeError(w, r, h.log, "could not parse range header", rid, bkt, obj, err) writeError(w, r, h.log, "could not parse range header", rid, bkt, obj, err)
return return
@ -97,6 +118,27 @@ func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
} }
} }
func parseGetObjectArgs(headers http.Header) (*getObjectArgs, error) {
var err error
args := &getObjectArgs{}
ifModifiedSince := headers.Get(api.IfModifiedSince)
if len(ifModifiedSince) > 0 {
if args.IfModifiedSince, err = time.Parse(http.TimeFormat, ifModifiedSince); err != nil {
return nil, fmt.Errorf("couldn't parse %s header: %w", api.IfModifiedSince, err)
}
}
ifUnmodifiedSince := headers.Get(api.IfUnmodifiedSince)
if len(ifUnmodifiedSince) > 0 {
if args.IfUnmodifiedSince, err = time.Parse(http.TimeFormat, ifUnmodifiedSince); err != nil {
return nil, fmt.Errorf("couldn't parse %s header: %w", api.IfUnmodifiedSince, err)
}
}
return args, nil
}
func writeRangeHeaders(w http.ResponseWriter, params *layer.RangeParams, size int64) { func writeRangeHeaders(w http.ResponseWriter, params *layer.RangeParams, size int64) {
w.Header().Set("Accept-Ranges", "bytes") w.Header().Set("Accept-Ranges", "bytes")
w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", params.Start, params.End, size)) w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", params.Start, params.End, size))

View file

@ -1,6 +1,6 @@
package api package api
// Standard S3 HTTP response constants. // Standard S3 HTTP request/response constants.
const ( const (
LastModified = "Last-Modified" LastModified = "Last-Modified"
Date = "Date" Date = "Date"
@ -22,4 +22,6 @@ const (
ContentDisposition = "Content-Disposition" ContentDisposition = "Content-Disposition"
Authorization = "Authorization" Authorization = "Authorization"
Action = "Action" Action = "Action"
IfModifiedSince = "If-Modified-Since"
IfUnmodifiedSince = "If-Unmodified-Since"
) )