go.uber.org/yarpc@v1.72.1/encoding/protobuf/v2/protobuf.go (about)

     1  // Copyright (c) 2022 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package v2
    22  
    23  import (
    24  	"context"
    25  	"reflect"
    26  	"strings"
    27  
    28  	"go.uber.org/yarpc"
    29  	"go.uber.org/yarpc/api/transport"
    30  	"go.uber.org/yarpc/pkg/procedure"
    31  	"go.uber.org/yarpc/yarpcerrors"
    32  	"google.golang.org/protobuf/proto"
    33  )
    34  
    35  // UseJSON says to use the json encoding for client/server communication.
    36  var UseJSON ClientOption = useJSON{}
    37  
    38  // ***all below functions should only be called by generated code***
    39  
    40  // BuildProceduresParams contains the parameters for BuildProcedures.
    41  type BuildProceduresParams struct {
    42  	ServiceName         string
    43  	UnaryHandlerParams  []BuildProceduresUnaryHandlerParams
    44  	OnewayHandlerParams []BuildProceduresOnewayHandlerParams
    45  	StreamHandlerParams []BuildProceduresStreamHandlerParams
    46  }
    47  
    48  // BuildProceduresUnaryHandlerParams contains the parameters for a UnaryHandler for BuildProcedures.
    49  type BuildProceduresUnaryHandlerParams struct {
    50  	MethodName string
    51  	Handler    transport.UnaryHandler
    52  }
    53  
    54  // BuildProceduresOnewayHandlerParams contains the parameters for a OnewayHandler for BuildProcedures.
    55  type BuildProceduresOnewayHandlerParams struct {
    56  	MethodName string
    57  	Handler    transport.OnewayHandler
    58  }
    59  
    60  // BuildProceduresStreamHandlerParams contains the parameters for a StreamHandler for BuildProcedures.
    61  type BuildProceduresStreamHandlerParams struct {
    62  	MethodName string
    63  	Handler    transport.StreamHandler
    64  }
    65  
    66  // BuildProcedures builds the transport.Procedures.
    67  func BuildProcedures(params BuildProceduresParams) []transport.Procedure {
    68  	procedures := make([]transport.Procedure, 0, 2*(len(params.UnaryHandlerParams)+len(params.OnewayHandlerParams)+len(params.StreamHandlerParams)))
    69  	for _, unaryHandlerParams := range params.UnaryHandlerParams {
    70  		procedures = append(
    71  			procedures,
    72  			transport.Procedure{
    73  				Name:        procedure.ToName(params.ServiceName, unaryHandlerParams.MethodName),
    74  				HandlerSpec: transport.NewUnaryHandlerSpec(unaryHandlerParams.Handler),
    75  				Encoding:    Encoding,
    76  			},
    77  			transport.Procedure{
    78  				Name:        procedure.ToName(params.ServiceName, unaryHandlerParams.MethodName),
    79  				HandlerSpec: transport.NewUnaryHandlerSpec(unaryHandlerParams.Handler),
    80  				Encoding:    JSONEncoding,
    81  			},
    82  		)
    83  	}
    84  	for _, onewayHandlerParams := range params.OnewayHandlerParams {
    85  		procedures = append(
    86  			procedures,
    87  			transport.Procedure{
    88  				Name:        procedure.ToName(params.ServiceName, onewayHandlerParams.MethodName),
    89  				HandlerSpec: transport.NewOnewayHandlerSpec(onewayHandlerParams.Handler),
    90  				Encoding:    Encoding,
    91  			},
    92  			transport.Procedure{
    93  				Name:        procedure.ToName(params.ServiceName, onewayHandlerParams.MethodName),
    94  				HandlerSpec: transport.NewOnewayHandlerSpec(onewayHandlerParams.Handler),
    95  				Encoding:    JSONEncoding,
    96  			},
    97  		)
    98  	}
    99  	for _, streamHandlerParams := range params.StreamHandlerParams {
   100  		procedures = append(
   101  			procedures,
   102  			transport.Procedure{
   103  				Name:        procedure.ToName(params.ServiceName, streamHandlerParams.MethodName),
   104  				HandlerSpec: transport.NewStreamHandlerSpec(streamHandlerParams.Handler),
   105  				Encoding:    Encoding,
   106  			},
   107  			transport.Procedure{
   108  				Name:        procedure.ToName(params.ServiceName, streamHandlerParams.MethodName),
   109  				HandlerSpec: transport.NewStreamHandlerSpec(streamHandlerParams.Handler),
   110  				Encoding:    JSONEncoding,
   111  			},
   112  		)
   113  	}
   114  	return procedures
   115  }
   116  
   117  // Client is a protobuf client.
   118  type Client interface {
   119  	Call(
   120  		ctx context.Context,
   121  		requestMethodName string,
   122  		request proto.Message,
   123  		newResponse func() proto.Message,
   124  		options ...yarpc.CallOption,
   125  	) (proto.Message, error)
   126  	CallOneway(
   127  		ctx context.Context,
   128  		requestMethodName string,
   129  		request proto.Message,
   130  		options ...yarpc.CallOption,
   131  	) (transport.Ack, error)
   132  }
   133  
   134  // StreamClient is a protobuf client with streaming.
   135  type StreamClient interface {
   136  	Client
   137  
   138  	CallStream(
   139  		ctx context.Context,
   140  		requestMethodName string,
   141  		opts ...yarpc.CallOption,
   142  	) (*ClientStream, error)
   143  }
   144  
   145  // ClientOption is an option for a new Client.
   146  type ClientOption interface {
   147  	apply(*client)
   148  }
   149  
   150  // ClientParams contains the parameters for creating a new Client.
   151  type ClientParams struct {
   152  	ServiceName  string
   153  	ClientConfig transport.ClientConfig
   154  	AnyResolver  AnyResolver
   155  	Options      []ClientOption
   156  }
   157  
   158  // NewClient creates a new client.
   159  func NewClient(params ClientParams) Client {
   160  	return newClient(params.ServiceName, params.ClientConfig, params.AnyResolver, params.Options...)
   161  }
   162  
   163  // NewStreamClient creates a new stream client.
   164  func NewStreamClient(params ClientParams) StreamClient {
   165  	return newClient(params.ServiceName, params.ClientConfig, params.AnyResolver, params.Options...)
   166  }
   167  
   168  // UnaryHandlerParams contains the parameters for creating a new UnaryHandler.
   169  type UnaryHandlerParams struct {
   170  	Handle      func(context.Context, proto.Message) (proto.Message, error)
   171  	NewRequest  func() proto.Message
   172  	AnyResolver AnyResolver
   173  }
   174  
   175  // NewUnaryHandler returns a new UnaryHandler.
   176  func NewUnaryHandler(params UnaryHandlerParams) transport.UnaryHandler {
   177  	return newUnaryHandler(
   178  		params.Handle,
   179  		params.NewRequest,
   180  		newCodec(params.AnyResolver),
   181  	)
   182  }
   183  
   184  // OnewayHandlerParams contains the parameters for creating a new OnewayHandler.
   185  type OnewayHandlerParams struct {
   186  	Handle      func(context.Context, proto.Message) error
   187  	NewRequest  func() proto.Message
   188  	AnyResolver AnyResolver
   189  }
   190  
   191  // NewOnewayHandler returns a new OnewayHandler.
   192  func NewOnewayHandler(params OnewayHandlerParams) transport.OnewayHandler {
   193  	return newOnewayHandler(
   194  		params.Handle,
   195  		params.NewRequest,
   196  		newCodec(params.AnyResolver),
   197  	)
   198  }
   199  
   200  // StreamHandlerParams contains the parameters for creating a new StreamHandler.
   201  type StreamHandlerParams struct {
   202  	Handle func(*ServerStream) error
   203  }
   204  
   205  // NewStreamHandler returns a new StreamHandler.
   206  func NewStreamHandler(params StreamHandlerParams) transport.StreamHandler {
   207  	return newStreamHandler(params.Handle)
   208  }
   209  
   210  // ClientBuilderOptions returns ClientOptions that yarpc.InjectClients should use for a
   211  // specific client given information about the field into which the client is being injected.
   212  func ClientBuilderOptions(_ transport.ClientConfig, structField reflect.StructField) []ClientOption {
   213  	var opts []ClientOption
   214  	for _, opt := range uniqueLowercaseStrings(strings.Split(structField.Tag.Get("proto"), ",")) {
   215  		switch opt {
   216  		case "json":
   217  			opts = append(opts, UseJSON)
   218  		}
   219  	}
   220  	return opts
   221  }
   222  
   223  // CastError returns an error saying that generated code could not properly cast a proto.Message to it's expected type.
   224  func CastError(expectedType proto.Message, actualType proto.Message) error {
   225  	return yarpcerrors.Newf(yarpcerrors.CodeInternal, "expected proto.Message to have type %T but had type %T", expectedType, actualType)
   226  }
   227  
   228  type useJSON struct{}
   229  
   230  func (useJSON) apply(client *client) {
   231  	client.encoding = JSONEncoding
   232  }
   233  
   234  func uniqueLowercaseStrings(s []string) []string {
   235  	m := make(map[string]bool, len(s))
   236  	for _, e := range s {
   237  		if e != "" {
   238  			m[strings.ToLower(e)] = true
   239  		}
   240  	}
   241  	c := make([]string, 0, len(m))
   242  	for key := range m {
   243  		c = append(c, key)
   244  	}
   245  	return c
   246  }
   247  
   248  // ClientStream is a protobuf-specific client stream.
   249  type ClientStream struct {
   250  	stream *transport.ClientStream
   251  	codec  *codec
   252  }
   253  
   254  // Context returns the context of the stream.
   255  func (c *ClientStream) Context() context.Context {
   256  	return c.stream.Context()
   257  }
   258  
   259  // Receive will receive a protobuf message from the client stream.
   260  func (c *ClientStream) Receive(newMessage func() proto.Message, options ...yarpc.StreamOption) (proto.Message, error) {
   261  	return readFromStream(context.Background(), c.stream, newMessage, c.codec)
   262  }
   263  
   264  // Send will send a protobuf message to the client stream.
   265  func (c *ClientStream) Send(message proto.Message, options ...yarpc.StreamOption) error {
   266  	return writeToStream(context.Background(), c.stream, message, c.codec)
   267  }
   268  
   269  // Close will close the protobuf stream.
   270  func (c *ClientStream) Close(options ...yarpc.StreamOption) error {
   271  	return c.stream.Close(context.Background())
   272  }
   273  
   274  // ServerStream is a protobuf-specific server stream.
   275  type ServerStream struct {
   276  	ctx    context.Context
   277  	stream *transport.ServerStream
   278  	codec  *codec
   279  }
   280  
   281  // Context returns the context of the stream.
   282  func (s *ServerStream) Context() context.Context {
   283  	return s.ctx
   284  }
   285  
   286  // Receive will receive a protobuf message from the server stream.
   287  func (s *ServerStream) Receive(newMessage func() proto.Message, options ...yarpc.StreamOption) (proto.Message, error) {
   288  	return readFromStream(context.Background(), s.stream, newMessage, s.codec)
   289  }
   290  
   291  // Send will send a protobuf message to the server stream.
   292  func (s *ServerStream) Send(message proto.Message, options ...yarpc.StreamOption) error {
   293  	return writeToStream(context.Background(), s.stream, message, s.codec)
   294  }