[#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:
parent
7ebcd5af8f
commit
da4eca5da5
2 changed files with 45 additions and 1 deletions
|
@ -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))
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue