1/* 2 * 3 * Copyright 2018 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19package grpc 20 21import ( 22 "fmt" 23 "net" 24 "time" 25 26 "golang.org/x/net/context" 27 "google.golang.org/grpc/balancer" 28 "google.golang.org/grpc/credentials" 29 "google.golang.org/grpc/internal" 30 "google.golang.org/grpc/internal/backoff" 31 "google.golang.org/grpc/internal/envconfig" 32 "google.golang.org/grpc/internal/transport" 33 "google.golang.org/grpc/keepalive" 34 "google.golang.org/grpc/resolver" 35 "google.golang.org/grpc/stats" 36) 37 38// dialOptions configure a Dial call. dialOptions are set by the DialOption 39// values passed to Dial. 40type dialOptions struct { 41 unaryInt UnaryClientInterceptor 42 streamInt StreamClientInterceptor 43 cp Compressor 44 dc Decompressor 45 bs backoff.Strategy 46 block bool 47 insecure bool 48 timeout time.Duration 49 scChan <-chan ServiceConfig 50 authority string 51 copts transport.ConnectOptions 52 callOptions []CallOption 53 // This is used by v1 balancer dial option WithBalancer to support v1 54 // balancer, and also by WithBalancerName dial option. 55 balancerBuilder balancer.Builder 56 // This is to support grpclb. 57 resolverBuilder resolver.Builder 58 waitForHandshake bool 59 channelzParentID int64 60 disableServiceConfig bool 61 disableRetry bool 62} 63 64// DialOption configures how we set up the connection. 65type DialOption interface { 66 apply(*dialOptions) 67} 68 69// EmptyDialOption does not alter the dial configuration. It can be embedded in 70// another structure to build custom dial options. 71// 72// This API is EXPERIMENTAL. 73type EmptyDialOption struct{} 74 75func (EmptyDialOption) apply(*dialOptions) {} 76 77// funcDialOption wraps a function that modifies dialOptions into an 78// implementation of the DialOption interface. 79type funcDialOption struct { 80 f func(*dialOptions) 81} 82 83func (fdo *funcDialOption) apply(do *dialOptions) { 84 fdo.f(do) 85} 86 87func newFuncDialOption(f func(*dialOptions)) *funcDialOption { 88 return &funcDialOption{ 89 f: f, 90 } 91} 92 93// WithWaitForHandshake blocks until the initial settings frame is received from 94// the server before assigning RPCs to the connection. Experimental API. 95func WithWaitForHandshake() DialOption { 96 return newFuncDialOption(func(o *dialOptions) { 97 o.waitForHandshake = true 98 }) 99} 100 101// WithWriteBufferSize determines how much data can be batched before doing a 102// write on the wire. The corresponding memory allocation for this buffer will 103// be twice the size to keep syscalls low. The default value for this buffer is 104// 32KB. 105// 106// Zero will disable the write buffer such that each write will be on underlying 107// connection. Note: A Send call may not directly translate to a write. 108func WithWriteBufferSize(s int) DialOption { 109 return newFuncDialOption(func(o *dialOptions) { 110 o.copts.WriteBufferSize = s 111 }) 112} 113 114// WithReadBufferSize lets you set the size of read buffer, this determines how 115// much data can be read at most for each read syscall. 116// 117// The default value for this buffer is 32KB. Zero will disable read buffer for 118// a connection so data framer can access the underlying conn directly. 119func WithReadBufferSize(s int) DialOption { 120 return newFuncDialOption(func(o *dialOptions) { 121 o.copts.ReadBufferSize = s 122 }) 123} 124 125// WithInitialWindowSize returns a DialOption which sets the value for initial 126// window size on a stream. The lower bound for window size is 64K and any value 127// smaller than that will be ignored. 128func WithInitialWindowSize(s int32) DialOption { 129 return newFuncDialOption(func(o *dialOptions) { 130 o.copts.InitialWindowSize = s 131 }) 132} 133 134// WithInitialConnWindowSize returns a DialOption which sets the value for 135// initial window size on a connection. The lower bound for window size is 64K 136// and any value smaller than that will be ignored. 137func WithInitialConnWindowSize(s int32) DialOption { 138 return newFuncDialOption(func(o *dialOptions) { 139 o.copts.InitialConnWindowSize = s 140 }) 141} 142 143// WithMaxMsgSize returns a DialOption which sets the maximum message size the 144// client can receive. 145// 146// Deprecated: use WithDefaultCallOptions(MaxCallRecvMsgSize(s)) instead. 147func WithMaxMsgSize(s int) DialOption { 148 return WithDefaultCallOptions(MaxCallRecvMsgSize(s)) 149} 150 151// WithDefaultCallOptions returns a DialOption which sets the default 152// CallOptions for calls over the connection. 153func WithDefaultCallOptions(cos ...CallOption) DialOption { 154 return newFuncDialOption(func(o *dialOptions) { 155 o.callOptions = append(o.callOptions, cos...) 156 }) 157} 158 159// WithCodec returns a DialOption which sets a codec for message marshaling and 160// unmarshaling. 161// 162// Deprecated: use WithDefaultCallOptions(CallCustomCodec(c)) instead. 163func WithCodec(c Codec) DialOption { 164 return WithDefaultCallOptions(CallCustomCodec(c)) 165} 166 167// WithCompressor returns a DialOption which sets a Compressor to use for 168// message compression. It has lower priority than the compressor set by the 169// UseCompressor CallOption. 170// 171// Deprecated: use UseCompressor instead. 172func WithCompressor(cp Compressor) DialOption { 173 return newFuncDialOption(func(o *dialOptions) { 174 o.cp = cp 175 }) 176} 177 178// WithDecompressor returns a DialOption which sets a Decompressor to use for 179// incoming message decompression. If incoming response messages are encoded 180// using the decompressor's Type(), it will be used. Otherwise, the message 181// encoding will be used to look up the compressor registered via 182// encoding.RegisterCompressor, which will then be used to decompress the 183// message. If no compressor is registered for the encoding, an Unimplemented 184// status error will be returned. 185// 186// Deprecated: use encoding.RegisterCompressor instead. 187func WithDecompressor(dc Decompressor) DialOption { 188 return newFuncDialOption(func(o *dialOptions) { 189 o.dc = dc 190 }) 191} 192 193// WithBalancer returns a DialOption which sets a load balancer with the v1 API. 194// Name resolver will be ignored if this DialOption is specified. 195// 196// Deprecated: use the new balancer APIs in balancer package and 197// WithBalancerName. 198func WithBalancer(b Balancer) DialOption { 199 return newFuncDialOption(func(o *dialOptions) { 200 o.balancerBuilder = &balancerWrapperBuilder{ 201 b: b, 202 } 203 }) 204} 205 206// WithBalancerName sets the balancer that the ClientConn will be initialized 207// with. Balancer registered with balancerName will be used. This function 208// panics if no balancer was registered by balancerName. 209// 210// The balancer cannot be overridden by balancer option specified by service 211// config. 212// 213// This is an EXPERIMENTAL API. 214func WithBalancerName(balancerName string) DialOption { 215 builder := balancer.Get(balancerName) 216 if builder == nil { 217 panic(fmt.Sprintf("grpc.WithBalancerName: no balancer is registered for name %v", balancerName)) 218 } 219 return newFuncDialOption(func(o *dialOptions) { 220 o.balancerBuilder = builder 221 }) 222} 223 224// withResolverBuilder is only for grpclb. 225func withResolverBuilder(b resolver.Builder) DialOption { 226 return newFuncDialOption(func(o *dialOptions) { 227 o.resolverBuilder = b 228 }) 229} 230 231// WithServiceConfig returns a DialOption which has a channel to read the 232// service configuration. 233// 234// Deprecated: service config should be received through name resolver, as 235// specified here. 236// https://github.com/grpc/grpc/blob/master/doc/service_config.md 237func WithServiceConfig(c <-chan ServiceConfig) DialOption { 238 return newFuncDialOption(func(o *dialOptions) { 239 o.scChan = c 240 }) 241} 242 243// WithBackoffMaxDelay configures the dialer to use the provided maximum delay 244// when backing off after failed connection attempts. 245func WithBackoffMaxDelay(md time.Duration) DialOption { 246 return WithBackoffConfig(BackoffConfig{MaxDelay: md}) 247} 248 249// WithBackoffConfig configures the dialer to use the provided backoff 250// parameters after connection failures. 251// 252// Use WithBackoffMaxDelay until more parameters on BackoffConfig are opened up 253// for use. 254func WithBackoffConfig(b BackoffConfig) DialOption { 255 return withBackoff(backoff.Exponential{ 256 MaxDelay: b.MaxDelay, 257 }) 258} 259 260// withBackoff sets the backoff strategy used for connectRetryNum after a failed 261// connection attempt. 262// 263// This can be exported if arbitrary backoff strategies are allowed by gRPC. 264func withBackoff(bs backoff.Strategy) DialOption { 265 return newFuncDialOption(func(o *dialOptions) { 266 o.bs = bs 267 }) 268} 269 270// WithBlock returns a DialOption which makes caller of Dial blocks until the 271// underlying connection is up. Without this, Dial returns immediately and 272// connecting the server happens in background. 273func WithBlock() DialOption { 274 return newFuncDialOption(func(o *dialOptions) { 275 o.block = true 276 }) 277} 278 279// WithInsecure returns a DialOption which disables transport security for this 280// ClientConn. Note that transport security is required unless WithInsecure is 281// set. 282func WithInsecure() DialOption { 283 return newFuncDialOption(func(o *dialOptions) { 284 o.insecure = true 285 }) 286} 287 288// WithTransportCredentials returns a DialOption which configures a connection 289// level security credentials (e.g., TLS/SSL). This should not be used together 290// with WithCredentialsBundle. 291func WithTransportCredentials(creds credentials.TransportCredentials) DialOption { 292 return newFuncDialOption(func(o *dialOptions) { 293 o.copts.TransportCredentials = creds 294 }) 295} 296 297// WithPerRPCCredentials returns a DialOption which sets credentials and places 298// auth state on each outbound RPC. 299func WithPerRPCCredentials(creds credentials.PerRPCCredentials) DialOption { 300 return newFuncDialOption(func(o *dialOptions) { 301 o.copts.PerRPCCredentials = append(o.copts.PerRPCCredentials, creds) 302 }) 303} 304 305// WithCredentialsBundle returns a DialOption to set a credentials bundle for 306// the ClientConn.WithCreds. This should not be used together with 307// WithTransportCredentials. 308// 309// This API is experimental. 310func WithCredentialsBundle(b credentials.Bundle) DialOption { 311 return newFuncDialOption(func(o *dialOptions) { 312 o.copts.CredsBundle = b 313 }) 314} 315 316// WithTimeout returns a DialOption that configures a timeout for dialing a 317// ClientConn initially. This is valid if and only if WithBlock() is present. 318// 319// Deprecated: use DialContext and context.WithTimeout instead. 320func WithTimeout(d time.Duration) DialOption { 321 return newFuncDialOption(func(o *dialOptions) { 322 o.timeout = d 323 }) 324} 325 326func withContextDialer(f func(context.Context, string) (net.Conn, error)) DialOption { 327 return newFuncDialOption(func(o *dialOptions) { 328 o.copts.Dialer = f 329 }) 330} 331 332func init() { 333 internal.WithContextDialer = withContextDialer 334 internal.WithResolverBuilder = withResolverBuilder 335} 336 337// WithDialer returns a DialOption that specifies a function to use for dialing 338// network addresses. If FailOnNonTempDialError() is set to true, and an error 339// is returned by f, gRPC checks the error's Temporary() method to decide if it 340// should try to reconnect to the network address. 341func WithDialer(f func(string, time.Duration) (net.Conn, error)) DialOption { 342 return withContextDialer( 343 func(ctx context.Context, addr string) (net.Conn, error) { 344 if deadline, ok := ctx.Deadline(); ok { 345 return f(addr, deadline.Sub(time.Now())) 346 } 347 return f(addr, 0) 348 }) 349} 350 351// WithStatsHandler returns a DialOption that specifies the stats handler for 352// all the RPCs and underlying network connections in this ClientConn. 353func WithStatsHandler(h stats.Handler) DialOption { 354 return newFuncDialOption(func(o *dialOptions) { 355 o.copts.StatsHandler = h 356 }) 357} 358 359// FailOnNonTempDialError returns a DialOption that specifies if gRPC fails on 360// non-temporary dial errors. If f is true, and dialer returns a non-temporary 361// error, gRPC will fail the connection to the network address and won't try to 362// reconnect. The default value of FailOnNonTempDialError is false. 363// 364// FailOnNonTempDialError only affects the initial dial, and does not do 365// anything useful unless you are also using WithBlock(). 366// 367// This is an EXPERIMENTAL API. 368func FailOnNonTempDialError(f bool) DialOption { 369 return newFuncDialOption(func(o *dialOptions) { 370 o.copts.FailOnNonTempDialError = f 371 }) 372} 373 374// WithUserAgent returns a DialOption that specifies a user agent string for all 375// the RPCs. 376func WithUserAgent(s string) DialOption { 377 return newFuncDialOption(func(o *dialOptions) { 378 o.copts.UserAgent = s 379 }) 380} 381 382// WithKeepaliveParams returns a DialOption that specifies keepalive parameters 383// for the client transport. 384func WithKeepaliveParams(kp keepalive.ClientParameters) DialOption { 385 return newFuncDialOption(func(o *dialOptions) { 386 o.copts.KeepaliveParams = kp 387 }) 388} 389 390// WithUnaryInterceptor returns a DialOption that specifies the interceptor for 391// unary RPCs. 392func WithUnaryInterceptor(f UnaryClientInterceptor) DialOption { 393 return newFuncDialOption(func(o *dialOptions) { 394 o.unaryInt = f 395 }) 396} 397 398// WithStreamInterceptor returns a DialOption that specifies the interceptor for 399// streaming RPCs. 400func WithStreamInterceptor(f StreamClientInterceptor) DialOption { 401 return newFuncDialOption(func(o *dialOptions) { 402 o.streamInt = f 403 }) 404} 405 406// WithAuthority returns a DialOption that specifies the value to be used as the 407// :authority pseudo-header. This value only works with WithInsecure and has no 408// effect if TransportCredentials are present. 409func WithAuthority(a string) DialOption { 410 return newFuncDialOption(func(o *dialOptions) { 411 o.authority = a 412 }) 413} 414 415// WithChannelzParentID returns a DialOption that specifies the channelz ID of 416// current ClientConn's parent. This function is used in nested channel creation 417// (e.g. grpclb dial). 418func WithChannelzParentID(id int64) DialOption { 419 return newFuncDialOption(func(o *dialOptions) { 420 o.channelzParentID = id 421 }) 422} 423 424// WithDisableServiceConfig returns a DialOption that causes grpc to ignore any 425// service config provided by the resolver and provides a hint to the resolver 426// to not fetch service configs. 427func WithDisableServiceConfig() DialOption { 428 return newFuncDialOption(func(o *dialOptions) { 429 o.disableServiceConfig = true 430 }) 431} 432 433// WithDisableRetry returns a DialOption that disables retries, even if the 434// service config enables them. This does not impact transparent retries, which 435// will happen automatically if no data is written to the wire or if the RPC is 436// unprocessed by the remote server. 437// 438// Retry support is currently disabled by default, but will be enabled by 439// default in the future. Until then, it may be enabled by setting the 440// environment variable "GRPC_GO_RETRY" to "on". 441// 442// This API is EXPERIMENTAL. 443func WithDisableRetry() DialOption { 444 return newFuncDialOption(func(o *dialOptions) { 445 o.disableRetry = true 446 }) 447} 448 449// WithMaxHeaderListSize returns a DialOption that specifies the maximum 450// (uncompressed) size of header list that the client is prepared to accept. 451func WithMaxHeaderListSize(s uint32) DialOption { 452 return newFuncDialOption(func(o *dialOptions) { 453 o.copts.MaxHeaderListSize = &s 454 }) 455} 456 457func defaultDialOptions() dialOptions { 458 return dialOptions{ 459 disableRetry: !envconfig.Retry, 460 copts: transport.ConnectOptions{ 461 WriteBufferSize: defaultWriteBufSize, 462 ReadBufferSize: defaultReadBufSize, 463 }, 464 } 465} 466