go.uber.org/yarpc@v1.72.1/encoding/protobuf/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 protobuf 22 23 import ( 24 "context" 25 "reflect" 26 "strings" 27 28 "github.com/gogo/protobuf/jsonpb" 29 "github.com/gogo/protobuf/proto" 30 "go.uber.org/yarpc" 31 "go.uber.org/yarpc/api/transport" 32 "go.uber.org/yarpc/pkg/procedure" 33 "go.uber.org/yarpc/yarpcerrors" 34 ) 35 36 const ( 37 // Encoding is the name of this encoding. 38 Encoding transport.Encoding = "proto" 39 40 // JSONEncoding is the name of the JSON encoding. 41 // 42 // Protobuf handlers are able to handle both Encoding and JSONEncoding encodings. 43 JSONEncoding transport.Encoding = "json" 44 ) 45 46 // UseJSON says to use the json encoding for client/server communication. 47 var UseJSON ClientOption = useJSON{} 48 49 // ***all below functions should only be called by generated code*** 50 51 // BuildProceduresParams contains the parameters for BuildProcedures. 52 type BuildProceduresParams struct { 53 ServiceName string 54 UnaryHandlerParams []BuildProceduresUnaryHandlerParams 55 OnewayHandlerParams []BuildProceduresOnewayHandlerParams 56 StreamHandlerParams []BuildProceduresStreamHandlerParams 57 } 58 59 // BuildProceduresUnaryHandlerParams contains the parameters for a UnaryHandler for BuildProcedures. 60 type BuildProceduresUnaryHandlerParams struct { 61 MethodName string 62 Handler transport.UnaryHandler 63 } 64 65 // BuildProceduresOnewayHandlerParams contains the parameters for a OnewayHandler for BuildProcedures. 66 type BuildProceduresOnewayHandlerParams struct { 67 MethodName string 68 Handler transport.OnewayHandler 69 } 70 71 // BuildProceduresStreamHandlerParams contains the parameters for a StreamHandler for BuildProcedures. 72 type BuildProceduresStreamHandlerParams struct { 73 MethodName string 74 Handler transport.StreamHandler 75 } 76 77 // BuildProcedures builds the transport.Procedures. 78 func BuildProcedures(params BuildProceduresParams) []transport.Procedure { 79 procedures := make([]transport.Procedure, 0, 2*(len(params.UnaryHandlerParams)+len(params.OnewayHandlerParams)+len(params.StreamHandlerParams))) 80 for _, unaryHandlerParams := range params.UnaryHandlerParams { 81 procedures = append( 82 procedures, 83 transport.Procedure{ 84 Name: procedure.ToName(params.ServiceName, unaryHandlerParams.MethodName), 85 HandlerSpec: transport.NewUnaryHandlerSpec(unaryHandlerParams.Handler), 86 Encoding: Encoding, 87 }, 88 transport.Procedure{ 89 Name: procedure.ToName(params.ServiceName, unaryHandlerParams.MethodName), 90 HandlerSpec: transport.NewUnaryHandlerSpec(unaryHandlerParams.Handler), 91 Encoding: JSONEncoding, 92 }, 93 ) 94 } 95 for _, onewayHandlerParams := range params.OnewayHandlerParams { 96 procedures = append( 97 procedures, 98 transport.Procedure{ 99 Name: procedure.ToName(params.ServiceName, onewayHandlerParams.MethodName), 100 HandlerSpec: transport.NewOnewayHandlerSpec(onewayHandlerParams.Handler), 101 Encoding: Encoding, 102 }, 103 transport.Procedure{ 104 Name: procedure.ToName(params.ServiceName, onewayHandlerParams.MethodName), 105 HandlerSpec: transport.NewOnewayHandlerSpec(onewayHandlerParams.Handler), 106 Encoding: JSONEncoding, 107 }, 108 ) 109 } 110 for _, streamHandlerParams := range params.StreamHandlerParams { 111 procedures = append( 112 procedures, 113 transport.Procedure{ 114 Name: procedure.ToName(params.ServiceName, streamHandlerParams.MethodName), 115 HandlerSpec: transport.NewStreamHandlerSpec(streamHandlerParams.Handler), 116 Encoding: Encoding, 117 }, 118 transport.Procedure{ 119 Name: procedure.ToName(params.ServiceName, streamHandlerParams.MethodName), 120 HandlerSpec: transport.NewStreamHandlerSpec(streamHandlerParams.Handler), 121 Encoding: JSONEncoding, 122 }, 123 ) 124 } 125 return procedures 126 } 127 128 // Client is a protobuf client. 129 type Client interface { 130 Call( 131 ctx context.Context, 132 requestMethodName string, 133 request proto.Message, 134 newResponse func() proto.Message, 135 options ...yarpc.CallOption, 136 ) (proto.Message, error) 137 CallOneway( 138 ctx context.Context, 139 requestMethodName string, 140 request proto.Message, 141 options ...yarpc.CallOption, 142 ) (transport.Ack, error) 143 } 144 145 // StreamClient is a protobuf client with streaming. 146 type StreamClient interface { 147 Client 148 149 CallStream( 150 ctx context.Context, 151 requestMethodName string, 152 opts ...yarpc.CallOption, 153 ) (*ClientStream, error) 154 } 155 156 // ClientOption is an option for a new Client. 157 type ClientOption interface { 158 apply(*client) 159 } 160 161 // ClientParams contains the parameters for creating a new Client. 162 type ClientParams struct { 163 ServiceName string 164 ClientConfig transport.ClientConfig 165 AnyResolver jsonpb.AnyResolver 166 Options []ClientOption 167 } 168 169 // NewClient creates a new client. 170 func NewClient(params ClientParams) Client { 171 return newClient(params.ServiceName, params.ClientConfig, params.AnyResolver, params.Options...) 172 } 173 174 // NewStreamClient creates a new stream client. 175 func NewStreamClient(params ClientParams) StreamClient { 176 return newClient(params.ServiceName, params.ClientConfig, params.AnyResolver, params.Options...) 177 } 178 179 // UnaryHandlerParams contains the parameters for creating a new UnaryHandler. 180 type UnaryHandlerParams struct { 181 Handle func(context.Context, proto.Message) (proto.Message, error) 182 NewRequest func() proto.Message 183 AnyResolver jsonpb.AnyResolver 184 } 185 186 // NewUnaryHandler returns a new UnaryHandler. 187 func NewUnaryHandler(params UnaryHandlerParams) transport.UnaryHandler { 188 return newUnaryHandler( 189 params.Handle, 190 params.NewRequest, 191 newCodec(params.AnyResolver), 192 ) 193 } 194 195 // OnewayHandlerParams contains the parameters for creating a new OnewayHandler. 196 type OnewayHandlerParams struct { 197 Handle func(context.Context, proto.Message) error 198 NewRequest func() proto.Message 199 AnyResolver jsonpb.AnyResolver 200 } 201 202 // NewOnewayHandler returns a new OnewayHandler. 203 func NewOnewayHandler(params OnewayHandlerParams) transport.OnewayHandler { 204 return newOnewayHandler( 205 params.Handle, 206 params.NewRequest, 207 newCodec(params.AnyResolver), 208 ) 209 } 210 211 // StreamHandlerParams contains the parameters for creating a new StreamHandler. 212 type StreamHandlerParams struct { 213 Handle func(*ServerStream) error 214 } 215 216 // NewStreamHandler returns a new StreamHandler. 217 func NewStreamHandler(params StreamHandlerParams) transport.StreamHandler { 218 return newStreamHandler(params.Handle) 219 } 220 221 // ClientBuilderOptions returns ClientOptions that yarpc.InjectClients should use for a 222 // specific client given information about the field into which the client is being injected. 223 func ClientBuilderOptions(_ transport.ClientConfig, structField reflect.StructField) []ClientOption { 224 var opts []ClientOption 225 for _, opt := range uniqueLowercaseStrings(strings.Split(structField.Tag.Get("proto"), ",")) { 226 switch opt { 227 case "json": 228 opts = append(opts, UseJSON) 229 } 230 } 231 return opts 232 } 233 234 // CastError returns an error saying that generated code could not properly cast a proto.Message to it's expected type. 235 func CastError(expectedType proto.Message, actualType proto.Message) error { 236 return yarpcerrors.Newf(yarpcerrors.CodeInternal, "expected proto.Message to have type %T but had type %T", expectedType, actualType) 237 } 238 239 type useJSON struct{} 240 241 func (useJSON) apply(client *client) { 242 client.encoding = JSONEncoding 243 } 244 245 func uniqueLowercaseStrings(s []string) []string { 246 m := make(map[string]bool, len(s)) 247 for _, e := range s { 248 if e != "" { 249 m[strings.ToLower(e)] = true 250 } 251 } 252 c := make([]string, 0, len(m)) 253 for key := range m { 254 c = append(c, key) 255 } 256 return c 257 } 258 259 // ClientStream is a protobuf-specific client stream. 260 type ClientStream struct { 261 stream *transport.ClientStream 262 codec *codec 263 } 264 265 // Context returns the context of the stream. 266 func (c *ClientStream) Context() context.Context { 267 return c.stream.Context() 268 } 269 270 // Receive will receive a protobuf message from the client stream. 271 func (c *ClientStream) Receive(newMessage func() proto.Message, options ...yarpc.StreamOption) (proto.Message, error) { 272 return readFromStream(context.Background(), c.stream, newMessage, c.codec) 273 } 274 275 // Send will send a protobuf message to the client stream. 276 func (c *ClientStream) Send(message proto.Message, options ...yarpc.StreamOption) error { 277 return writeToStream(context.Background(), c.stream, message, c.codec) 278 } 279 280 // Close will close the protobuf stream. 281 func (c *ClientStream) Close(options ...yarpc.StreamOption) error { 282 return c.stream.Close(context.Background()) 283 } 284 285 // ServerStream is a protobuf-specific server stream. 286 type ServerStream struct { 287 ctx context.Context 288 stream *transport.ServerStream 289 codec *codec 290 } 291 292 // Context returns the context of the stream. 293 func (s *ServerStream) Context() context.Context { 294 return s.ctx 295 } 296 297 // Receive will receive a protobuf message from the server stream. 298 func (s *ServerStream) Receive(newMessage func() proto.Message, options ...yarpc.StreamOption) (proto.Message, error) { 299 return readFromStream(context.Background(), s.stream, newMessage, s.codec) 300 } 301 302 // Send will send a protobuf message to the server stream. 303 func (s *ServerStream) Send(message proto.Message, options ...yarpc.StreamOption) error { 304 return writeToStream(context.Background(), s.stream, message, s.codec) 305 }