WIP: Cache head results in the request context #974

Closed
fyrchik wants to merge 6 commits from fyrchik/frostfs-node:head-cache into master
10 changed files with 164 additions and 137 deletions

View file

@ -100,7 +100,7 @@ func (c *Checker) StickyBitCheck(info v2.RequestInfo, owner user.ID) bool {
} }
// CheckEACL is a main check function for extended ACL. // CheckEACL is a main check function for extended ACL.
func (c *Checker) CheckEACL(msg any, reqInfo v2.RequestInfo) error { func (c *Checker) CheckEACL(ctx context.Context, msg any, reqInfo v2.RequestInfo) error {
basicACL := reqInfo.BasicACL() basicACL := reqInfo.BasicACL()
if !basicACL.Extendable() { if !basicACL.Extendable() {
return nil return nil
@ -136,7 +136,7 @@ func (c *Checker) CheckEACL(msg any, reqInfo v2.RequestInfo) error {
return err return err
} }
hdrSrc, err := c.getHeaderSource(cnr, msg, reqInfo) hdrSrc, err := c.getHeaderSource(ctx, cnr, msg, reqInfo)
if err != nil { if err != nil {
return err return err
} }
@ -173,7 +173,7 @@ func getRole(reqInfo v2.RequestInfo) eaclSDK.Role {
return eaclRole return eaclRole
} }
func (c *Checker) getHeaderSource(cnr cid.ID, msg any, reqInfo v2.RequestInfo) (eaclSDK.TypedHeaderSource, error) { func (c *Checker) getHeaderSource(ctx context.Context, cnr cid.ID, msg any, reqInfo v2.RequestInfo) (eaclSDK.TypedHeaderSource, error) {
var xHeaderSource eaclV2.XHeaderSource var xHeaderSource eaclV2.XHeaderSource
if req, ok := msg.(eaclV2.Request); ok { if req, ok := msg.(eaclV2.Request); ok {
xHeaderSource = eaclV2.NewRequestXHeaderSource(req) xHeaderSource = eaclV2.NewRequestXHeaderSource(req)
@ -181,7 +181,7 @@ func (c *Checker) getHeaderSource(cnr cid.ID, msg any, reqInfo v2.RequestInfo) (
xHeaderSource = eaclV2.NewResponseXHeaderSource(msg.(eaclV2.Response), reqInfo.Request().(eaclV2.Request)) xHeaderSource = eaclV2.NewResponseXHeaderSource(msg.(eaclV2.Response), reqInfo.Request().(eaclV2.Request))
} }
hdrSrc, err := eaclV2.NewMessageHeaderSource(&localStorage{ls: c.localStorage}, xHeaderSource, cnr, eaclV2.WithOID(reqInfo.ObjectID())) hdrSrc, err := eaclV2.NewMessageHeaderSource(ctx, &localStorage{ls: c.localStorage}, xHeaderSource, cnr, reqInfo.ObjectID())
if err != nil { if err != nil {
return nil, fmt.Errorf("can't parse headers: %w", err) return nil, fmt.Errorf("can't parse headers: %w", err)
} }

View file

@ -103,10 +103,11 @@ func TestHeadRequest(t *testing.T) {
newSource := func(t *testing.T) eaclSDK.TypedHeaderSource { newSource := func(t *testing.T) eaclSDK.TypedHeaderSource {
hdrSrc, err := NewMessageHeaderSource( hdrSrc, err := NewMessageHeaderSource(
context.TODO(),
lStorage, lStorage,
NewRequestXHeaderSource(req), NewRequestXHeaderSource(req),
addr.Container(), addr.Container(),
WithOID(&id)) &id)
require.NoError(t, err) require.NoError(t, err)
return hdrSrc return hdrSrc
} }

View file

@ -9,6 +9,7 @@ import (
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
refsV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" refsV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
eaclSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl" eaclSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/eacl"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
@ -16,8 +17,6 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
) )
type Option func(*cfg)
type cfg struct { type cfg struct {
storage ObjectStorage storage ObjectStorage
@ -46,24 +45,21 @@ type headerSource struct {
incompleteObjectHeaders bool incompleteObjectHeaders bool
} }
func NewMessageHeaderSource(os ObjectStorage, xhs XHeaderSource, cnrID cid.ID, opts ...Option) (eaclSDK.TypedHeaderSource, error) { func NewMessageHeaderSource(ctx context.Context, os ObjectStorage, xhs XHeaderSource, cnrID cid.ID, objID *oid.ID) (eaclSDK.TypedHeaderSource, error) {
cfg := &cfg{ cfg := &cfg{
storage: os, storage: os,
cnr: cnrID, cnr: cnrID,
obj: objID,
msg: xhs, msg: xhs,
} }
for i := range opts {
opts[i](cfg)
}
if cfg.msg == nil { if cfg.msg == nil {
return nil, errors.New("message is not provided") return nil, errors.New("message is not provided")
} }
var res headerSource var res headerSource
err := cfg.readObjectHeaders(&res) err := cfg.readObjectHeaders(ctx, &res)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -96,18 +92,18 @@ func (x xHeader) Value() string {
var errMissingOID = errors.New("object ID is missing") var errMissingOID = errors.New("object ID is missing")
func (h *cfg) readObjectHeaders(dst *headerSource) error { func (h *cfg) readObjectHeaders(ctx context.Context, dst *headerSource) error {
switch m := h.msg.(type) { switch m := h.msg.(type) {
default: default:
panic(fmt.Sprintf("unexpected message type %T", h.msg)) panic(fmt.Sprintf("unexpected message type %T", h.msg))
case requestXHeaderSource: case requestXHeaderSource:
return h.readObjectHeadersFromRequestXHeaderSource(m, dst) return h.readObjectHeadersFromRequestXHeaderSource(ctx, m, dst)
case responseXHeaderSource: case responseXHeaderSource:
return h.readObjectHeadersResponseXHeaderSource(m, dst) return h.readObjectHeadersResponseXHeaderSource(ctx, m, dst)
} }
} }
func (h *cfg) readObjectHeadersFromRequestXHeaderSource(m requestXHeaderSource, dst *headerSource) error { func (h *cfg) readObjectHeadersFromRequestXHeaderSource(ctx context.Context, m requestXHeaderSource, dst *headerSource) error {
switch req := m.req.(type) { switch req := m.req.(type) {
case case
*objectV2.GetRequest, *objectV2.GetRequest,
@ -116,7 +112,7 @@ func (h *cfg) readObjectHeadersFromRequestXHeaderSource(m requestXHeaderSource,
return errMissingOID return errMissingOID
} }
objHeaders, completed := h.localObjectHeaders(h.cnr, h.obj) objHeaders, completed := h.localObjectHeaders(ctx, h.cnr, h.obj)
dst.objectHeaders = objHeaders dst.objectHeaders = objHeaders
dst.incompleteObjectHeaders = !completed dst.incompleteObjectHeaders = !completed
@ -154,10 +150,10 @@ func (h *cfg) readObjectHeadersFromRequestXHeaderSource(m requestXHeaderSource,
return nil return nil
} }
func (h *cfg) readObjectHeadersResponseXHeaderSource(m responseXHeaderSource, dst *headerSource) error { func (h *cfg) readObjectHeadersResponseXHeaderSource(ctx context.Context, m responseXHeaderSource, dst *headerSource) error {
switch resp := m.resp.(type) { switch resp := m.resp.(type) {
default: default:
objectHeaders, completed := h.localObjectHeaders(h.cnr, h.obj) objectHeaders, completed := h.localObjectHeaders(ctx, h.cnr, h.obj)
dst.objectHeaders = objectHeaders dst.objectHeaders = objectHeaders
dst.incompleteObjectHeaders = !completed dst.incompleteObjectHeaders = !completed
@ -198,14 +194,25 @@ func (h *cfg) readObjectHeadersResponseXHeaderSource(m responseXHeaderSource, ds
return nil return nil
} }
func (h *cfg) localObjectHeaders(cnr cid.ID, idObj *oid.ID) ([]eaclSDK.Header, bool) { func (h *cfg) localObjectHeaders(ctx context.Context, cnr cid.ID, idObj *oid.ID) ([]eaclSDK.Header, bool) {
if idObj != nil { if idObj != nil {
var addr oid.Address var addr oid.Address
addr.SetContainer(cnr) addr.SetContainer(cnr)
addr.SetObject(*idObj) addr.SetObject(*idObj)
obj, err := h.storage.Head(context.TODO(), addr) reqCtx, _ := object.FromRequestContext(ctx)
if reqCtx != nil {
hdr := reqCtx.GetHeader(addr)
if hdr != nil {
return headersFromObject(hdr, cnr, idObj), true
}
}
obj, err := h.storage.Head(ctx, addr)
if err == nil { if err == nil {
if reqCtx != nil {
reqCtx.Header.Store(obj)
}
return headersFromObject(obj, cnr, idObj), true return headersFromObject(obj, cnr, idObj), true
} }
} }

View file

@ -1,11 +0,0 @@
package v2
import (
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
)
func WithOID(v *oid.ID) Option {
return func(c *cfg) {
c.obj = v
}
}

View file

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
sessionV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session" sessionV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
@ -157,3 +158,13 @@ func unmarshalPublicKeyWithOwner(rawKey []byte) (*user.ID, *keys.PublicKey, erro
return &idSender, key, nil return &idSender, key, nil
} }
func (r RequestInfo) toRequestContext() *object.RequestContext {
return &object.RequestContext{
Namespace: r.ContainerNamespace(),
ContainerOwner: r.ContainerOwner(),
SenderKey: r.SenderKey(),
Role: r.RequestRole(),
SoftAPECheck: r.IsSoftAPECheck(),
}
}

View file

@ -108,24 +108,11 @@ func New(next object.ServiceServer,
type wrappedGetObjectStream struct { type wrappedGetObjectStream struct {
object.GetObjectStream object.GetObjectStream
requestInfo RequestInfo requestContext *object.RequestContext
} }
func (w *wrappedGetObjectStream) Context() context.Context { func (w *wrappedGetObjectStream) Context() context.Context {
return context.WithValue(w.GetObjectStream.Context(), object.RequestContextKey, &object.RequestContext{ return object.NewRequestContext(w.GetObjectStream.Context(), w.requestContext)
Namespace: w.requestInfo.ContainerNamespace(),
ContainerOwner: w.requestInfo.ContainerOwner(),
SenderKey: w.requestInfo.SenderKey(),
Role: w.requestInfo.RequestRole(),
SoftAPECheck: w.requestInfo.IsSoftAPECheck(),
})
}
func newWrappedGetObjectStreamStream(getObjectStream object.GetObjectStream, reqInfo RequestInfo) object.GetObjectStream {
return &wrappedGetObjectStream{
GetObjectStream: getObjectStream,
requestInfo: reqInfo,
}
} }
// wrappedRangeStream propagates RequestContext into GetObjectRangeStream's context. // wrappedRangeStream propagates RequestContext into GetObjectRangeStream's context.
@ -133,24 +120,11 @@ func newWrappedGetObjectStreamStream(getObjectStream object.GetObjectStream, req
type wrappedRangeStream struct { type wrappedRangeStream struct {
object.GetObjectRangeStream object.GetObjectRangeStream
requestInfo RequestInfo requestContext *object.RequestContext
} }
func (w *wrappedRangeStream) Context() context.Context { func (w *wrappedRangeStream) Context() context.Context {
return context.WithValue(w.GetObjectRangeStream.Context(), object.RequestContextKey, &object.RequestContext{ return object.NewRequestContext(w.GetObjectRangeStream.Context(), w.requestContext)
Namespace: w.requestInfo.ContainerNamespace(),
ContainerOwner: w.requestInfo.ContainerOwner(),
SenderKey: w.requestInfo.SenderKey(),
Role: w.requestInfo.RequestRole(),
SoftAPECheck: w.requestInfo.IsSoftAPECheck(),
})
}
func newWrappedRangeStream(rangeStream object.GetObjectRangeStream, reqInfo RequestInfo) object.GetObjectRangeStream {
return &wrappedRangeStream{
GetObjectRangeStream: rangeStream,
requestInfo: reqInfo,
}
} }
// wrappedSearchStream propagates RequestContext into SearchStream's context. // wrappedSearchStream propagates RequestContext into SearchStream's context.
@ -158,24 +132,11 @@ func newWrappedRangeStream(rangeStream object.GetObjectRangeStream, reqInfo Requ
type wrappedSearchStream struct { type wrappedSearchStream struct {
object.SearchStream object.SearchStream
requestInfo RequestInfo requestContext *object.RequestContext
} }
func (w *wrappedSearchStream) Context() context.Context { func (w *wrappedSearchStream) Context() context.Context {
return context.WithValue(w.SearchStream.Context(), object.RequestContextKey, &object.RequestContext{ return object.NewRequestContext(w.SearchStream.Context(), w.requestContext)
Namespace: w.requestInfo.ContainerNamespace(),
ContainerOwner: w.requestInfo.ContainerOwner(),
SenderKey: w.requestInfo.SenderKey(),
Role: w.requestInfo.RequestRole(),
SoftAPECheck: w.requestInfo.IsSoftAPECheck(),
})
}
func newWrappedSearchStream(searchStream object.SearchStream, reqInfo RequestInfo) object.SearchStream {
return &wrappedSearchStream{
SearchStream: searchStream,
requestInfo: reqInfo,
}
} }
// Get implements ServiceServer interface, makes ACL checks and calls // Get implements ServiceServer interface, makes ACL checks and calls
@ -222,18 +183,25 @@ func (b Service) Get(request *objectV2.GetRequest, stream object.GetObjectStream
reqInfo.obj = obj reqInfo.obj = obj
reqCtx := reqInfo.toRequestContext()
if reqInfo.IsSoftAPECheck() { if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) { if !b.checker.CheckBasicACL(reqInfo) {
return basicACLErr(reqInfo) return basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil { }
ctx := object.NewRequestContext(stream.Context(), reqCtx)
if err := b.checker.CheckEACL(ctx, request, reqInfo); err != nil {
return eACLErr(reqInfo, err) return eACLErr(reqInfo, err)
} }
} }
return b.next.Get(request, &getStreamBasicChecker{ return b.next.Get(request, &getStreamBasicChecker{
GetObjectStream: newWrappedGetObjectStreamStream(stream, reqInfo), GetObjectStream: &wrappedGetObjectStream{
info: reqInfo, GetObjectStream: stream,
checker: b.checker, requestContext: reqCtx,
},
info: reqInfo,
checker: b.checker,
}) })
} }
@ -291,17 +259,20 @@ func (b Service) Head(
reqInfo.obj = obj reqInfo.obj = obj
ctx = requestContext(ctx, reqInfo)
if reqInfo.IsSoftAPECheck() { if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) { if !b.checker.CheckBasicACL(reqInfo) {
return nil, basicACLErr(reqInfo) return nil, basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil { }
if err := b.checker.CheckEACL(ctx, request, reqInfo); err != nil {
return nil, eACLErr(reqInfo, err) return nil, eACLErr(reqInfo, err)
} }
} }
resp, err := b.next.Head(requestContext(ctx, reqInfo), request) resp, err := b.next.Head(ctx, request)
if err == nil { if err == nil {
if err = b.checker.CheckEACL(resp, reqInfo); err != nil { if err = b.checker.CheckEACL(ctx, resp, reqInfo); err != nil {
err = eACLErr(reqInfo, err) err = eACLErr(reqInfo, err)
} }
} }
@ -344,18 +315,25 @@ func (b Service) Search(request *objectV2.SearchRequest, stream object.SearchStr
return err return err
} }
reqCtx := reqInfo.toRequestContext()
if reqInfo.IsSoftAPECheck() { if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) { if !b.checker.CheckBasicACL(reqInfo) {
return basicACLErr(reqInfo) return basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil { }
ctx := object.NewRequestContext(stream.Context(), reqCtx)
if err := b.checker.CheckEACL(ctx, request, reqInfo); err != nil {
return eACLErr(reqInfo, err) return eACLErr(reqInfo, err)
} }
} }
return b.next.Search(request, &searchStreamBasicChecker{ return b.next.Search(request, &searchStreamBasicChecker{
checker: b.checker, checker: b.checker,
SearchStream: newWrappedSearchStream(stream, reqInfo), SearchStream: &wrappedSearchStream{
info: reqInfo, SearchStream: stream,
requestContext: reqCtx,
},
info: reqInfo,
}) })
} }
@ -404,15 +382,15 @@ func (b Service) Delete(
reqInfo.obj = obj reqInfo.obj = obj
ctx = requestContext(ctx, reqInfo)
if reqInfo.IsSoftAPECheck() { if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) { if !b.checker.CheckBasicACL(reqInfo) {
return nil, basicACLErr(reqInfo) return nil, basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil { } else if err := b.checker.CheckEACL(ctx, request, reqInfo); err != nil {
return nil, eACLErr(reqInfo, err) return nil, eACLErr(reqInfo, err)
} }
} }
return b.next.Delete(ctx, request)
return b.next.Delete(requestContext(ctx, reqInfo), request)
} }
func (b Service) GetRange(request *objectV2.GetRangeRequest, stream object.GetObjectRangeStream) error { func (b Service) GetRange(request *objectV2.GetRangeRequest, stream object.GetObjectRangeStream) error {
@ -457,29 +435,30 @@ func (b Service) GetRange(request *objectV2.GetRangeRequest, stream object.GetOb
reqInfo.obj = obj reqInfo.obj = obj
reqCtx := reqInfo.toRequestContext()
if reqInfo.IsSoftAPECheck() { if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) { if !b.checker.CheckBasicACL(reqInfo) {
return basicACLErr(reqInfo) return basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil { }
ctx := object.NewRequestContext(stream.Context(), reqCtx)
if err := b.checker.CheckEACL(ctx, request, reqInfo); err != nil {
return eACLErr(reqInfo, err) return eACLErr(reqInfo, err)
} }
} }
return b.next.GetRange(request, &rangeStreamBasicChecker{ return b.next.GetRange(request, &rangeStreamBasicChecker{
checker: b.checker, checker: b.checker,
GetObjectRangeStream: newWrappedRangeStream(stream, reqInfo), GetObjectRangeStream: &wrappedRangeStream{
info: reqInfo, GetObjectRangeStream: stream,
requestContext: reqCtx,
},
info: reqInfo,
}) })
} }
func requestContext(ctx context.Context, reqInfo RequestInfo) context.Context { func requestContext(ctx context.Context, reqInfo RequestInfo) context.Context {
return context.WithValue(ctx, object.RequestContextKey, &object.RequestContext{ return object.NewRequestContext(ctx, reqInfo.toRequestContext())
Namespace: reqInfo.ContainerNamespace(),
ContainerOwner: reqInfo.ContainerOwner(),
SenderKey: reqInfo.SenderKey(),
Role: reqInfo.RequestRole(),
SoftAPECheck: reqInfo.IsSoftAPECheck(),
})
} }
func (b Service) GetRangeHash( func (b Service) GetRangeHash(
@ -527,15 +506,18 @@ func (b Service) GetRangeHash(
reqInfo.obj = obj reqInfo.obj = obj
ctx = requestContext(ctx, reqInfo)
if reqInfo.IsSoftAPECheck() { if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) { if !b.checker.CheckBasicACL(reqInfo) {
return nil, basicACLErr(reqInfo) return nil, basicACLErr(reqInfo)
} else if err := b.checker.CheckEACL(request, reqInfo); err != nil { }
if err := b.checker.CheckEACL(ctx, request, reqInfo); err != nil {
return nil, eACLErr(reqInfo, err) return nil, eACLErr(reqInfo, err)
} }
} }
return b.next.GetRangeHash(requestContext(ctx, reqInfo), request) return b.next.GetRangeHash(ctx, request)
} }
func (b Service) PutSingle(ctx context.Context, request *objectV2.PutSingleRequest) (*objectV2.PutSingleResponse, error) { func (b Service) PutSingle(ctx context.Context, request *objectV2.PutSingleRequest) (*objectV2.PutSingleResponse, error) {
@ -586,16 +568,18 @@ func (b Service) PutSingle(ctx context.Context, request *objectV2.PutSingleReque
reqInfo.obj = obj reqInfo.obj = obj
ctx = requestContext(ctx, reqInfo)
if reqInfo.IsSoftAPECheck() { if reqInfo.IsSoftAPECheck() {
if !b.checker.CheckBasicACL(reqInfo) || !b.checker.StickyBitCheck(reqInfo, idOwner) { if !b.checker.CheckBasicACL(reqInfo) || !b.checker.StickyBitCheck(reqInfo, idOwner) {
return nil, basicACLErr(reqInfo) return nil, basicACLErr(reqInfo)
} }
if err := b.checker.CheckEACL(request, reqInfo); err != nil {
if err := b.checker.CheckEACL(ctx, request, reqInfo); err != nil {
return nil, eACLErr(reqInfo, err) return nil, eACLErr(reqInfo, err)
} }
} }
return b.next.PutSingle(requestContext(ctx, reqInfo), request) return b.next.PutSingle(ctx, request)
} }
func (p putStreamBasicChecker) Send(ctx context.Context, request *objectV2.PutRequest) error { func (p putStreamBasicChecker) Send(ctx context.Context, request *objectV2.PutRequest) error {
@ -706,7 +690,7 @@ func (p putStreamBasicChecker) CloseAndRecv(ctx context.Context) (*objectV2.PutR
func (g *getStreamBasicChecker) Send(resp *objectV2.GetResponse) error { func (g *getStreamBasicChecker) Send(resp *objectV2.GetResponse) error {
if _, ok := resp.GetBody().GetObjectPart().(*objectV2.GetObjectPartInit); ok { if _, ok := resp.GetBody().GetObjectPart().(*objectV2.GetObjectPartInit); ok {
if err := g.checker.CheckEACL(resp, g.info); err != nil { if err := g.checker.CheckEACL(g.GetObjectStream.Context(), resp, g.info); err != nil {
return eACLErr(g.info, err) return eACLErr(g.info, err)
} }
} }
@ -715,7 +699,7 @@ func (g *getStreamBasicChecker) Send(resp *objectV2.GetResponse) error {
} }
func (g *rangeStreamBasicChecker) Send(resp *objectV2.GetRangeResponse) error { func (g *rangeStreamBasicChecker) Send(resp *objectV2.GetRangeResponse) error {
if err := g.checker.CheckEACL(resp, g.info); err != nil { if err := g.checker.CheckEACL(g.GetObjectRangeStream.Context(), resp, g.info); err != nil {
return eACLErr(g.info, err) return eACLErr(g.info, err)
} }
@ -723,7 +707,7 @@ func (g *rangeStreamBasicChecker) Send(resp *objectV2.GetRangeResponse) error {
} }
func (g *searchStreamBasicChecker) Send(resp *objectV2.SearchResponse) error { func (g *searchStreamBasicChecker) Send(resp *objectV2.SearchResponse) error {
if err := g.checker.CheckEACL(resp, g.info); err != nil { if err := g.checker.CheckEACL(g.SearchStream.Context(), resp, g.info); err != nil {
return eACLErr(g.info, err) return eACLErr(g.info, err)
} }

View file

@ -1,6 +1,8 @@
package v2 package v2
import ( import (
"context"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
) )
@ -12,7 +14,7 @@ type ACLChecker interface {
CheckBasicACL(RequestInfo) bool CheckBasicACL(RequestInfo) bool
// CheckEACL must return non-nil error if request // CheckEACL must return non-nil error if request
// doesn't pass extended ACL validation. // doesn't pass extended ACL validation.
CheckEACL(any, RequestInfo) error CheckEACL(context.Context, any, RequestInfo) error
// StickyBitCheck must return true only if sticky bit // StickyBitCheck must return true only if sticky bit
// is disabled or enabled but request contains correct // is disabled or enabled but request contains correct
// owner field. // owner field.

View file

@ -3,8 +3,6 @@ package ape
import ( import (
"context" "context"
"encoding/hex" "encoding/hex"
"errors"
"fmt"
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object" objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
@ -18,8 +16,6 @@ import (
nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native" nativeschema "git.frostfs.info/TrueCloudLab/policy-engine/schema/native"
) )
var errFailedToCastToRequestContext = errors.New("failed cast to RequestContext")
type Service struct { type Service struct {
log *logger.Logger log *logger.Logger
@ -101,25 +97,13 @@ func (g *getStreamBasicChecker) Send(resp *objectV2.GetResponse) error {
return g.GetObjectStream.Send(resp) return g.GetObjectStream.Send(resp)
} }
func requestContext(ctx context.Context) (*objectSvc.RequestContext, error) {
untyped := ctx.Value(objectSvc.RequestContextKey)
if untyped == nil {
return nil, fmt.Errorf("no key %s in context", objectSvc.RequestContextKey)
}
rc, ok := untyped.(*objectSvc.RequestContext)
if !ok {
return nil, errFailedToCastToRequestContext
}
return rc, nil
}
func (c *Service) Get(request *objectV2.GetRequest, stream objectSvc.GetObjectStream) error { func (c *Service) Get(request *objectV2.GetRequest, stream objectSvc.GetObjectStream) error {
cnrID, objID, err := getAddressParamsSDK(request.GetBody().GetAddress().GetContainerID(), request.GetBody().GetAddress().GetObjectID()) cnrID, objID, err := getAddressParamsSDK(request.GetBody().GetAddress().GetContainerID(), request.GetBody().GetAddress().GetObjectID())
if err != nil { if err != nil {
return toStatusErr(err) return toStatusErr(err)
} }
reqCtx, err := requestContext(stream.Context()) reqCtx, err := objectSvc.FromRequestContext(stream.Context())
if err != nil { if err != nil {
return toStatusErr(err) return toStatusErr(err)
} }
@ -156,7 +140,7 @@ type putStreamBasicChecker struct {
func (p *putStreamBasicChecker) Send(ctx context.Context, request *objectV2.PutRequest) error { func (p *putStreamBasicChecker) Send(ctx context.Context, request *objectV2.PutRequest) error {
if partInit, ok := request.GetBody().GetObjectPart().(*objectV2.PutObjectPartInit); ok { if partInit, ok := request.GetBody().GetObjectPart().(*objectV2.PutObjectPartInit); ok {
reqCtx, err := requestContext(ctx) reqCtx, err := objectSvc.FromRequestContext(ctx)
if err != nil { if err != nil {
return toStatusErr(err) return toStatusErr(err)
} }
@ -205,7 +189,7 @@ func (c *Service) Head(ctx context.Context, request *objectV2.HeadRequest) (*obj
return nil, err return nil, err
} }
reqCtx, err := requestContext(ctx) reqCtx, err := objectSvc.FromRequestContext(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -219,6 +203,7 @@ func (c *Service) Head(ctx context.Context, request *objectV2.HeadRequest) (*obj
SenderKey: hex.EncodeToString(reqCtx.SenderKey), SenderKey: hex.EncodeToString(reqCtx.SenderKey),
ContainerOwner: reqCtx.ContainerOwner, ContainerOwner: reqCtx.ContainerOwner,
SoftAPECheck: reqCtx.SoftAPECheck, SoftAPECheck: reqCtx.SoftAPECheck,
Header: reqCtx.Header.Load().ToV2().GetHeader(),
}) })
if err != nil { if err != nil {
return nil, toStatusErr(err) return nil, toStatusErr(err)
@ -273,7 +258,7 @@ func (c *Service) Search(request *objectV2.SearchRequest, stream objectSvc.Searc
} }
} }
reqCtx, err := requestContext(stream.Context()) reqCtx, err := objectSvc.FromRequestContext(stream.Context())
if err != nil { if err != nil {
return toStatusErr(err) return toStatusErr(err)
} }
@ -300,7 +285,7 @@ func (c *Service) Delete(ctx context.Context, request *objectV2.DeleteRequest) (
return nil, err return nil, err
} }
reqCtx, err := requestContext(ctx) reqCtx, err := objectSvc.FromRequestContext(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -333,7 +318,7 @@ func (c *Service) GetRange(request *objectV2.GetRangeRequest, stream objectSvc.G
return toStatusErr(err) return toStatusErr(err)
} }
reqCtx, err := requestContext(stream.Context()) reqCtx, err := objectSvc.FromRequestContext(stream.Context())
if err != nil { if err != nil {
return toStatusErr(err) return toStatusErr(err)
} }
@ -361,7 +346,7 @@ func (c *Service) GetRangeHash(ctx context.Context, request *objectV2.GetRangeHa
return nil, err return nil, err
} }
reqCtx, err := requestContext(ctx) reqCtx, err := objectSvc.FromRequestContext(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -398,7 +383,7 @@ func (c *Service) PutSingle(ctx context.Context, request *objectV2.PutSingleRequ
return nil, err return nil, err
} }
reqCtx, err := requestContext(ctx) reqCtx, err := objectSvc.FromRequestContext(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -5,6 +5,7 @@ import (
"errors" "errors"
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object"
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing" "git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
@ -50,6 +51,12 @@ func (r *request) executeLocal(ctx context.Context) {
func (r *request) get(ctx context.Context) (*objectSDK.Object, error) { func (r *request) get(ctx context.Context) (*objectSDK.Object, error) {
if r.headOnly() { if r.headOnly() {
reqCtx, _ := object.FromRequestContext(ctx)
if reqCtx != nil && !r.isRaw() {
if hdr := reqCtx.GetHeader(r.address()); hdr != nil {
return hdr, nil
}
}
return r.localStorage.Head(ctx, r.address(), r.isRaw()) return r.localStorage.Head(ctx, r.address(), r.isRaw())
} }
if rng := r.ctxRange(); rng != nil { if rng := r.ctxRange(); rng != nil {

View file

@ -1,13 +1,20 @@
package object package object
import ( import (
"context"
"fmt"
"sync/atomic"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/object"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
) )
type RequestContextKeyT struct{} type requestContextKeyT struct{}
var RequestContextKey = RequestContextKeyT{} var requestContextKey = requestContextKeyT{}
// RequestContext is a context passed between middleware handlers. // RequestContext is a context passed between middleware handlers.
type RequestContext struct { type RequestContext struct {
@ -20,4 +27,38 @@ type RequestContext struct {
Role acl.Role Role acl.Role
SoftAPECheck bool SoftAPECheck bool
Header atomic.Pointer[objectSDK.Object]
}
// NewRequestContext returns a copy of ctx which carries value.
func NewRequestContext(ctx context.Context, value *RequestContext) context.Context {
return context.WithValue(ctx, requestContextKey, value)
}
// FromRequestContext returns RequestContext value stored in ctx if any.
func FromRequestContext(ctx context.Context) (*RequestContext, error) {
reqCtx, ok := ctx.Value(requestContextKey).(*RequestContext)
if !ok {
return nil, fmt.Errorf("no key %s in context", requestContextKey)
}
return reqCtx, nil
}
// GetHeader returns header if it is present and matches cid + oid pair.
func (r *RequestContext) GetHeader(addr oid.Address) *objectSDK.Object {
if r == nil {
return nil
}
hdr := r.Header.Load()
if hdr == nil {
return nil
}
storedAddr := object.AddressOf(hdr)
if addr.Equals(storedAddr) {
return hdr
}
return nil
} }