go.uber.org/yarpc@v1.72.1/transport/grpc/options.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 grpc 22 23 import ( 24 "context" 25 "crypto/tls" 26 "math" 27 "net" 28 29 opentracing "github.com/opentracing/opentracing-go" 30 "go.uber.org/net/metrics" 31 "go.uber.org/yarpc/api/backoff" 32 "go.uber.org/yarpc/api/transport" 33 yarpctls "go.uber.org/yarpc/api/transport/tls" 34 intbackoff "go.uber.org/yarpc/internal/backoff" 35 "go.uber.org/yarpc/transport/internal/tls/dialer" 36 "go.uber.org/zap" 37 "google.golang.org/grpc" 38 "google.golang.org/grpc/credentials" 39 "google.golang.org/grpc/keepalive" 40 ) 41 42 const ( 43 // defensive programming 44 // these are copied from grpc-go but we set them explicitly here 45 // in case these change in grpc-go so that yarpc stays consistent 46 defaultServerMaxSendMsgSize = math.MaxInt32 47 defaultClientMaxSendMsgSize = math.MaxInt32 48 // Overriding default server and client maximum request and response 49 // receive sizes to 64MB. 50 defaultServerMaxRecvMsgSize = 1024 * 1024 * 64 51 defaultClientMaxRecvMsgSize = 1024 * 1024 * 64 52 ) 53 54 // Option is an interface shared by TransportOption, InboundOption, and OutboundOption 55 // allowing either to be recognized by TransportSpec(). 56 type Option interface { 57 grpcOption() 58 } 59 60 var _ Option = (TransportOption)(nil) 61 var _ Option = (InboundOption)(nil) 62 var _ Option = (OutboundOption)(nil) 63 var _ Option = (DialOption)(nil) 64 65 // TransportOption is an option for a transport. 66 type TransportOption func(*transportOptions) 67 68 func (TransportOption) grpcOption() {} 69 70 // ServiceName specifices the name of the service used in transport logging 71 // and metrics. 72 func ServiceName(name string) TransportOption { 73 return func(transportOptions *transportOptions) { 74 transportOptions.serviceName = name 75 } 76 } 77 78 // BackoffStrategy specifies the backoff strategy for delays between 79 // connection attempts for each peer. 80 // 81 // The default is exponential backoff starting with 10ms fully jittered, 82 // doubling each attempt, with a maximum interval of 30s. 83 func BackoffStrategy(backoffStrategy backoff.Strategy) TransportOption { 84 return func(transportOptions *transportOptions) { 85 transportOptions.backoffStrategy = backoffStrategy 86 } 87 } 88 89 // Tracer specifies the tracer to use. 90 // 91 // By default, opentracing.GlobalTracer() is used. 92 func Tracer(tracer opentracing.Tracer) TransportOption { 93 return func(transportOptions *transportOptions) { 94 transportOptions.tracer = tracer 95 } 96 } 97 98 // Logger sets a logger to use for internal logging. 99 // 100 // The default is to not write any logs. 101 func Logger(logger *zap.Logger) TransportOption { 102 return func(transportOptions *transportOptions) { 103 transportOptions.logger = logger 104 } 105 } 106 107 // Meter sets a meter to use for transport metrics. 108 // 109 // The default is to not emit any metrics. 110 func Meter(meter *metrics.Scope) TransportOption { 111 return func(transportOptions *transportOptions) { 112 transportOptions.meter = meter 113 } 114 } 115 116 // ServerMaxRecvMsgSize is the maximum message size the server can receive. 117 // 118 // The default is 4MB. 119 func ServerMaxRecvMsgSize(serverMaxRecvMsgSize int) TransportOption { 120 return func(transportOptions *transportOptions) { 121 transportOptions.serverMaxRecvMsgSize = serverMaxRecvMsgSize 122 } 123 } 124 125 // ServerMaxSendMsgSize is the maximum message size the server can send. 126 // 127 // The default is unlimited. 128 func ServerMaxSendMsgSize(serverMaxSendMsgSize int) TransportOption { 129 return func(transportOptions *transportOptions) { 130 transportOptions.serverMaxSendMsgSize = serverMaxSendMsgSize 131 } 132 } 133 134 // ServerMaxHeaderListSize returns a transport option for configuring maximum 135 // header list size the server must accept. 136 // 137 // The default is 16MB (gRPC default). 138 func ServerMaxHeaderListSize(serverMaxHeaderListSize uint32) TransportOption { 139 return func(transportOptions *transportOptions) { 140 transportOptions.serverMaxHeaderListSize = &serverMaxHeaderListSize 141 } 142 } 143 144 // ClientMaxRecvMsgSize is the maximum message size the client can receive. 145 // 146 // The default is 4MB. 147 func ClientMaxRecvMsgSize(clientMaxRecvMsgSize int) TransportOption { 148 return func(transportOptions *transportOptions) { 149 transportOptions.clientMaxRecvMsgSize = clientMaxRecvMsgSize 150 } 151 } 152 153 // ClientMaxSendMsgSize is the maximum message size the client can send. 154 // 155 // The default is unlimited. 156 func ClientMaxSendMsgSize(clientMaxSendMsgSize int) TransportOption { 157 return func(transportOptions *transportOptions) { 158 transportOptions.clientMaxSendMsgSize = clientMaxSendMsgSize 159 } 160 } 161 162 // ClientMaxHeaderListSize returns a transport option for configuring maximum 163 // header list size the client must accept. 164 // 165 // The default is 16MB (gRPC default). 166 func ClientMaxHeaderListSize(clientMaxHeaderListSize uint32) TransportOption { 167 return func(transportOptions *transportOptions) { 168 transportOptions.clientMaxHeaderListSize = &clientMaxHeaderListSize 169 } 170 } 171 172 // InboundOption is an option for an inbound. 173 type InboundOption func(*inboundOptions) 174 175 func (InboundOption) grpcOption() {} 176 177 // InboundCredentials returns an InboundOption that sets credentials for incoming 178 // connections. 179 func InboundCredentials(creds credentials.TransportCredentials) InboundOption { 180 return func(inboundOptions *inboundOptions) { 181 inboundOptions.creds = creds 182 } 183 } 184 185 // InboundTLSConfiguration returns an InboundOption that provides the TLS confiugration 186 // used for setting up TLS inbound. 187 func InboundTLSConfiguration(config *tls.Config) InboundOption { 188 return func(inboundOptions *inboundOptions) { 189 inboundOptions.tlsConfig = config 190 } 191 } 192 193 // InboundTLSMode returns an InboundOption that sets inbound TLS mode. 194 // It must be noted that TLS configuration must be passed separately using inbound 195 // option InboundTLSConfiguration. 196 func InboundTLSMode(mode yarpctls.Mode) InboundOption { 197 return func(inboundOptions *inboundOptions) { 198 inboundOptions.tlsMode = mode 199 } 200 } 201 202 // OutboundOption is an option for an outbound. 203 type OutboundOption func(*outboundOptions) 204 205 func (OutboundOption) grpcOption() {} 206 207 // OutboundTLSConfigProvider returns an OutboundOption that provides the 208 // outbound TLS config provider. 209 func OutboundTLSConfigProvider(provider yarpctls.OutboundTLSConfigProvider) OutboundOption { 210 return func(outboundOptions *outboundOptions) { 211 outboundOptions.tlsConfigProvider = provider 212 } 213 } 214 215 // OutboundCompressor returns an OutboundOption that applies compressorion 216 // for requests in the outbound. 217 func OutboundCompressor(compressor transport.Compressor) OutboundOption { 218 return func(outboundOptions *outboundOptions) { 219 if compressor != nil { 220 outboundOptions.compressor = compressor.Name() 221 } 222 } 223 } 224 225 // DialOption is an option that influences grpc.Dial. 226 type DialOption func(*dialOptions) 227 228 func (DialOption) grpcOption() {} 229 230 // DialerCredentials returns a DialOption which configures a 231 // connection level security credentials (e.g., TLS/SSL). 232 func DialerCredentials(creds credentials.TransportCredentials) DialOption { 233 return func(dialOptions *dialOptions) { 234 dialOptions.creds = creds 235 } 236 } 237 238 // DialerTLSConfig return a DialOption which configures the TLS config for the 239 // outbound. 240 func DialerTLSConfig(config *tls.Config) DialOption { 241 return func(dialOptions *dialOptions) { 242 dialOptions.tlsConfig = config 243 } 244 } 245 246 // DialerDestinationServiceName returns a DialOption which configures the 247 // destination service name of the dialer. This is used in TLS dialer metrics. 248 func DialerDestinationServiceName(service string) DialOption { 249 return func(dialOptions *dialOptions) { 250 dialOptions.destServiceName = service 251 } 252 } 253 254 // ContextDialer sets the dialer for creating outbound connections. 255 // 256 // See https://godoc.org/google.golang.org/grpc#WithContextDialer for more 257 // details. 258 func ContextDialer(f func(context.Context, string) (net.Conn, error)) DialOption { 259 return func(dialOptions *dialOptions) { 260 dialOptions.contextDialer = f 261 } 262 } 263 264 // Compressor sets the compressor to be used by default for gRPC connections 265 func Compressor(compressor transport.Compressor) DialOption { 266 return func(dialOptions *dialOptions) { 267 if compressor != nil { 268 // We assume that the grpc-go compressor was also globally 269 // registered and just use the name. 270 // Future implementations may elect to actually use the compressor. 271 dialOptions.defaultCompressor = compressor.Name() 272 } 273 } 274 } 275 276 // KeepaliveParams sets the gRPC keepalive parameters of the outbound 277 // connection. 278 // See https://pkg.go.dev/google.golang.org/grpc#WithKeepaliveParams for more 279 // details. 280 func KeepaliveParams(params keepalive.ClientParameters) DialOption { 281 return func(dialOptions *dialOptions) { 282 dialOptions.keepaliveParams = ¶ms 283 } 284 } 285 286 type transportOptions struct { 287 backoffStrategy backoff.Strategy 288 tracer opentracing.Tracer 289 logger *zap.Logger 290 meter *metrics.Scope 291 serviceName string 292 serverMaxRecvMsgSize int 293 serverMaxSendMsgSize int 294 clientMaxRecvMsgSize int 295 clientMaxSendMsgSize int 296 serverMaxHeaderListSize *uint32 297 clientMaxHeaderListSize *uint32 298 } 299 300 func newTransportOptions(options []TransportOption) *transportOptions { 301 transportOptions := &transportOptions{ 302 backoffStrategy: intbackoff.DefaultExponential, 303 serverMaxRecvMsgSize: defaultServerMaxRecvMsgSize, 304 serverMaxSendMsgSize: defaultServerMaxSendMsgSize, 305 clientMaxRecvMsgSize: defaultClientMaxRecvMsgSize, 306 clientMaxSendMsgSize: defaultClientMaxSendMsgSize, 307 } 308 for _, option := range options { 309 option(transportOptions) 310 } 311 if transportOptions.logger == nil { 312 transportOptions.logger = zap.NewNop() 313 } 314 if transportOptions.tracer == nil { 315 transportOptions.tracer = opentracing.GlobalTracer() 316 } 317 return transportOptions 318 } 319 320 type inboundOptions struct { 321 creds credentials.TransportCredentials 322 323 tlsConfig *tls.Config 324 tlsMode yarpctls.Mode 325 } 326 327 func newInboundOptions(options []InboundOption) *inboundOptions { 328 inboundOptions := &inboundOptions{} 329 for _, option := range options { 330 option(inboundOptions) 331 } 332 return inboundOptions 333 } 334 335 type outboundOptions struct { 336 compressor string 337 tlsConfigProvider yarpctls.OutboundTLSConfigProvider 338 } 339 340 func newOutboundOptions(options []OutboundOption) *outboundOptions { 341 outboundOptions := &outboundOptions{} 342 for _, option := range options { 343 option(outboundOptions) 344 } 345 return outboundOptions 346 } 347 348 type dialOptions struct { 349 creds credentials.TransportCredentials 350 contextDialer func(context.Context, string) (net.Conn, error) 351 defaultCompressor string 352 keepaliveParams *keepalive.ClientParameters 353 tlsConfig *tls.Config 354 destServiceName string 355 } 356 357 func (d *dialOptions) grpcOptions(t *Transport) []grpc.DialOption { 358 credsOption := grpc.WithInsecure() 359 if d.creds != nil { 360 credsOption = grpc.WithTransportCredentials(d.creds) 361 } 362 363 opts := []grpc.DialOption{ 364 credsOption, 365 } 366 367 if d.defaultCompressor != "" { 368 opts = append(opts, grpc.WithDefaultCallOptions(grpc.UseCompressor(d.defaultCompressor))) 369 } 370 371 if d.keepaliveParams != nil { 372 opts = append(opts, grpc.WithKeepaliveParams(*d.keepaliveParams)) 373 } 374 375 contextDialer := d.contextDialer 376 if d.tlsConfig != nil { 377 params := dialer.Params{ 378 Config: d.tlsConfig, 379 Meter: t.options.meter, 380 Logger: t.options.logger, 381 ServiceName: t.options.serviceName, 382 TransportName: TransportName, 383 Dest: d.destServiceName, 384 } 385 386 if d.contextDialer != nil { 387 params.Dialer = func(ctx context.Context, network, addr string) (net.Conn, error) { 388 return d.contextDialer(ctx, addr) 389 } 390 } 391 tlsDialer := dialer.NewTLSDialer(params) 392 contextDialer = func(ctx context.Context, addr string) (net.Conn, error) { 393 return tlsDialer.DialContext(ctx, "tcp", addr) 394 } 395 } 396 opts = append(opts, grpc.WithContextDialer(contextDialer)) 397 398 return opts 399 } 400 401 func newDialOptions(options []DialOption) *dialOptions { 402 var dopts dialOptions 403 for _, option := range options { 404 option(&dopts) 405 } 406 return &dopts 407 }