2020-08-24 10:05:10 +00:00
|
|
|
package util
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto/ecdsa"
|
2021-11-06 11:13:04 +00:00
|
|
|
"errors"
|
2021-05-18 08:12:51 +00:00
|
|
|
"fmt"
|
2020-08-24 10:05:10 +00:00
|
|
|
|
2020-10-22 11:02:40 +00:00
|
|
|
"github.com/nspcc-dev/neofs-api-go/v2/session"
|
2020-08-24 10:05:10 +00:00
|
|
|
"github.com/nspcc-dev/neofs-api-go/v2/signature"
|
2021-11-06 11:13:04 +00:00
|
|
|
apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status"
|
2020-08-24 10:05:10 +00:00
|
|
|
)
|
|
|
|
|
2021-11-06 11:13:04 +00:00
|
|
|
type RequestMessage interface {
|
|
|
|
GetMetaHeader() *session.RequestMetaHeader
|
|
|
|
}
|
|
|
|
|
2020-10-22 08:03:34 +00:00
|
|
|
// ResponseMessage is an interface of NeoFS response message.
|
2020-10-22 11:02:40 +00:00
|
|
|
type ResponseMessage interface {
|
|
|
|
GetMetaHeader() *session.ResponseMetaHeader
|
|
|
|
SetMetaHeader(*session.ResponseMetaHeader)
|
|
|
|
}
|
2020-10-22 08:03:34 +00:00
|
|
|
|
|
|
|
type UnaryHandler func(context.Context, interface{}) (ResponseMessage, error)
|
2020-08-24 10:05:10 +00:00
|
|
|
|
2020-08-26 09:43:39 +00:00
|
|
|
type SignService struct {
|
2020-08-24 10:05:10 +00:00
|
|
|
key *ecdsa.PrivateKey
|
|
|
|
}
|
|
|
|
|
2020-12-02 23:45:25 +00:00
|
|
|
type ResponseMessageWriter func(ResponseMessage) error
|
|
|
|
|
2020-08-26 10:00:41 +00:00
|
|
|
type ServerStreamHandler func(context.Context, interface{}) (ResponseMessageReader, error)
|
2020-08-26 09:41:43 +00:00
|
|
|
|
2020-10-22 08:03:34 +00:00
|
|
|
type ResponseMessageReader func() (ResponseMessage, error)
|
2020-08-26 09:41:43 +00:00
|
|
|
|
2021-11-06 11:13:04 +00:00
|
|
|
var ErrAbortStream = errors.New("abort message stream")
|
2020-08-26 09:41:43 +00:00
|
|
|
|
2021-11-06 11:13:04 +00:00
|
|
|
type ResponseConstructor func() ResponseMessage
|
2020-08-26 10:00:41 +00:00
|
|
|
|
|
|
|
type RequestMessageWriter func(interface{}) error
|
|
|
|
|
2020-10-22 08:03:34 +00:00
|
|
|
type ClientStreamCloser func() (ResponseMessage, error)
|
2020-08-26 10:00:41 +00:00
|
|
|
|
|
|
|
type RequestMessageStreamer struct {
|
|
|
|
key *ecdsa.PrivateKey
|
|
|
|
|
|
|
|
send RequestMessageWriter
|
|
|
|
|
|
|
|
close ClientStreamCloser
|
2021-11-06 11:13:04 +00:00
|
|
|
|
|
|
|
respCons ResponseConstructor
|
|
|
|
|
|
|
|
statusSupported bool
|
|
|
|
|
|
|
|
sendErr error
|
2020-08-26 09:41:43 +00:00
|
|
|
}
|
|
|
|
|
2020-08-26 09:43:39 +00:00
|
|
|
func NewUnarySignService(key *ecdsa.PrivateKey) *SignService {
|
|
|
|
return &SignService{
|
2020-08-26 09:20:26 +00:00
|
|
|
key: key,
|
2020-08-24 10:05:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-26 10:00:41 +00:00
|
|
|
func (s *RequestMessageStreamer) Send(req interface{}) error {
|
2021-11-06 11:13:04 +00:00
|
|
|
// req argument should be strengthen with type RequestMessage
|
|
|
|
s.statusSupported = isStatusSupported(req.(RequestMessage)) // panic is OK here for now
|
|
|
|
|
|
|
|
var err error
|
|
|
|
|
2020-08-26 10:00:41 +00:00
|
|
|
// verify request signatures
|
2021-11-06 11:13:04 +00:00
|
|
|
if err = signature.VerifyServiceMessage(req); err != nil {
|
|
|
|
err = fmt.Errorf("could not verify request: %w", err)
|
|
|
|
} else {
|
|
|
|
err = s.send(req)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if !s.statusSupported {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
s.sendErr = err
|
|
|
|
|
|
|
|
return ErrAbortStream
|
2020-08-26 10:00:41 +00:00
|
|
|
}
|
|
|
|
|
2021-11-06 11:13:04 +00:00
|
|
|
return nil
|
2020-08-26 10:00:41 +00:00
|
|
|
}
|
|
|
|
|
2020-10-22 11:02:40 +00:00
|
|
|
func (s *RequestMessageStreamer) CloseAndRecv() (ResponseMessage, error) {
|
2021-11-06 11:13:04 +00:00
|
|
|
var (
|
|
|
|
resp ResponseMessage
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
if s.sendErr != nil {
|
|
|
|
err = s.sendErr
|
|
|
|
} else {
|
|
|
|
resp, err = s.close()
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("could not close stream and receive response: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-26 10:00:41 +00:00
|
|
|
if err != nil {
|
2021-11-06 11:13:04 +00:00
|
|
|
if !s.statusSupported {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var st apistatus.ServerInternal // specific API status should be set according to error
|
|
|
|
|
|
|
|
apistatus.WriteInternalServerErr(&st, err)
|
|
|
|
|
|
|
|
resp = s.respCons()
|
|
|
|
|
|
|
|
setStatusV2(resp, st)
|
2020-08-26 10:00:41 +00:00
|
|
|
}
|
|
|
|
|
2021-11-06 11:13:04 +00:00
|
|
|
if err = signResponse(s.key, resp, s.statusSupported); err != nil {
|
|
|
|
return nil, err
|
2020-08-26 10:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2021-11-06 11:13:04 +00:00
|
|
|
func (s *SignService) CreateRequestStreamer(sender RequestMessageWriter, closer ClientStreamCloser, blankResp ResponseConstructor) *RequestMessageStreamer {
|
2020-08-26 10:00:41 +00:00
|
|
|
return &RequestMessageStreamer{
|
|
|
|
key: s.key,
|
|
|
|
send: sender,
|
|
|
|
close: closer,
|
2021-11-06 11:13:04 +00:00
|
|
|
|
|
|
|
respCons: blankResp,
|
2020-08-26 10:00:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-06 11:13:04 +00:00
|
|
|
func (s *SignService) HandleServerStreamRequest(
|
|
|
|
req interface{},
|
|
|
|
respWriter ResponseMessageWriter,
|
|
|
|
blankResp ResponseConstructor,
|
|
|
|
respWriterCaller func(ResponseMessageWriter) error,
|
|
|
|
) error {
|
|
|
|
// handle protocol versions <=2.10 (API statuses was introduced in 2.11 only)
|
2020-08-26 09:41:43 +00:00
|
|
|
|
2021-11-06 11:13:04 +00:00
|
|
|
// req argument should be strengthen with type RequestMessage
|
|
|
|
statusSupported := isStatusSupported(req.(RequestMessage)) // panic is OK here for now
|
2020-08-26 09:41:43 +00:00
|
|
|
|
2021-11-06 11:13:04 +00:00
|
|
|
var err error
|
2020-08-25 13:01:16 +00:00
|
|
|
|
2020-12-02 23:45:25 +00:00
|
|
|
// verify request signatures
|
2021-11-06 11:13:04 +00:00
|
|
|
if err = signature.VerifyServiceMessage(req); err != nil {
|
|
|
|
err = fmt.Errorf("could not verify request: %w", err)
|
|
|
|
} else {
|
|
|
|
err = respWriterCaller(func(resp ResponseMessage) error {
|
|
|
|
if err := signResponse(s.key, resp, statusSupported); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return respWriter(resp)
|
|
|
|
})
|
2020-12-02 23:45:25 +00:00
|
|
|
}
|
|
|
|
|
2021-11-06 11:13:04 +00:00
|
|
|
if err != nil {
|
|
|
|
if !statusSupported {
|
|
|
|
return err
|
2020-12-02 23:45:25 +00:00
|
|
|
}
|
|
|
|
|
2021-11-06 11:13:04 +00:00
|
|
|
var st apistatus.ServerInternal // specific API status should be set according to error
|
|
|
|
|
|
|
|
apistatus.WriteInternalServerErr(&st, err)
|
|
|
|
|
|
|
|
resp := blankResp()
|
|
|
|
|
|
|
|
setStatusV2(resp, st)
|
|
|
|
|
|
|
|
_ = signResponse(s.key, resp, false) // panics or returns nil with false arg
|
|
|
|
|
2020-12-02 23:45:25 +00:00
|
|
|
return respWriter(resp)
|
2021-11-06 11:13:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2020-12-02 23:45:25 +00:00
|
|
|
}
|
|
|
|
|
2021-11-06 11:13:04 +00:00
|
|
|
func (s *SignService) HandleUnaryRequest(ctx context.Context, req interface{}, handler UnaryHandler, blankResp ResponseConstructor) (ResponseMessage, error) {
|
|
|
|
// handle protocol versions <=2.10 (API statuses was introduced in 2.11 only)
|
|
|
|
|
|
|
|
// req argument should be strengthen with type RequestMessage
|
|
|
|
statusSupported := isStatusSupported(req.(RequestMessage)) // panic is OK here for now
|
|
|
|
|
|
|
|
var (
|
|
|
|
resp ResponseMessage
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
2020-08-24 10:05:10 +00:00
|
|
|
// verify request signatures
|
2021-11-06 11:13:04 +00:00
|
|
|
if err = signature.VerifyServiceMessage(req); err != nil {
|
|
|
|
err = fmt.Errorf("could not verify request: %w", err)
|
|
|
|
} else {
|
|
|
|
// process request
|
|
|
|
resp, err = handler(ctx, req)
|
2020-08-24 10:05:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
2021-11-06 11:13:04 +00:00
|
|
|
if !statusSupported {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var st apistatus.ServerInternal // specific API status should be set according to error
|
|
|
|
|
|
|
|
apistatus.WriteInternalServerErr(&st, err)
|
|
|
|
|
|
|
|
resp = blankResp()
|
|
|
|
|
|
|
|
setStatusV2(resp, st)
|
2020-08-24 10:05:10 +00:00
|
|
|
}
|
|
|
|
|
2020-08-26 09:41:43 +00:00
|
|
|
// sign the response
|
2021-11-06 11:13:04 +00:00
|
|
|
if err = signResponse(s.key, resp, statusSupported); err != nil {
|
|
|
|
return nil, err
|
2020-08-26 09:41:43 +00:00
|
|
|
}
|
|
|
|
|
2020-08-24 10:05:10 +00:00
|
|
|
return resp, nil
|
|
|
|
}
|
2021-11-06 11:13:04 +00:00
|
|
|
|
|
|
|
func isStatusSupported(req RequestMessage) bool {
|
|
|
|
version := req.GetMetaHeader().GetVersion()
|
|
|
|
|
|
|
|
mjr := version.GetMajor()
|
|
|
|
|
|
|
|
return mjr > 2 || mjr == 2 && version.GetMinor() >= 11
|
|
|
|
}
|
|
|
|
|
|
|
|
func setStatusV2(resp ResponseMessage, st apistatus.Status) {
|
|
|
|
session.SetStatus(resp, apistatus.ToStatusV2(st))
|
|
|
|
}
|
|
|
|
|
|
|
|
// signs response with private key via signature.SignServiceMessage.
|
|
|
|
// The signature error affects the result depending on the protocol version:
|
|
|
|
// * if status return is supported, panics since we cannot return the failed status, because it will not be signed;
|
|
|
|
// * otherwise, returns error in order to transport it directly.
|
|
|
|
func signResponse(key *ecdsa.PrivateKey, resp interface{}, statusSupported bool) error {
|
|
|
|
err := signature.SignServiceMessage(key, resp)
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("could not sign response: %w", err)
|
|
|
|
|
|
|
|
if statusSupported {
|
|
|
|
// We can't pass this error as status code since response will be unsigned.
|
|
|
|
// Isn't expected in practice, so panic is ok here.
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|