2021-07-15 07:57:22 +00:00
|
|
|
package downloader
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net/http"
|
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
|
2021-11-12 11:37:05 +00:00
|
|
|
"github.com/nspcc-dev/neofs-http-gw/response"
|
2021-07-15 07:57:22 +00:00
|
|
|
"github.com/nspcc-dev/neofs-http-gw/tokens"
|
2021-11-30 07:22:05 +00:00
|
|
|
"github.com/nspcc-dev/neofs-http-gw/utils"
|
2021-11-15 11:12:15 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/client"
|
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/object"
|
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
2021-07-15 07:57:22 +00:00
|
|
|
"github.com/valyala/fasthttp"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
|
|
|
const sizeToDetectType = 512
|
|
|
|
|
2021-11-15 11:12:15 +00:00
|
|
|
func (r request) headObject(clnt pool.Object, objectAddress *object.Address) {
|
2021-07-15 07:57:22 +00:00
|
|
|
var start = time.Now()
|
|
|
|
if err := tokens.StoreBearerToken(r.RequestCtx); err != nil {
|
|
|
|
r.log.Error("could not fetch and store bearer token", zap.Error(err))
|
2021-11-12 11:37:05 +00:00
|
|
|
response.Error(r.RequestCtx, "could not fetch and store bearer token", fasthttp.StatusBadRequest)
|
2021-07-15 07:57:22 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
options := new(client.ObjectHeaderParams).WithAddress(objectAddress)
|
|
|
|
bearerOpt := bearerOpts(r.RequestCtx)
|
|
|
|
obj, err := clnt.GetObjectHeader(r.RequestCtx, options, bearerOpt)
|
|
|
|
if err != nil {
|
|
|
|
r.handleNeoFSErr(err, start)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
r.Response.Header.Set("Content-Length", strconv.FormatUint(obj.PayloadSize(), 10))
|
|
|
|
var contentType string
|
|
|
|
for _, attr := range obj.Attributes() {
|
|
|
|
key := attr.Key()
|
|
|
|
val := attr.Value()
|
|
|
|
if !isValidToken(key) || !isValidValue(val) {
|
|
|
|
continue
|
|
|
|
}
|
2021-11-30 07:22:05 +00:00
|
|
|
r.Response.Header.Set(utils.UserAttributeHeaderPrefix+key, val)
|
2021-07-15 07:57:22 +00:00
|
|
|
switch key {
|
|
|
|
case object.AttributeTimestamp:
|
|
|
|
value, err := strconv.ParseInt(val, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
r.log.Info("couldn't parse creation date",
|
|
|
|
zap.String("key", key),
|
|
|
|
zap.String("val", val),
|
|
|
|
zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
r.Response.Header.Set("Last-Modified", time.Unix(value, 0).UTC().Format(http.TimeFormat))
|
|
|
|
case object.AttributeContentType:
|
|
|
|
contentType = val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
r.Response.Header.Set("x-object-id", obj.ID().String())
|
|
|
|
r.Response.Header.Set("x-owner-id", obj.OwnerID().String())
|
|
|
|
r.Response.Header.Set("x-container-id", obj.ContainerID().String())
|
|
|
|
|
|
|
|
if len(contentType) == 0 {
|
|
|
|
objRange := object.NewRange()
|
|
|
|
objRange.SetOffset(0)
|
|
|
|
if sizeToDetectType < obj.PayloadSize() {
|
|
|
|
objRange.SetLength(sizeToDetectType)
|
|
|
|
} else {
|
|
|
|
objRange.SetLength(obj.PayloadSize())
|
|
|
|
}
|
|
|
|
ops := new(client.RangeDataParams).WithAddress(objectAddress).WithRange(objRange)
|
|
|
|
data, err := clnt.ObjectPayloadRangeData(r.RequestCtx, ops, bearerOpt)
|
|
|
|
if err != nil {
|
|
|
|
r.handleNeoFSErr(err, start)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
contentType = http.DetectContentType(data)
|
|
|
|
}
|
|
|
|
r.SetContentType(contentType)
|
|
|
|
}
|
|
|
|
|
|
|
|
// HeadByAddress handles head requests using simple cid/oid format.
|
|
|
|
func (d *Downloader) HeadByAddress(c *fasthttp.RequestCtx) {
|
|
|
|
d.byAddress(c, request.headObject)
|
|
|
|
}
|
|
|
|
|
|
|
|
// HeadByAttribute handles attribute-based head requests.
|
|
|
|
func (d *Downloader) HeadByAttribute(c *fasthttp.RequestCtx) {
|
|
|
|
d.byAttribute(c, request.headObject)
|
|
|
|
}
|