30c6ca0714
Implement generic `Client` that can communicate with the remote server via protobuf `Message`'s. The client can uniformly execute any protobuf RPC on the remote server using any of the supported transport protocols. Currently only gRPC protocol is supported. Additionally implement helpful functions to transmit messages by one of the flow types: unary, client- or server-side stream. Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
124 lines
2.5 KiB
Go
124 lines
2.5 KiB
Go
package client
|
|
|
|
import (
|
|
"io"
|
|
"sync"
|
|
|
|
"github.com/nspcc-dev/neofs-api-go/rpc/common"
|
|
"github.com/nspcc-dev/neofs-api-go/rpc/message"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// SendUnary initializes communication session by RPC info, performs unary RPC
|
|
// and closes the session.
|
|
func SendUnary(cli *Client, info common.CallMethodInfo, req, resp message.Message, opts ...CallOption) error {
|
|
rw, err := cli.Init(info, opts...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = rw.WriteMessage(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = rw.ReadMessage(resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return rw.Close()
|
|
}
|
|
|
|
// MessageWriterCloser wraps MessageWriter
|
|
// and io.Closer interfaces.
|
|
type MessageWriterCloser interface {
|
|
MessageWriter
|
|
io.Closer
|
|
}
|
|
|
|
type clientStreamWriterCloser struct {
|
|
MessageReadWriter
|
|
|
|
resp message.Message
|
|
}
|
|
|
|
func (c *clientStreamWriterCloser) Close() error {
|
|
err := c.MessageReadWriter.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return c.ReadMessage(c.resp)
|
|
}
|
|
|
|
// OpenClientStream initializes communication session by RPC info, opens client-side stream
|
|
// and returns its interface.
|
|
//
|
|
// All stream writes must be performed before the closing. Close must be called once.
|
|
func OpenClientStream(cli *Client, info common.CallMethodInfo, resp message.Message, opts ...CallOption) (MessageWriterCloser, error) {
|
|
rw, err := cli.Init(info, opts...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &clientStreamWriterCloser{
|
|
MessageReadWriter: rw,
|
|
resp: resp,
|
|
}, nil
|
|
}
|
|
|
|
// MessageReaderCloser wraps MessageReader
|
|
// and io.Closer interface.
|
|
type MessageReaderCloser interface {
|
|
MessageReader
|
|
io.Closer
|
|
}
|
|
|
|
type serverStreamReaderCloser struct {
|
|
rw MessageReadWriter
|
|
|
|
once sync.Once
|
|
|
|
req message.Message
|
|
}
|
|
|
|
func (s *serverStreamReaderCloser) ReadMessage(msg message.Message) error {
|
|
var err error
|
|
|
|
s.once.Do(func() {
|
|
err = s.rw.WriteMessage(s.req)
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = s.rw.ReadMessage(msg)
|
|
if !errors.Is(err, io.EOF) {
|
|
return err
|
|
}
|
|
|
|
err = s.rw.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return io.EOF
|
|
}
|
|
|
|
// OpenServerStream initializes communication session by RPC info, opens server-side stream
|
|
// and returns its interface.
|
|
//
|
|
// All stream reads must be performed before the closing. Close must be called once.
|
|
func OpenServerStream(cli *Client, info common.CallMethodInfo, req message.Message, opts ...CallOption) (MessageReader, error) {
|
|
rw, err := cli.Init(info, opts...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &serverStreamReaderCloser{
|
|
rw: rw,
|
|
req: req,
|
|
}, nil
|
|
}
|