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