Skip to content
2 changes: 2 additions & 0 deletions cmd/neofs-node/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net"
"time"

iprotobuf "github.com/nspcc-dev/neofs-node/internal/protobuf"
"github.com/nspcc-dev/neofs-sdk-go/object"
"go.uber.org/zap"
"golang.org/x/net/netutil"
Expand Down Expand Up @@ -61,6 +62,7 @@ func initGRPC(c *cfg) {
MinTime: 5 * time.Second, // w/o this server sends GoAway with ENHANCE_YOUR_CALM code "too_many_pings"
PermitWithoutStream: true,
}),
grpc.ForceServerCodecV2(iprotobuf.BufferedCodec{}),
}
if maxRecvMsgSizeOpt != nil {
// TODO(@cthulhu-rider): the setting can be server-global only now, support
Expand Down
21 changes: 20 additions & 1 deletion cmd/neofs-node/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"math"
"slices"
"sync"
"sync/atomic"
"time"
Expand Down Expand Up @@ -52,6 +53,7 @@ import (
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/nspcc-dev/neofs-sdk-go/version"
"go.uber.org/zap"
"google.golang.org/grpc"
)

type objectSvc struct {
Expand Down Expand Up @@ -315,8 +317,25 @@ func initObjectService(c *cfg) {
server := objectService.New(objSvc, mNumber, c.cfgObject.pool.search, fsChain, storage, c.metaService, c.key.PrivateKey, c.metricsCollector, aclChecker, aclSvc, coreConstructor)
os.server = server

svcDesc := protoobject.ObjectService_ServiceDesc
svcDesc.Methods = slices.Clone(protoobject.ObjectService_ServiceDesc.Methods)

const headMethod = "Head"
headInd := slices.IndexFunc(svcDesc.Methods, func(md grpc.MethodDesc) bool { return md.MethodName == headMethod })
if headInd < 0 {
fatalOnErr(fmt.Errorf("missing %s method handler in object service desc", headMethod))
}

svcDesc.Methods[headInd].Handler = func(_ any, ctx context.Context, dec func(any) error, _ grpc.UnaryServerInterceptor) (any, error) {
req := new(protoobject.HeadRequest)
if err := dec(req); err != nil {
return nil, err
}
return server.HeadBuffered(ctx, req)
}

for _, srv := range c.cfgGRPC.servers {
protoobject.RegisterObjectServiceServer(srv, server)
srv.RegisterService(&svcDesc, server)
}
}

Expand Down
166 changes: 166 additions & 0 deletions internal/object/wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ import (
"fmt"
"io"

iprotobuf "github.com/nspcc-dev/neofs-node/internal/protobuf"
"github.com/nspcc-dev/neofs-sdk-go/object"
protoobject "github.com/nspcc-dev/neofs-sdk-go/proto/object"
"github.com/nspcc-dev/neofs-sdk-go/proto/refs"
"google.golang.org/protobuf/encoding/protowire"
"google.golang.org/protobuf/proto"
)

// MaxHeaderVarintLen is varint len of [object.MaxHeaderLen].
// TODO: unit test.
const MaxHeaderVarintLen = 3 // object.MaxHeaderLen

// Protobuf field numbers for object message.
const (
_ = iota
Expand All @@ -21,6 +26,35 @@ const (
fieldObjectPayload
)

// Protobuf field numbers for header message.
const (
_ = iota
FieldHeaderVersion
FieldHeaderContainerID
FieldHeaderOwnerID
FieldHeaderCreationEpoch
FieldHeaderPayloadLength
FieldHeaderPayloadHash
FieldHeaderType
FieldHeaderHomoHash
FieldHeaderSessionToken
FieldHeaderAttributes
FieldHeaderSplit
FieldHeaderSessionTokenV2
)

// Protobuf field numbers for split header message.
const (
_ = iota
FieldHeaderSplitParent
FieldHeaderSplitPrevious
FieldHeaderSplitParentSignature
FieldHeaderSplitParentHeader
/* FieldHeaderSplitChildren */ _
/* FieldHeaderSplitSplitID */ _
/* FieldHeaderSplitFirst */ _
)

// WriteWithoutPayload writes the object header to the given writer without the payload.
func WriteWithoutPayload(w io.Writer, obj object.Object) error {
header := obj.CutPayload().Marshal()
Expand Down Expand Up @@ -108,3 +142,135 @@ func ReadHeaderPrefix(r io.Reader) (*object.Object, []byte, error) {
}
return ExtractHeaderAndPayload(buf[:n])
}

// SeekParentHeaderFields seeks parent ID, signature and header in object
// message with direct field order.
func SeekParentHeaderFields(b []byte) (iprotobuf.FieldBounds, iprotobuf.FieldBounds, iprotobuf.FieldBounds, error) {
var idf, sigf, hdrf iprotobuf.FieldBounds

rootHdrf, err := iprotobuf.SeekBytesField(b, fieldObjectHeader)
if err != nil {
return idf, sigf, hdrf, err
}

if rootHdrf.IsMissing() {
return idf, sigf, hdrf, nil
}

splitf, err := iprotobuf.SeekBytesField(b[rootHdrf.ValueFrom:rootHdrf.To], FieldHeaderSplit)
if err != nil {
return idf, sigf, hdrf, err
}

if splitf.IsMissing() {
return idf, sigf, hdrf, nil
}

b = b[:rootHdrf.ValueFrom+splitf.To]
off := rootHdrf.ValueFrom + splitf.ValueFrom
var prevNum protowire.Number
loop:
for {
num, typ, n, err := iprotobuf.ParseTag(b, off)
if err != nil {
return idf, sigf, hdrf, err
}

if num < prevNum {
return idf, sigf, hdrf, iprotobuf.NewUnorderedFieldsError(prevNum, num)
}
if num == prevNum && num <= FieldHeaderSplitParentHeader {
return idf, sigf, hdrf, iprotobuf.NewRepeatedFieldError(num)
}
prevNum = num

switch num {
case FieldHeaderSplitParent:
idf, err = iprotobuf.ParseLenFieldBounds(b, off, n, num, typ)
if err != nil {
return idf, sigf, hdrf, err
}
off = idf.To
case FieldHeaderSplitPrevious:
off += n
ln, n, err := iprotobuf.ParseLenField(b, off, num, typ)
if err != nil {
return idf, sigf, hdrf, err
}
off += n + ln
case FieldHeaderSplitParentSignature:
sigf, err = iprotobuf.ParseLenFieldBounds(b, off, n, num, typ)
if err != nil {
return idf, sigf, hdrf, err
}
off = sigf.To
case FieldHeaderSplitParentHeader:
hdrf, err = iprotobuf.ParseLenFieldBounds(b, off, n, num, typ)
if err != nil {
return idf, sigf, hdrf, err
}
break loop
default:
break loop
}

if off == len(b) {
break
}
}

return idf, sigf, hdrf, nil
}

// SeekParentHeaderFields seeks ID, signature and header in object message with
// direct field order.
func SeekHeaderFields(b []byte) (iprotobuf.FieldBounds, iprotobuf.FieldBounds, iprotobuf.FieldBounds, error) {
var idf, sigf, hdrf iprotobuf.FieldBounds
var off int
var prevNum protowire.Number
loop:
for {
num, typ, n, err := iprotobuf.ParseTag(b, off)
if err != nil {
return idf, sigf, hdrf, err
}

if num < prevNum {
return idf, sigf, hdrf, iprotobuf.NewUnorderedFieldsError(prevNum, num)
}
if num == prevNum {
return idf, sigf, hdrf, iprotobuf.NewRepeatedFieldError(num)
}
prevNum = num

switch num {
case fieldObjectID:
idf, err = iprotobuf.ParseLenFieldBounds(b, off, n, num, typ)
if err != nil {
return idf, sigf, hdrf, err
}
off = idf.To
case fieldObjectSignature:
sigf, err = iprotobuf.ParseLenFieldBounds(b, off, n, num, typ)
if err != nil {
return idf, sigf, hdrf, err
}
off = sigf.To
case fieldObjectHeader:
hdrf, err = iprotobuf.ParseLenFieldBounds(b, off, n, num, typ)
if err != nil {
return idf, sigf, hdrf, err
}

break loop
default:
break loop
}

if off == len(b) {
break
}
}

return idf, sigf, hdrf, nil
}
Loading
Loading