2023-03-31 14:41:42 +00:00
|
|
|
package getsvc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-04-04 10:20:57 +00:00
|
|
|
"crypto/ecdsa"
|
2023-03-31 14:41:42 +00:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
|
2023-03-13 11:37:35 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/pkg/tracing"
|
2023-03-31 14:41:42 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc"
|
|
|
|
rpcclient "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/client"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/network"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/internal"
|
|
|
|
internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/internal/client"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
2023-03-13 11:37:35 +00:00
|
|
|
"go.opentelemetry.io/otel/attribute"
|
|
|
|
"go.opentelemetry.io/otel/trace"
|
2023-03-31 14:41:42 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type getRangeRequestForwarder struct {
|
|
|
|
OnceResign *sync.Once
|
|
|
|
GlobalProgress int
|
2023-04-04 10:20:57 +00:00
|
|
|
Key *ecdsa.PrivateKey
|
2023-03-31 14:41:42 +00:00
|
|
|
Request *objectV2.GetRangeRequest
|
|
|
|
Stream *streamObjectRangeWriter
|
|
|
|
}
|
|
|
|
|
2023-04-03 08:17:59 +00:00
|
|
|
func (f *getRangeRequestForwarder) forwardRequestToNode(ctx context.Context, addr network.Address, c client.MultiAddressClient, pubkey []byte) (*object.Object, error) {
|
2023-03-13 11:37:35 +00:00
|
|
|
ctx, span := tracing.StartSpanFromContext(ctx, "getRangeRequestForwarder.forwardRequestToNode",
|
|
|
|
trace.WithAttributes(attribute.String("address", addr.String())),
|
|
|
|
)
|
|
|
|
defer span.End()
|
|
|
|
|
2023-04-04 10:20:57 +00:00
|
|
|
var err error
|
2023-03-31 14:41:42 +00:00
|
|
|
|
|
|
|
// once compose and resign forwarding request
|
|
|
|
f.OnceResign.Do(func() {
|
|
|
|
// compose meta header of the local server
|
|
|
|
metaHdr := new(session.RequestMetaHeader)
|
|
|
|
metaHdr.SetTTL(f.Request.GetMetaHeader().GetTTL() - 1)
|
|
|
|
// TODO: #1165 think how to set the other fields
|
|
|
|
metaHdr.SetOrigin(f.Request.GetMetaHeader())
|
|
|
|
writeCurrentVersion(metaHdr)
|
|
|
|
|
|
|
|
f.Request.SetMetaHeader(metaHdr)
|
|
|
|
|
2023-04-04 10:20:57 +00:00
|
|
|
err = signature.SignServiceMessage(f.Key, f.Request)
|
2023-03-31 14:41:42 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
rangeStream, err := f.openStream(ctx, addr, c)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, f.readStream(ctx, rangeStream, c, pubkey)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *getRangeRequestForwarder) verifyResponse(resp *objectV2.GetRangeResponse, pubkey []byte) error {
|
|
|
|
// verify response key
|
|
|
|
if err := internal.VerifyResponseKeyV2(pubkey, resp); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// verify response structure
|
|
|
|
if err := signature.VerifyServiceMessage(resp); err != nil {
|
|
|
|
return fmt.Errorf("could not verify %T: %w", resp, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := checkStatus(resp.GetMetaHeader().GetStatus()); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *getRangeRequestForwarder) openStream(ctx context.Context, addr network.Address, c client.MultiAddressClient) (*rpc.ObjectRangeResponseReader, error) {
|
|
|
|
// open stream
|
|
|
|
var rangeStream *rpc.ObjectRangeResponseReader
|
|
|
|
err := c.RawForAddress(addr, func(cli *rpcclient.Client) error {
|
|
|
|
var e error
|
|
|
|
rangeStream, e = rpc.GetObjectRange(cli, f.Request, rpcclient.WithContext(ctx))
|
|
|
|
return e
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not create Get payload range stream: %w", err)
|
|
|
|
}
|
|
|
|
return rangeStream, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *getRangeRequestForwarder) readStream(ctx context.Context, rangeStream *rpc.ObjectRangeResponseReader, c client.MultiAddressClient, pubkey []byte) error {
|
|
|
|
resp := new(objectV2.GetRangeResponse)
|
|
|
|
var localProgress int
|
|
|
|
|
|
|
|
for {
|
|
|
|
// receive message from server stream
|
|
|
|
err := rangeStream.Read(resp)
|
|
|
|
if err != nil {
|
|
|
|
if errors.Is(err, io.EOF) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
internalclient.ReportError(c, err)
|
|
|
|
return fmt.Errorf("reading the response failed: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := f.verifyResponse(resp, pubkey); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
switch v := resp.GetBody().GetRangePart().(type) {
|
|
|
|
case nil:
|
|
|
|
return fmt.Errorf("unexpected range type %T", v)
|
|
|
|
case *objectV2.GetRangePartChunk:
|
|
|
|
origChunk := v.GetChunk()
|
|
|
|
|
|
|
|
chunk := chunkToSend(f.GlobalProgress, localProgress, origChunk)
|
|
|
|
if len(chunk) == 0 {
|
|
|
|
localProgress += len(origChunk)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = f.Stream.WriteChunk(ctx, chunk); err != nil {
|
|
|
|
return fmt.Errorf("could not write object chunk in GetRange forwarder: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
localProgress += len(origChunk)
|
|
|
|
f.GlobalProgress += len(chunk)
|
|
|
|
case *objectV2.SplitInfo:
|
|
|
|
si := object.NewSplitInfoFromV2(v)
|
|
|
|
return object.NewSplitInfoError(si)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|