[#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"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/nspcc-dev/neofs-s3-gw/api"
|
||||
|
@ -12,6 +13,11 @@ import (
|
|||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type getObjectArgs struct {
|
||||
IfModifiedSince time.Time
|
||||
IfUnmodifiedSince time.Time
|
||||
}
|
||||
|
||||
func fetchRangeHeader(headers http.Header, fullSize uint64) (*layer.RangeParams, error) {
|
||||
const prefix = "bytes="
|
||||
rangeHeader := headers.Get("Range")
|
||||
|
@ -73,10 +79,25 @@ func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
|
|||
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 {
|
||||
writeError(w, r, h.log, "could not find object", rid, bkt, obj, err)
|
||||
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 {
|
||||
writeError(w, r, h.log, "could not parse range header", rid, bkt, obj, err)
|
||||
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) {
|
||||
w.Header().Set("Accept-Ranges", "bytes")
|
||||
w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", params.Start, params.End, size))
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package api
|
||||
|
||||
// Standard S3 HTTP response constants.
|
||||
// Standard S3 HTTP request/response constants.
|
||||
const (
|
||||
LastModified = "Last-Modified"
|
||||
Date = "Date"
|
||||
|
@ -22,4 +22,6 @@ const (
|
|||
ContentDisposition = "Content-Disposition"
|
||||
Authorization = "Authorization"
|
||||
Action = "Action"
|
||||
IfModifiedSince = "If-Modified-Since"
|
||||
IfUnmodifiedSince = "If-Unmodified-Since"
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue