140 lines
4.2 KiB
Go
140 lines
4.2 KiB
Go
package client
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client"
|
|
v2session "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/signature"
|
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
|
|
)
|
|
|
|
// structure is embedded to all resulting types in order to inherit status-related methods.
|
|
type statusRes struct {
|
|
st apistatus.Status
|
|
}
|
|
|
|
// Status returns server's status return.
|
|
//
|
|
// Use apistatus package functionality to handle the status.
|
|
func (x statusRes) Status() apistatus.Status {
|
|
return x.st
|
|
}
|
|
|
|
// groups meta parameters shared between all Client operations.
|
|
type prmCommonMeta struct {
|
|
// FrostFS request X-Headers
|
|
xHeaders []string
|
|
}
|
|
|
|
// WithXHeaders specifies list of extended headers (string key-value pairs)
|
|
// to be attached to the request. Must have an even length.
|
|
//
|
|
// Slice must not be mutated until the operation completes.
|
|
func (x *prmCommonMeta) WithXHeaders(hs ...string) {
|
|
if len(hs)%2 != 0 {
|
|
panic("slice of X-Headers with odd length")
|
|
}
|
|
|
|
x.xHeaders = hs
|
|
}
|
|
|
|
func writeXHeadersToMeta(xHeaders []string, h *v2session.RequestMetaHeader) {
|
|
if len(xHeaders) == 0 {
|
|
return
|
|
}
|
|
|
|
if len(xHeaders)%2 != 0 {
|
|
panic("slice of X-Headers with odd length")
|
|
}
|
|
|
|
hs := make([]v2session.XHeader, len(xHeaders)/2)
|
|
for i := 0; i < len(xHeaders); i += 2 {
|
|
hs[i].SetKey(xHeaders[i])
|
|
hs[i].SetValue(xHeaders[i+1])
|
|
}
|
|
|
|
h.SetXHeaders(hs)
|
|
}
|
|
|
|
// error messages.
|
|
var (
|
|
errorMissingContainer = errors.New("missing container")
|
|
errorMissingObject = errors.New("missing object")
|
|
errorAccountNotSet = errors.New("account not set")
|
|
errorServerAddrUnset = errors.New("server address is unset or empty")
|
|
errorNonPositiveTimeout = errors.New("non-positive timeout")
|
|
errorEACLTableNotSet = errors.New("eACL table not set")
|
|
errorMissingAnnouncements = errors.New("missing announcements")
|
|
errorZeroRangeLength = errors.New("zero range length")
|
|
errorMissingRanges = errors.New("missing ranges")
|
|
errorInvalidXHeaders = errors.New("xheaders must be presented only as key-value pairs")
|
|
)
|
|
|
|
type request interface {
|
|
GetMetaHeader() *v2session.RequestMetaHeader
|
|
SetMetaHeader(*v2session.RequestMetaHeader)
|
|
SetVerificationHeader(*v2session.RequestVerificationHeader)
|
|
}
|
|
|
|
func (c *Client) prepareRequest(req request, meta *v2session.RequestMetaHeader) {
|
|
ttl := meta.GetTTL()
|
|
if ttl == 0 {
|
|
ttl = 2
|
|
}
|
|
|
|
verV2 := meta.GetVersion()
|
|
if verV2 == nil {
|
|
verV2 = new(refs.Version)
|
|
version.Current().WriteToV2(verV2)
|
|
}
|
|
|
|
meta.SetTTL(ttl)
|
|
meta.SetVersion(verV2)
|
|
meta.SetNetworkMagic(c.prm.netMagic)
|
|
|
|
req.SetMetaHeader(meta)
|
|
}
|
|
|
|
// processResponse verifies response signature and converts status to an error if needed.
|
|
func (c *Client) processResponse(resp responseV2) (apistatus.Status, error) {
|
|
if c.prm.cbRespInfo != nil {
|
|
rmi := ResponseMetaInfo{
|
|
key: resp.GetVerificationHeader().GetBodySignature().GetKey(),
|
|
epoch: resp.GetMetaHeader().GetEpoch(),
|
|
}
|
|
if err := c.prm.cbRespInfo(rmi); err != nil {
|
|
return nil, fmt.Errorf("response callback error: %w", err)
|
|
}
|
|
}
|
|
|
|
err := signature.VerifyServiceMessage(resp)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid response signature: %w", err)
|
|
}
|
|
|
|
st := apistatus.FromStatusV2(resp.GetMetaHeader().GetStatus())
|
|
if c.prm.resolveFrostFSErrors {
|
|
return st, apistatus.ErrFromStatus(st)
|
|
}
|
|
return st, nil
|
|
}
|
|
|
|
// ExecRaw executes f with underlying git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client.Client
|
|
// instance. Communicate over the Protocol Buffers protocol in a more flexible way:
|
|
// most often used to transmit data over a fixed version of the FrostFS protocol, as well
|
|
// as to support custom services.
|
|
//
|
|
// The f must not manipulate the client connection passed into it.
|
|
//
|
|
// Like all other operations, must be called after connecting to the server and
|
|
// before closing the connection.
|
|
//
|
|
// See also Dial and Close.
|
|
// See also git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/rpc/client package docs.
|
|
func (c *Client) ExecRaw(f func(client *client.Client) error) error {
|
|
return f(&c.c)
|
|
}
|