2023-08-31 08:37:03 +00:00
|
|
|
package handler
|
2021-07-15 07:57:22 +00:00
|
|
|
|
|
|
|
import (
|
2023-03-15 08:07:44 +00:00
|
|
|
"context"
|
2024-12-02 08:45:30 +00:00
|
|
|
"errors"
|
2022-02-08 16:24:23 +00:00
|
|
|
"io"
|
2021-07-15 07:57:22 +00:00
|
|
|
"net/http"
|
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
|
2024-12-02 08:45:30 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/layer"
|
2023-08-27 15:09:02 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/logs"
|
2023-03-07 14:08:53 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
|
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
2021-07-15 07:57:22 +00:00
|
|
|
"github.com/valyala/fasthttp"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
2022-03-04 06:53:49 +00:00
|
|
|
// max bytes needed to detect content type according to http.DetectContentType docs.
|
2021-07-15 07:57:22 +00:00
|
|
|
const sizeToDetectType = 512
|
|
|
|
|
2022-02-15 08:27:51 +00:00
|
|
|
const (
|
|
|
|
hdrObjectID = "X-Object-Id"
|
|
|
|
hdrOwnerID = "X-Owner-Id"
|
|
|
|
hdrContainerID = "X-Container-Id"
|
|
|
|
)
|
|
|
|
|
2023-08-31 08:37:03 +00:00
|
|
|
func (h *Handler) headObject(ctx context.Context, req request, objectAddress oid.Address) {
|
2021-07-15 07:57:22 +00:00
|
|
|
var start = time.Now()
|
|
|
|
|
2023-05-30 14:01:20 +00:00
|
|
|
btoken := bearerToken(ctx)
|
2022-04-07 12:56:18 +00:00
|
|
|
|
2024-07-15 13:35:08 +00:00
|
|
|
prm := PrmObjectHead{
|
2024-06-17 14:56:43 +00:00
|
|
|
PrmAuth: PrmAuth{
|
|
|
|
BearerToken: btoken,
|
|
|
|
},
|
2024-06-25 12:31:46 +00:00
|
|
|
Address: objectAddress,
|
2022-04-19 15:46:51 +00:00
|
|
|
}
|
2022-04-07 12:56:18 +00:00
|
|
|
|
2024-07-15 13:35:08 +00:00
|
|
|
obj, err := h.frostfs.HeadObject(ctx, prm)
|
2021-07-15 07:57:22 +00:00
|
|
|
if err != nil {
|
2023-03-15 08:07:44 +00:00
|
|
|
req.handleFrostFSErr(err, start)
|
2021-07-15 07:57:22 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-07-15 13:35:08 +00:00
|
|
|
req.Response.Header.Set(fasthttp.HeaderContentLength, strconv.FormatUint(obj.PayloadSize(), 10))
|
2021-07-15 07:57:22 +00:00
|
|
|
var contentType string
|
2024-07-15 13:35:08 +00:00
|
|
|
for _, attr := range obj.Attributes() {
|
2021-07-15 07:57:22 +00:00
|
|
|
key := attr.Key()
|
|
|
|
val := attr.Value()
|
|
|
|
if !isValidToken(key) || !isValidValue(val) {
|
|
|
|
continue
|
|
|
|
}
|
2023-03-16 08:40:08 +00:00
|
|
|
|
|
|
|
key = utils.BackwardTransformIfSystem(key)
|
|
|
|
|
2023-03-15 08:07:44 +00:00
|
|
|
req.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 {
|
2023-08-27 15:09:02 +00:00
|
|
|
req.log.Info(logs.CouldntParseCreationDate,
|
2021-07-15 07:57:22 +00:00
|
|
|
zap.String("key", key),
|
|
|
|
zap.String("val", val),
|
|
|
|
zap.Error(err))
|
|
|
|
continue
|
|
|
|
}
|
2023-03-15 08:07:44 +00:00
|
|
|
req.Response.Header.Set(fasthttp.HeaderLastModified, time.Unix(value, 0).UTC().Format(http.TimeFormat))
|
2021-07-15 07:57:22 +00:00
|
|
|
case object.AttributeContentType:
|
|
|
|
contentType = val
|
|
|
|
}
|
|
|
|
}
|
2022-02-15 09:13:43 +00:00
|
|
|
|
2024-07-15 13:35:08 +00:00
|
|
|
idsToResponse(&req.Response, obj)
|
2021-07-15 07:57:22 +00:00
|
|
|
|
|
|
|
if len(contentType) == 0 {
|
2024-07-15 13:35:08 +00:00
|
|
|
contentType, _, err = readContentType(obj.PayloadSize(), func(sz uint64) (io.Reader, error) {
|
|
|
|
prmRange := PrmObjectRange{
|
2024-06-17 14:56:43 +00:00
|
|
|
PrmAuth: PrmAuth{
|
|
|
|
BearerToken: btoken,
|
|
|
|
},
|
|
|
|
Address: objectAddress,
|
|
|
|
PayloadRange: [2]uint64{0, sz},
|
2022-04-19 15:46:51 +00:00
|
|
|
}
|
2022-04-07 12:56:18 +00:00
|
|
|
|
2024-07-15 13:35:08 +00:00
|
|
|
return h.frostfs.RangeObject(ctx, prmRange)
|
2022-03-04 06:53:49 +00:00
|
|
|
})
|
|
|
|
if err != nil && err != io.EOF {
|
2023-03-15 08:07:44 +00:00
|
|
|
req.handleFrostFSErr(err, start)
|
2021-07-15 07:57:22 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2023-03-15 08:07:44 +00:00
|
|
|
req.SetContentType(contentType)
|
2021-07-15 07:57:22 +00:00
|
|
|
}
|
|
|
|
|
2022-02-15 09:13:43 +00:00
|
|
|
func idsToResponse(resp *fasthttp.Response, obj *object.Object) {
|
2022-04-19 15:46:51 +00:00
|
|
|
objID, _ := obj.ID()
|
|
|
|
cnrID, _ := obj.ContainerID()
|
|
|
|
resp.Header.Set(hdrObjectID, objID.String())
|
2022-02-15 09:13:43 +00:00
|
|
|
resp.Header.Set(hdrOwnerID, obj.OwnerID().String())
|
2022-04-19 15:46:51 +00:00
|
|
|
resp.Header.Set(hdrContainerID, cnrID.String())
|
2022-02-15 09:13:43 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 08:19:35 +00:00
|
|
|
// HeadByAddressOrBucketName handles head requests using simple cid/oid or bucketname/key format.
|
2023-08-31 08:37:03 +00:00
|
|
|
func (h *Handler) HeadByAddressOrBucketName(c *fasthttp.RequestCtx) {
|
2024-12-02 08:45:30 +00:00
|
|
|
cidParam, _ := c.UserValue("cid").(string)
|
|
|
|
oidParam, _ := c.UserValue("oid").(string)
|
2023-05-05 08:19:35 +00:00
|
|
|
|
2024-12-02 08:45:30 +00:00
|
|
|
ctx := utils.GetContextFromRequest(c)
|
|
|
|
log := utils.GetReqLogOrDefault(ctx, h.log).With(
|
|
|
|
zap.String("cid", cidParam),
|
|
|
|
zap.String("oid", oidParam),
|
|
|
|
)
|
|
|
|
|
|
|
|
bktInfo, err := h.getBucketInfo(ctx, cidParam, log)
|
2023-05-05 08:19:35 +00:00
|
|
|
if err != nil {
|
2024-12-02 08:45:30 +00:00
|
|
|
logAndSendBucketError(c, log, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
checkS3Err := h.tree.CheckSettingsNodeExists(ctx, bktInfo)
|
|
|
|
if checkS3Err != nil && !errors.Is(checkS3Err, layer.ErrNodeNotFound) {
|
|
|
|
logAndSendBucketError(c, log, checkS3Err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
req := h.newRequest(c, log)
|
|
|
|
|
|
|
|
var objID oid.ID
|
|
|
|
if checkS3Err == nil {
|
|
|
|
h.byS3Path(ctx, req, bktInfo.CID, oidParam, h.headObject)
|
|
|
|
} else if err = objID.DecodeString(oidParam); err == nil {
|
|
|
|
h.byNativeAddress(ctx, req, bktInfo.CID, objID, h.headObject)
|
2023-05-05 08:19:35 +00:00
|
|
|
} else {
|
2024-12-02 08:45:30 +00:00
|
|
|
logAndSendBucketError(c, log, checkS3Err)
|
|
|
|
return
|
2023-05-05 08:19:35 +00:00
|
|
|
}
|
2021-07-15 07:57:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// HeadByAttribute handles attribute-based head requests.
|
2023-08-31 08:37:03 +00:00
|
|
|
func (h *Handler) HeadByAttribute(c *fasthttp.RequestCtx) {
|
|
|
|
h.byAttribute(c, h.headObject)
|
2021-07-15 07:57:22 +00:00
|
|
|
}
|