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