forked from TrueCloudLab/frostfs-sdk-go
[#131] client: Re-implement Object.Head method
Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
parent
8d734d1020
commit
caf31a928c
5 changed files with 216 additions and 251 deletions
|
@ -271,7 +271,7 @@ func (x *contextCall) readResponse() bool {
|
||||||
return x.processResponse()
|
return x.processResponse()
|
||||||
}
|
}
|
||||||
|
|
||||||
// closes the message stream (if closer is set) and writes the results (if resuls is set).
|
// closes the message stream (if closer is set) and writes the results (if result is set).
|
||||||
// Return means success. If failed, contextCall.err contains the reason.
|
// Return means success. If failed, contextCall.err contains the reason.
|
||||||
func (x *contextCall) close() bool {
|
func (x *contextCall) close() bool {
|
||||||
if x.closer != nil {
|
if x.closer != nil {
|
||||||
|
|
192
client/object.go
192
client/object.go
|
@ -32,14 +32,6 @@ type DeleteObjectParams struct {
|
||||||
tombTgt ObjectAddressWriter
|
tombTgt ObjectAddressWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
type ObjectHeaderParams struct {
|
|
||||||
addr *address.Address
|
|
||||||
|
|
||||||
raw bool
|
|
||||||
|
|
||||||
short bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type RangeDataParams struct {
|
type RangeDataParams struct {
|
||||||
addr *address.Address
|
addr *address.Address
|
||||||
|
|
||||||
|
@ -251,190 +243,6 @@ func writeUnexpectedMessageTypeErr(res resCommon, val interface{}) {
|
||||||
res.setStatus(st)
|
res.setStatus(st)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ObjectHeaderParams) WithAddress(v *address.Address) *ObjectHeaderParams {
|
|
||||||
if p != nil {
|
|
||||||
p.addr = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ObjectHeaderParams) Address() *address.Address {
|
|
||||||
if p != nil {
|
|
||||||
return p.addr
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ObjectHeaderParams) WithAllFields() *ObjectHeaderParams {
|
|
||||||
if p != nil {
|
|
||||||
p.short = false
|
|
||||||
}
|
|
||||||
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllFields return true if parameter set to return all header fields, returns
|
|
||||||
// false if parameter set to return only main fields of header.
|
|
||||||
func (p *ObjectHeaderParams) AllFields() bool {
|
|
||||||
if p != nil {
|
|
||||||
return !p.short
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ObjectHeaderParams) WithMainFields() *ObjectHeaderParams {
|
|
||||||
if p != nil {
|
|
||||||
p.short = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ObjectHeaderParams) WithRawFlag(v bool) *ObjectHeaderParams {
|
|
||||||
if p != nil {
|
|
||||||
p.raw = v
|
|
||||||
}
|
|
||||||
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *ObjectHeaderParams) RawFlag() bool {
|
|
||||||
if p != nil {
|
|
||||||
return p.raw
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
type ObjectHeadRes struct {
|
|
||||||
statusRes
|
|
||||||
objectRes
|
|
||||||
}
|
|
||||||
|
|
||||||
// HeadObject receives object's header through NeoFS API call.
|
|
||||||
//
|
|
||||||
// Any client's internal or transport errors are returned as `error`.
|
|
||||||
// If WithNeoFSErrorParsing option has been provided, unsuccessful
|
|
||||||
// NeoFS status codes are returned as `error`, otherwise, are included
|
|
||||||
// in the returned result structure.
|
|
||||||
func (c *Client) HeadObject(ctx context.Context, p *ObjectHeaderParams, opts ...CallOption) (*ObjectHeadRes, error) {
|
|
||||||
callOpts := c.defaultCallOptions()
|
|
||||||
|
|
||||||
for i := range opts {
|
|
||||||
if opts[i] != nil {
|
|
||||||
opts[i](callOpts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create request
|
|
||||||
req := new(v2object.HeadRequest)
|
|
||||||
|
|
||||||
// initialize request body
|
|
||||||
body := new(v2object.HeadRequestBody)
|
|
||||||
req.SetBody(body)
|
|
||||||
|
|
||||||
// set meta header
|
|
||||||
meta := v2MetaHeaderFromOpts(callOpts)
|
|
||||||
|
|
||||||
if err := c.attachV2SessionToken(callOpts, meta, v2SessionReqInfo{
|
|
||||||
addr: p.addr.ToV2(),
|
|
||||||
verb: v2session.ObjectVerbHead,
|
|
||||||
}); err != nil {
|
|
||||||
return nil, fmt.Errorf("could not attach session token: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.SetMetaHeader(meta)
|
|
||||||
|
|
||||||
// fill body fields
|
|
||||||
body.SetAddress(p.addr.ToV2())
|
|
||||||
body.SetMainOnly(p.short)
|
|
||||||
body.SetRaw(p.raw)
|
|
||||||
|
|
||||||
// sign the request
|
|
||||||
if err := signature.SignServiceMessage(callOpts.key, req); err != nil {
|
|
||||||
return nil, fmt.Errorf("signing the request failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// send Head request
|
|
||||||
resp, err := rpcapi.HeadObject(c.Raw(), req, client.WithContext(ctx))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("sending the request failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
res = new(ObjectHeadRes)
|
|
||||||
procPrm processResponseV2Prm
|
|
||||||
procRes processResponseV2Res
|
|
||||||
)
|
|
||||||
|
|
||||||
procPrm.callOpts = callOpts
|
|
||||||
procPrm.resp = resp
|
|
||||||
|
|
||||||
procRes.statusRes = res
|
|
||||||
|
|
||||||
// process response in general
|
|
||||||
if c.processResponseV2(&procRes, procPrm) {
|
|
||||||
if procRes.cliErr != nil {
|
|
||||||
return nil, procRes.cliErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
hdr *v2object.Header
|
|
||||||
idSig *v2refs.Signature
|
|
||||||
)
|
|
||||||
|
|
||||||
switch v := resp.GetBody().GetHeaderPart().(type) {
|
|
||||||
case nil:
|
|
||||||
writeUnexpectedMessageTypeErr(res, v)
|
|
||||||
return res, nil
|
|
||||||
case *v2object.ShortHeader:
|
|
||||||
if !p.short {
|
|
||||||
writeUnexpectedMessageTypeErr(res, v)
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
h := v
|
|
||||||
|
|
||||||
hdr = new(v2object.Header)
|
|
||||||
hdr.SetPayloadLength(h.GetPayloadLength())
|
|
||||||
hdr.SetVersion(h.GetVersion())
|
|
||||||
hdr.SetOwnerID(h.GetOwnerID())
|
|
||||||
hdr.SetObjectType(h.GetObjectType())
|
|
||||||
hdr.SetCreationEpoch(h.GetCreationEpoch())
|
|
||||||
hdr.SetPayloadHash(h.GetPayloadHash())
|
|
||||||
hdr.SetHomomorphicHash(h.GetHomomorphicHash())
|
|
||||||
case *v2object.HeaderWithSignature:
|
|
||||||
if p.short {
|
|
||||||
writeUnexpectedMessageTypeErr(res, v)
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
hdr = v.GetHeader()
|
|
||||||
idSig = v.GetSignature()
|
|
||||||
case *v2object.SplitInfo:
|
|
||||||
si := object.NewSplitInfoFromV2(v)
|
|
||||||
|
|
||||||
return nil, object.NewSplitInfoError(si)
|
|
||||||
}
|
|
||||||
|
|
||||||
obj := new(v2object.Object)
|
|
||||||
obj.SetHeader(hdr)
|
|
||||||
obj.SetSignature(idSig)
|
|
||||||
|
|
||||||
raw := object.NewRawFromV2(obj)
|
|
||||||
raw.SetID(p.addr.ObjectID())
|
|
||||||
|
|
||||||
res.setObject(raw.Object())
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *RangeDataParams) WithAddress(v *address.Address) *RangeDataParams {
|
func (p *RangeDataParams) WithAddress(v *address.Address) *RangeDataParams {
|
||||||
if p != nil {
|
if p != nil {
|
||||||
p.addr = v
|
p.addr = v
|
||||||
|
|
|
@ -20,8 +20,8 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/token"
|
"github.com/nspcc-dev/neofs-sdk-go/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PrmObjectGet groups parameters of ObjectGetInit operation.
|
// shared parameters of GET/HEAD/RANGE.
|
||||||
type PrmObjectGet struct {
|
type prmObjectRead struct {
|
||||||
raw bool
|
raw bool
|
||||||
|
|
||||||
local bool
|
local bool
|
||||||
|
@ -40,41 +40,55 @@ type PrmObjectGet struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarkRaw marks an intent to read physically stored object.
|
// MarkRaw marks an intent to read physically stored object.
|
||||||
func (x *PrmObjectGet) MarkRaw() {
|
func (x *prmObjectRead) MarkRaw() {
|
||||||
x.raw = true
|
x.raw = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarkLocal tells the server to execute the operation locally.
|
// MarkLocal tells the server to execute the operation locally.
|
||||||
func (x *PrmObjectGet) MarkLocal() {
|
func (x *prmObjectRead) MarkLocal() {
|
||||||
x.local = true
|
x.local = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithinSession specifies session within which object should be read.
|
// WithinSession specifies session within which object should be read.
|
||||||
func (x *PrmObjectGet) WithinSession(t session.Token) {
|
//
|
||||||
|
// Creator of the session acquires the authorship of the request.
|
||||||
|
// This may affect the execution of an operation (e.g. access control).
|
||||||
|
//
|
||||||
|
// Must be signed.
|
||||||
|
func (x *prmObjectRead) WithinSession(t session.Token) {
|
||||||
x.session = t
|
x.session = t
|
||||||
x.sessionSet = true
|
x.sessionSet = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithBearerToken attaches bearer token to be used for the operation.
|
// WithBearerToken attaches bearer token to be used for the operation.
|
||||||
func (x *PrmObjectGet) WithBearerToken(t token.BearerToken) {
|
//
|
||||||
|
// If set, underlying eACL rules will be used in access control.
|
||||||
|
//
|
||||||
|
// Must be signed.
|
||||||
|
func (x *prmObjectRead) WithBearerToken(t token.BearerToken) {
|
||||||
x.bearer = t
|
x.bearer = t
|
||||||
x.bearerSet = true
|
x.bearerSet = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromContainer specifies NeoFS container of the object.
|
// FromContainer specifies NeoFS container of the object.
|
||||||
// Required parameter.
|
// Required parameter.
|
||||||
func (x *PrmObjectGet) FromContainer(id cid.ID) {
|
func (x *prmObjectRead) FromContainer(id cid.ID) {
|
||||||
x.cnr = id
|
x.cnr = id
|
||||||
x.cnrSet = true
|
x.cnrSet = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByID specifies identifier of the requested object.
|
// ByID specifies identifier of the requested object.
|
||||||
// Required parameter.
|
// Required parameter.
|
||||||
func (x *PrmObjectGet) ByID(id oid.ID) {
|
func (x *prmObjectRead) ByID(id oid.ID) {
|
||||||
x.obj = id
|
x.obj = id
|
||||||
x.objSet = true
|
x.objSet = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrmObjectGet groups parameters of ObjectGetInit operation.
|
||||||
|
type PrmObjectGet struct {
|
||||||
|
prmObjectRead
|
||||||
|
}
|
||||||
|
|
||||||
// ResObjectGet groups the final result values of ObjectGetInit operation.
|
// ResObjectGet groups the final result values of ObjectGetInit operation.
|
||||||
type ResObjectGet struct {
|
type ResObjectGet struct {
|
||||||
statusRes
|
statusRes
|
||||||
|
@ -101,6 +115,10 @@ func (x *ObjectReader) UseKey(key ecdsa.PrivateKey) {
|
||||||
x.ctxCall.key = key
|
x.ctxCall.key = key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleSplitInfo(ctx *contextCall, i *v2object.SplitInfo) {
|
||||||
|
ctx.err = object.NewSplitInfoError(object.NewSplitInfoFromV2(i))
|
||||||
|
}
|
||||||
|
|
||||||
// ReadHeader reads header of the object. Result means success.
|
// ReadHeader reads header of the object. Result means success.
|
||||||
// Failure reason can be received via Close.
|
// Failure reason can be received via Close.
|
||||||
func (x *ObjectReader) ReadHeader(dst *object.Object) bool {
|
func (x *ObjectReader) ReadHeader(dst *object.Object) bool {
|
||||||
|
@ -117,7 +135,7 @@ func (x *ObjectReader) ReadHeader(dst *object.Object) bool {
|
||||||
x.ctxCall.err = fmt.Errorf("unexpected message instead of heading part: %T", v)
|
x.ctxCall.err = fmt.Errorf("unexpected message instead of heading part: %T", v)
|
||||||
return false
|
return false
|
||||||
case *v2object.SplitInfo:
|
case *v2object.SplitInfo:
|
||||||
x.ctxCall.err = object.NewSplitInfoError(object.NewSplitInfoFromV2(v))
|
handleSplitInfo(&x.ctxCall, v)
|
||||||
return false
|
return false
|
||||||
case *v2object.GetObjectPartInit:
|
case *v2object.GetObjectPartInit:
|
||||||
partInit = v
|
partInit = v
|
||||||
|
@ -231,7 +249,7 @@ func (x *ObjectReader) Read(p []byte) (int, error) {
|
||||||
|
|
||||||
// ObjectGetInit initiates reading an object through a remote server using NeoFS API protocol.
|
// ObjectGetInit initiates reading an object through a remote server using NeoFS API protocol.
|
||||||
//
|
//
|
||||||
// The call only opens the transmission channel, explicit fetching is done using the ObjectWriter.
|
// The call only opens the transmission channel, explicit fetching is done using the ObjectReader.
|
||||||
// Exactly one return value is non-nil. Resulting reader must be finally closed.
|
// Exactly one return value is non-nil. Resulting reader must be finally closed.
|
||||||
//
|
//
|
||||||
// Immediately panics if parameters are set incorrectly (see PrmObjectGet docs).
|
// Immediately panics if parameters are set incorrectly (see PrmObjectGet docs).
|
||||||
|
@ -311,3 +329,130 @@ func (c *Client) ObjectGetInit(ctx context.Context, prm PrmObjectGet) (*ObjectRe
|
||||||
|
|
||||||
return &r, nil
|
return &r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrmObjectHead groups parameters of ObjectHead operation.
|
||||||
|
type PrmObjectHead struct {
|
||||||
|
prmObjectRead
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResObjectHead groups resulting values of ObjectHead operation.
|
||||||
|
type ResObjectHead struct {
|
||||||
|
statusRes
|
||||||
|
|
||||||
|
// requested object (response doesn't carry the ID)
|
||||||
|
idObj oid.ID
|
||||||
|
|
||||||
|
hdr *v2object.HeaderWithSignature
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadHeader reads header of the requested object.
|
||||||
|
// Returns false if header is missing in the response (not read).
|
||||||
|
func (x *ResObjectHead) ReadHeader(dst *object.Object) bool {
|
||||||
|
if x.hdr == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var objv2 v2object.Object
|
||||||
|
|
||||||
|
objv2.SetHeader(x.hdr.GetHeader())
|
||||||
|
objv2.SetSignature(x.hdr.GetSignature())
|
||||||
|
|
||||||
|
raw := object.NewRawFromV2(&objv2)
|
||||||
|
raw.SetID(&x.idObj)
|
||||||
|
|
||||||
|
*dst = *raw.Object()
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObjectHead reads object header through a remote server using NeoFS API protocol.
|
||||||
|
//
|
||||||
|
// Exactly one return value is non-nil. By default, server status is returned in res structure.
|
||||||
|
// Any client's internal or transport errors are returned as `error`,
|
||||||
|
// If WithNeoFSErrorParsing option has been provided, unsuccessful
|
||||||
|
// NeoFS status codes are returned as `error`, otherwise, are included
|
||||||
|
// in the returned result structure.
|
||||||
|
//
|
||||||
|
// Immediately panics if parameters are set incorrectly (see PrmObjectHead docs).
|
||||||
|
// Context is required and must not be nil. It is used for network communication.
|
||||||
|
//
|
||||||
|
// Return errors:
|
||||||
|
// *object.SplitInfoError (returned on virtual objects with PrmObjectHead.MakeRaw).
|
||||||
|
//
|
||||||
|
// Return statuses:
|
||||||
|
// - global (see Client docs).
|
||||||
|
func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectHead, error) {
|
||||||
|
switch {
|
||||||
|
case ctx == nil:
|
||||||
|
panic(panicMsgMissingContext)
|
||||||
|
case !prm.cnrSet:
|
||||||
|
panic(panicMsgMissingContainer)
|
||||||
|
case !prm.objSet:
|
||||||
|
panic("missing object")
|
||||||
|
}
|
||||||
|
|
||||||
|
var addr v2refs.Address
|
||||||
|
|
||||||
|
addr.SetContainerID(prm.cnr.ToV2())
|
||||||
|
addr.SetObjectID(prm.obj.ToV2())
|
||||||
|
|
||||||
|
// form request body
|
||||||
|
var body v2object.HeadRequestBody
|
||||||
|
|
||||||
|
body.SetRaw(prm.raw)
|
||||||
|
body.SetAddress(&addr)
|
||||||
|
|
||||||
|
// form meta header
|
||||||
|
var meta v2session.RequestMetaHeader
|
||||||
|
|
||||||
|
if prm.local {
|
||||||
|
meta.SetTTL(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if prm.bearerSet {
|
||||||
|
meta.SetBearerToken(prm.bearer.ToV2())
|
||||||
|
}
|
||||||
|
|
||||||
|
if prm.sessionSet {
|
||||||
|
meta.SetSessionToken(prm.session.ToV2())
|
||||||
|
}
|
||||||
|
|
||||||
|
// form request
|
||||||
|
var req v2object.HeadRequest
|
||||||
|
|
||||||
|
req.SetBody(&body)
|
||||||
|
req.SetMetaHeader(&meta)
|
||||||
|
|
||||||
|
// init call context
|
||||||
|
|
||||||
|
var (
|
||||||
|
cc contextCall
|
||||||
|
res ResObjectHead
|
||||||
|
)
|
||||||
|
|
||||||
|
res.idObj = prm.obj
|
||||||
|
|
||||||
|
c.initCallContext(&cc)
|
||||||
|
cc.req = &req
|
||||||
|
cc.statusRes = &res
|
||||||
|
cc.call = func() (responseV2, error) {
|
||||||
|
return rpcapi.HeadObject(c.Raw(), &req, client.WithContext(ctx))
|
||||||
|
}
|
||||||
|
cc.result = func(r responseV2) {
|
||||||
|
switch v := r.(*v2object.HeadResponse).GetBody().GetHeaderPart().(type) {
|
||||||
|
default:
|
||||||
|
cc.err = fmt.Errorf("unexpected header type %T", v)
|
||||||
|
case *v2object.SplitInfo:
|
||||||
|
handleSplitInfo(&cc, v)
|
||||||
|
case *v2object.HeaderWithSignature:
|
||||||
|
res.hdr = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process call
|
||||||
|
if !cc.processCall() {
|
||||||
|
return nil, cc.err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
|
@ -260,15 +260,11 @@ func (mr *MockClientMockRecorder) HashObjectPayloadRanges(arg0, arg1 interface{}
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HashObjectPayloadRanges", reflect.TypeOf((*MockClient)(nil).HashObjectPayloadRanges), varargs...)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HashObjectPayloadRanges", reflect.TypeOf((*MockClient)(nil).HashObjectPayloadRanges), varargs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HeadObject mocks base method.
|
// ObjectHead mocks base method.
|
||||||
func (m *MockClient) HeadObject(arg0 context.Context, arg1 *client0.ObjectHeaderParams, arg2 ...client0.CallOption) (*client0.ObjectHeadRes, error) {
|
func (m *MockClient) ObjectHead(arg0 context.Context, arg1 client0.PrmObjectHead) (*client0.ResObjectHead, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
varargs := []interface{}{arg0, arg1}
|
ret := m.ctrl.Call(m, "HeadObject", arg0, arg1)
|
||||||
for _, a := range arg2 {
|
ret0, _ := ret[0].(*client0.ResObjectHead)
|
||||||
varargs = append(varargs, a)
|
|
||||||
}
|
|
||||||
ret := m.ctrl.Call(m, "HeadObject", varargs...)
|
|
||||||
ret0, _ := ret[0].(*client0.ObjectHeadRes)
|
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
@ -277,7 +273,7 @@ func (m *MockClient) HeadObject(arg0 context.Context, arg1 *client0.ObjectHeader
|
||||||
func (mr *MockClientMockRecorder) HeadObject(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
|
func (mr *MockClientMockRecorder) HeadObject(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
varargs := append([]interface{}{arg0, arg1}, arg2...)
|
varargs := append([]interface{}{arg0, arg1}, arg2...)
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HeadObject", reflect.TypeOf((*MockClient)(nil).HeadObject), varargs...)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HeadObject", reflect.TypeOf((*MockClient)(nil).ObjectHead), varargs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListContainers mocks base method.
|
// ListContainers mocks base method.
|
||||||
|
|
94
pool/pool.go
94
pool/pool.go
|
@ -46,7 +46,7 @@ type Client interface {
|
||||||
ObjectPutInit(context.Context, client.PrmObjectPutInit) (*client.ObjectWriter, error)
|
ObjectPutInit(context.Context, client.PrmObjectPutInit) (*client.ObjectWriter, error)
|
||||||
DeleteObject(context.Context, *client.DeleteObjectParams, ...client.CallOption) (*client.ObjectDeleteRes, error)
|
DeleteObject(context.Context, *client.DeleteObjectParams, ...client.CallOption) (*client.ObjectDeleteRes, error)
|
||||||
ObjectGetInit(context.Context, client.PrmObjectGet) (*client.ObjectReader, error)
|
ObjectGetInit(context.Context, client.PrmObjectGet) (*client.ObjectReader, error)
|
||||||
HeadObject(context.Context, *client.ObjectHeaderParams, ...client.CallOption) (*client.ObjectHeadRes, error)
|
ObjectHead(context.Context, client.PrmObjectHead) (*client.ResObjectHead, error)
|
||||||
ObjectPayloadRangeData(context.Context, *client.RangeDataParams, ...client.CallOption) (*client.ObjectRangeRes, error)
|
ObjectPayloadRangeData(context.Context, *client.RangeDataParams, ...client.CallOption) (*client.ObjectRangeRes, error)
|
||||||
HashObjectPayloadRanges(context.Context, *client.RangeChecksumParams, ...client.CallOption) (*client.ObjectRangeHashRes, error)
|
HashObjectPayloadRanges(context.Context, *client.RangeChecksumParams, ...client.CallOption) (*client.ObjectRangeHashRes, error)
|
||||||
SearchObjects(context.Context, *client.SearchObjectParams, ...client.CallOption) (*client.ObjectSearchRes, error)
|
SearchObjects(context.Context, *client.SearchObjectParams, ...client.CallOption) (*client.ObjectSearchRes, error)
|
||||||
|
@ -164,7 +164,7 @@ type Object interface {
|
||||||
PutObject(ctx context.Context, hdr object.Object, payload io.Reader, opts ...CallOption) (*oid.ID, error)
|
PutObject(ctx context.Context, hdr object.Object, payload io.Reader, opts ...CallOption) (*oid.ID, error)
|
||||||
DeleteObject(ctx context.Context, params *client.DeleteObjectParams, opts ...CallOption) error
|
DeleteObject(ctx context.Context, params *client.DeleteObjectParams, opts ...CallOption) error
|
||||||
GetObject(ctx context.Context, addr address.Address, opts ...CallOption) (*ResGetObject, error)
|
GetObject(ctx context.Context, addr address.Address, opts ...CallOption) (*ResGetObject, error)
|
||||||
GetObjectHeader(ctx context.Context, params *client.ObjectHeaderParams, opts ...CallOption) (*object.Object, error)
|
HeadObject(context.Context, address.Address, ...CallOption) (*object.Object, error)
|
||||||
ObjectPayloadRangeData(ctx context.Context, params *client.RangeDataParams, opts ...CallOption) ([]byte, error)
|
ObjectPayloadRangeData(ctx context.Context, params *client.RangeDataParams, opts ...CallOption) ([]byte, error)
|
||||||
ObjectPayloadRangeSHA256(ctx context.Context, params *client.RangeChecksumParams, opts ...CallOption) ([][32]byte, error)
|
ObjectPayloadRangeSHA256(ctx context.Context, params *client.RangeChecksumParams, opts ...CallOption) ([][32]byte, error)
|
||||||
ObjectPayloadRangeTZ(ctx context.Context, params *client.RangeChecksumParams, opts ...CallOption) ([][64]byte, error)
|
ObjectPayloadRangeTZ(ctx context.Context, params *client.RangeChecksumParams, opts ...CallOption) ([][64]byte, error)
|
||||||
|
@ -648,6 +648,8 @@ func (p *pool) initCallContextWithRetry(ctx *callContextWithRetry, cfg *callConf
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// opens new session or uses cached one.
|
||||||
|
// Must be called only on initialized callContext with set sessionTarget.
|
||||||
func (p *pool) openDefaultSession(ctx *callContext) error {
|
func (p *pool) openDefaultSession(ctx *callContext) error {
|
||||||
cacheKey := formCacheKey(ctx.endpoint, ctx.key)
|
cacheKey := formCacheKey(ctx.endpoint, ctx.key)
|
||||||
|
|
||||||
|
@ -684,14 +686,29 @@ func (p *pool) openDefaultSession(ctx *callContext) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pool) handleAttemptError(ctx *callContextWithRetry, err error) bool {
|
// opens default session (if sessionDefault is set), and calls f. If f returns
|
||||||
isTokenErr := p.checkSessionTokenErr(err, ctx.endpoint)
|
// session-related error (*), and retrying is enabled, then f is called once more.
|
||||||
// note that checkSessionTokenErr must be called
|
//
|
||||||
res := isTokenErr && !ctx.noRetry
|
// (*) in this case cached token is removed.
|
||||||
|
func (p *pool) callWithRetry(ctx *callContextWithRetry, f func() error) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
ctx.noRetry = true
|
if ctx.sessionDefault {
|
||||||
|
err = p.openDefaultSession(&ctx.callContext)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("open default session: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res
|
err = f()
|
||||||
|
|
||||||
|
if p.checkSessionTokenErr(err, ctx.endpoint) && !ctx.noRetry {
|
||||||
|
// don't retry anymore
|
||||||
|
ctx.noRetry = true
|
||||||
|
return p.callWithRetry(ctx, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pool) PutObject(ctx context.Context, hdr object.Object, payload io.Reader, opts ...CallOption) (*oid.ID, error) {
|
func (p *pool) PutObject(ctx context.Context, hdr object.Object, payload io.Reader, opts ...CallOption) (*oid.ID, error) {
|
||||||
|
@ -836,27 +853,6 @@ type ResGetObject struct {
|
||||||
Payload io.ReadCloser
|
Payload io.ReadCloser
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pool) callWithRetry(ctx *callContextWithRetry, f func() error) error {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if ctx.sessionDefault {
|
|
||||||
err = p.openDefaultSession(&ctx.callContext)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("open default session: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = f()
|
|
||||||
|
|
||||||
if p.checkSessionTokenErr(err, ctx.endpoint) && !ctx.noRetry {
|
|
||||||
// don't retry anymore
|
|
||||||
ctx.noRetry = true
|
|
||||||
return p.callWithRetry(ctx, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pool) GetObject(ctx context.Context, addr address.Address, opts ...CallOption) (*ResGetObject, error) {
|
func (p *pool) GetObject(ctx context.Context, addr address.Address, opts ...CallOption) (*ResGetObject, error) {
|
||||||
cfg := cfgFromOpts(append(opts, useDefaultSession())...)
|
cfg := cfgFromOpts(append(opts, useDefaultSession())...)
|
||||||
|
|
||||||
|
@ -903,25 +899,45 @@ func (p *pool) GetObject(ctx context.Context, addr address.Address, opts ...Call
|
||||||
return &res, nil
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pool) GetObjectHeader(ctx context.Context, params *client.ObjectHeaderParams, opts ...CallOption) (*object.Object, error) {
|
func (p *pool) HeadObject(ctx context.Context, addr address.Address, opts ...CallOption) (*object.Object, error) {
|
||||||
cfg := cfgFromOpts(append(opts, useDefaultSession())...)
|
cfg := cfgFromOpts(append(opts, useDefaultSession())...)
|
||||||
cp, options, err := p.conn(ctx, cfg)
|
|
||||||
|
var prm client.PrmObjectHead
|
||||||
|
|
||||||
|
var cc callContextWithRetry
|
||||||
|
|
||||||
|
cc.Context = ctx
|
||||||
|
cc.sessionTarget = prm.WithinSession
|
||||||
|
|
||||||
|
err := p.initCallContextWithRetry(&cc, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := cp.client.HeadObject(ctx, params, options...)
|
if cnr := addr.ContainerID(); cnr != nil {
|
||||||
|
prm.FromContainer(*cnr)
|
||||||
if p.checkSessionTokenErr(err, cp.address) && !cfg.isRetry {
|
|
||||||
opts = append(opts, retry())
|
|
||||||
return p.GetObjectHeader(ctx, params, opts...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil { // here err already carries both status and client errors
|
if obj := addr.ObjectID(); obj != nil {
|
||||||
return nil, err
|
prm.ByID(*obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.Object(), nil
|
var obj object.Object
|
||||||
|
|
||||||
|
err = p.callWithRetry(&cc, func() error {
|
||||||
|
res, err := cc.client.ObjectHead(ctx, prm)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("read object header via client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !res.ReadHeader(&obj) {
|
||||||
|
return errors.New("missing object header in response")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return &obj, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pool) ObjectPayloadRangeData(ctx context.Context, params *client.RangeDataParams, opts ...CallOption) ([]byte, error) {
|
func (p *pool) ObjectPayloadRangeData(ctx context.Context, params *client.RangeDataParams, opts ...CallOption) ([]byte, error) {
|
||||||
|
|
Loading…
Reference in a new issue