client: define dialOptions as interfaces instead of functions (#2230)
This commit is contained in:
Родитель
4f4261e767
Коммит
445634bdcc
|
@ -140,7 +140,7 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
cc.ctx, cc.cancel = context.WithCancel(context.Background())
|
cc.ctx, cc.cancel = context.WithCancel(context.Background())
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(&cc.dopts)
|
opt.apply(&cc.dopts)
|
||||||
}
|
}
|
||||||
|
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
|
|
148
dialoptions.go
148
dialoptions.go
|
@ -62,14 +62,40 @@ type dialOptions struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialOption configures how we set up the connection.
|
// DialOption configures how we set up the connection.
|
||||||
type DialOption func(*dialOptions)
|
type DialOption interface {
|
||||||
|
apply(*dialOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EmptyDialOption does not alter the dial configuration. It can be embedded in
|
||||||
|
// another structure to build custom dial options.
|
||||||
|
//
|
||||||
|
// This API is EXPERIMENTAL.
|
||||||
|
type EmptyDialOption struct{}
|
||||||
|
|
||||||
|
func (EmptyDialOption) apply(*dialOptions) {}
|
||||||
|
|
||||||
|
// funcDialOption wraps a function that modifies dialOptions into an
|
||||||
|
// implementation of the DialOption interface.
|
||||||
|
type funcDialOption struct {
|
||||||
|
f func(*dialOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fdo *funcDialOption) apply(do *dialOptions) {
|
||||||
|
fdo.f(do)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFuncDialOption(f func(*dialOptions)) *funcDialOption {
|
||||||
|
return &funcDialOption{
|
||||||
|
f: f,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithWaitForHandshake blocks until the initial settings frame is received from
|
// WithWaitForHandshake blocks until the initial settings frame is received from
|
||||||
// the server before assigning RPCs to the connection. Experimental API.
|
// the server before assigning RPCs to the connection. Experimental API.
|
||||||
func WithWaitForHandshake() DialOption {
|
func WithWaitForHandshake() DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.waitForHandshake = true
|
o.waitForHandshake = true
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithWriteBufferSize determines how much data can be batched before doing a
|
// WithWriteBufferSize determines how much data can be batched before doing a
|
||||||
|
@ -80,9 +106,9 @@ func WithWaitForHandshake() DialOption {
|
||||||
// Zero will disable the write buffer such that each write will be on underlying
|
// Zero will disable the write buffer such that each write will be on underlying
|
||||||
// connection. Note: A Send call may not directly translate to a write.
|
// connection. Note: A Send call may not directly translate to a write.
|
||||||
func WithWriteBufferSize(s int) DialOption {
|
func WithWriteBufferSize(s int) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.copts.WriteBufferSize = s
|
o.copts.WriteBufferSize = s
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithReadBufferSize lets you set the size of read buffer, this determines how
|
// WithReadBufferSize lets you set the size of read buffer, this determines how
|
||||||
|
@ -91,27 +117,27 @@ func WithWriteBufferSize(s int) DialOption {
|
||||||
// The default value for this buffer is 32KB. Zero will disable read buffer for
|
// The default value for this buffer is 32KB. Zero will disable read buffer for
|
||||||
// a connection so data framer can access the underlying conn directly.
|
// a connection so data framer can access the underlying conn directly.
|
||||||
func WithReadBufferSize(s int) DialOption {
|
func WithReadBufferSize(s int) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.copts.ReadBufferSize = s
|
o.copts.ReadBufferSize = s
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithInitialWindowSize returns a DialOption which sets the value for initial
|
// WithInitialWindowSize returns a DialOption which sets the value for initial
|
||||||
// window size on a stream. The lower bound for window size is 64K and any value
|
// window size on a stream. The lower bound for window size is 64K and any value
|
||||||
// smaller than that will be ignored.
|
// smaller than that will be ignored.
|
||||||
func WithInitialWindowSize(s int32) DialOption {
|
func WithInitialWindowSize(s int32) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.copts.InitialWindowSize = s
|
o.copts.InitialWindowSize = s
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithInitialConnWindowSize returns a DialOption which sets the value for
|
// WithInitialConnWindowSize returns a DialOption which sets the value for
|
||||||
// initial window size on a connection. The lower bound for window size is 64K
|
// initial window size on a connection. The lower bound for window size is 64K
|
||||||
// and any value smaller than that will be ignored.
|
// and any value smaller than that will be ignored.
|
||||||
func WithInitialConnWindowSize(s int32) DialOption {
|
func WithInitialConnWindowSize(s int32) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.copts.InitialConnWindowSize = s
|
o.copts.InitialConnWindowSize = s
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithMaxMsgSize returns a DialOption which sets the maximum message size the
|
// WithMaxMsgSize returns a DialOption which sets the maximum message size the
|
||||||
|
@ -125,9 +151,9 @@ func WithMaxMsgSize(s int) DialOption {
|
||||||
// WithDefaultCallOptions returns a DialOption which sets the default
|
// WithDefaultCallOptions returns a DialOption which sets the default
|
||||||
// CallOptions for calls over the connection.
|
// CallOptions for calls over the connection.
|
||||||
func WithDefaultCallOptions(cos ...CallOption) DialOption {
|
func WithDefaultCallOptions(cos ...CallOption) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.callOptions = append(o.callOptions, cos...)
|
o.callOptions = append(o.callOptions, cos...)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithCodec returns a DialOption which sets a codec for message marshaling and
|
// WithCodec returns a DialOption which sets a codec for message marshaling and
|
||||||
|
@ -144,9 +170,9 @@ func WithCodec(c Codec) DialOption {
|
||||||
//
|
//
|
||||||
// Deprecated: use UseCompressor instead.
|
// Deprecated: use UseCompressor instead.
|
||||||
func WithCompressor(cp Compressor) DialOption {
|
func WithCompressor(cp Compressor) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.cp = cp
|
o.cp = cp
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithDecompressor returns a DialOption which sets a Decompressor to use for
|
// WithDecompressor returns a DialOption which sets a Decompressor to use for
|
||||||
|
@ -159,9 +185,9 @@ func WithCompressor(cp Compressor) DialOption {
|
||||||
//
|
//
|
||||||
// Deprecated: use encoding.RegisterCompressor instead.
|
// Deprecated: use encoding.RegisterCompressor instead.
|
||||||
func WithDecompressor(dc Decompressor) DialOption {
|
func WithDecompressor(dc Decompressor) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.dc = dc
|
o.dc = dc
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithBalancer returns a DialOption which sets a load balancer with the v1 API.
|
// WithBalancer returns a DialOption which sets a load balancer with the v1 API.
|
||||||
|
@ -170,11 +196,11 @@ func WithDecompressor(dc Decompressor) DialOption {
|
||||||
// Deprecated: use the new balancer APIs in balancer package and
|
// Deprecated: use the new balancer APIs in balancer package and
|
||||||
// WithBalancerName.
|
// WithBalancerName.
|
||||||
func WithBalancer(b Balancer) DialOption {
|
func WithBalancer(b Balancer) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.balancerBuilder = &balancerWrapperBuilder{
|
o.balancerBuilder = &balancerWrapperBuilder{
|
||||||
b: b,
|
b: b,
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithBalancerName sets the balancer that the ClientConn will be initialized
|
// WithBalancerName sets the balancer that the ClientConn will be initialized
|
||||||
|
@ -190,16 +216,16 @@ func WithBalancerName(balancerName string) DialOption {
|
||||||
if builder == nil {
|
if builder == nil {
|
||||||
panic(fmt.Sprintf("grpc.WithBalancerName: no balancer is registered for name %v", balancerName))
|
panic(fmt.Sprintf("grpc.WithBalancerName: no balancer is registered for name %v", balancerName))
|
||||||
}
|
}
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.balancerBuilder = builder
|
o.balancerBuilder = builder
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// withResolverBuilder is only for grpclb.
|
// withResolverBuilder is only for grpclb.
|
||||||
func withResolverBuilder(b resolver.Builder) DialOption {
|
func withResolverBuilder(b resolver.Builder) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.resolverBuilder = b
|
o.resolverBuilder = b
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithServiceConfig returns a DialOption which has a channel to read the
|
// WithServiceConfig returns a DialOption which has a channel to read the
|
||||||
|
@ -209,9 +235,9 @@ func withResolverBuilder(b resolver.Builder) DialOption {
|
||||||
// specified here.
|
// specified here.
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/service_config.md
|
// https://github.com/grpc/grpc/blob/master/doc/service_config.md
|
||||||
func WithServiceConfig(c <-chan ServiceConfig) DialOption {
|
func WithServiceConfig(c <-chan ServiceConfig) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.scChan = c
|
o.scChan = c
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithBackoffMaxDelay configures the dialer to use the provided maximum delay
|
// WithBackoffMaxDelay configures the dialer to use the provided maximum delay
|
||||||
|
@ -236,43 +262,43 @@ func WithBackoffConfig(b BackoffConfig) DialOption {
|
||||||
//
|
//
|
||||||
// This can be exported if arbitrary backoff strategies are allowed by gRPC.
|
// This can be exported if arbitrary backoff strategies are allowed by gRPC.
|
||||||
func withBackoff(bs backoff.Strategy) DialOption {
|
func withBackoff(bs backoff.Strategy) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.bs = bs
|
o.bs = bs
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithBlock returns a DialOption which makes caller of Dial blocks until the
|
// WithBlock returns a DialOption which makes caller of Dial blocks until the
|
||||||
// underlying connection is up. Without this, Dial returns immediately and
|
// underlying connection is up. Without this, Dial returns immediately and
|
||||||
// connecting the server happens in background.
|
// connecting the server happens in background.
|
||||||
func WithBlock() DialOption {
|
func WithBlock() DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.block = true
|
o.block = true
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithInsecure returns a DialOption which disables transport security for this
|
// WithInsecure returns a DialOption which disables transport security for this
|
||||||
// ClientConn. Note that transport security is required unless WithInsecure is
|
// ClientConn. Note that transport security is required unless WithInsecure is
|
||||||
// set.
|
// set.
|
||||||
func WithInsecure() DialOption {
|
func WithInsecure() DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.insecure = true
|
o.insecure = true
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithTransportCredentials returns a DialOption which configures a connection
|
// WithTransportCredentials returns a DialOption which configures a connection
|
||||||
// level security credentials (e.g., TLS/SSL).
|
// level security credentials (e.g., TLS/SSL).
|
||||||
func WithTransportCredentials(creds credentials.TransportCredentials) DialOption {
|
func WithTransportCredentials(creds credentials.TransportCredentials) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.copts.TransportCredentials = creds
|
o.copts.TransportCredentials = creds
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithPerRPCCredentials returns a DialOption which sets credentials and places
|
// WithPerRPCCredentials returns a DialOption which sets credentials and places
|
||||||
// auth state on each outbound RPC.
|
// auth state on each outbound RPC.
|
||||||
func WithPerRPCCredentials(creds credentials.PerRPCCredentials) DialOption {
|
func WithPerRPCCredentials(creds credentials.PerRPCCredentials) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.copts.PerRPCCredentials = append(o.copts.PerRPCCredentials, creds)
|
o.copts.PerRPCCredentials = append(o.copts.PerRPCCredentials, creds)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithTimeout returns a DialOption that configures a timeout for dialing a
|
// WithTimeout returns a DialOption that configures a timeout for dialing a
|
||||||
|
@ -280,15 +306,15 @@ func WithPerRPCCredentials(creds credentials.PerRPCCredentials) DialOption {
|
||||||
//
|
//
|
||||||
// Deprecated: use DialContext and context.WithTimeout instead.
|
// Deprecated: use DialContext and context.WithTimeout instead.
|
||||||
func WithTimeout(d time.Duration) DialOption {
|
func WithTimeout(d time.Duration) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.timeout = d
|
o.timeout = d
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func withContextDialer(f func(context.Context, string) (net.Conn, error)) DialOption {
|
func withContextDialer(f func(context.Context, string) (net.Conn, error)) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.copts.Dialer = f
|
o.copts.Dialer = f
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -313,9 +339,9 @@ func WithDialer(f func(string, time.Duration) (net.Conn, error)) DialOption {
|
||||||
// WithStatsHandler returns a DialOption that specifies the stats handler for
|
// WithStatsHandler returns a DialOption that specifies the stats handler for
|
||||||
// all the RPCs and underlying network connections in this ClientConn.
|
// all the RPCs and underlying network connections in this ClientConn.
|
||||||
func WithStatsHandler(h stats.Handler) DialOption {
|
func WithStatsHandler(h stats.Handler) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.copts.StatsHandler = h
|
o.copts.StatsHandler = h
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// FailOnNonTempDialError returns a DialOption that specifies if gRPC fails on
|
// FailOnNonTempDialError returns a DialOption that specifies if gRPC fails on
|
||||||
|
@ -325,68 +351,68 @@ func WithStatsHandler(h stats.Handler) DialOption {
|
||||||
//
|
//
|
||||||
// This is an EXPERIMENTAL API.
|
// This is an EXPERIMENTAL API.
|
||||||
func FailOnNonTempDialError(f bool) DialOption {
|
func FailOnNonTempDialError(f bool) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.copts.FailOnNonTempDialError = f
|
o.copts.FailOnNonTempDialError = f
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithUserAgent returns a DialOption that specifies a user agent string for all
|
// WithUserAgent returns a DialOption that specifies a user agent string for all
|
||||||
// the RPCs.
|
// the RPCs.
|
||||||
func WithUserAgent(s string) DialOption {
|
func WithUserAgent(s string) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.copts.UserAgent = s
|
o.copts.UserAgent = s
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithKeepaliveParams returns a DialOption that specifies keepalive parameters
|
// WithKeepaliveParams returns a DialOption that specifies keepalive parameters
|
||||||
// for the client transport.
|
// for the client transport.
|
||||||
func WithKeepaliveParams(kp keepalive.ClientParameters) DialOption {
|
func WithKeepaliveParams(kp keepalive.ClientParameters) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.copts.KeepaliveParams = kp
|
o.copts.KeepaliveParams = kp
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithUnaryInterceptor returns a DialOption that specifies the interceptor for
|
// WithUnaryInterceptor returns a DialOption that specifies the interceptor for
|
||||||
// unary RPCs.
|
// unary RPCs.
|
||||||
func WithUnaryInterceptor(f UnaryClientInterceptor) DialOption {
|
func WithUnaryInterceptor(f UnaryClientInterceptor) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.unaryInt = f
|
o.unaryInt = f
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithStreamInterceptor returns a DialOption that specifies the interceptor for
|
// WithStreamInterceptor returns a DialOption that specifies the interceptor for
|
||||||
// streaming RPCs.
|
// streaming RPCs.
|
||||||
func WithStreamInterceptor(f StreamClientInterceptor) DialOption {
|
func WithStreamInterceptor(f StreamClientInterceptor) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.streamInt = f
|
o.streamInt = f
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithAuthority returns a DialOption that specifies the value to be used as the
|
// WithAuthority returns a DialOption that specifies the value to be used as the
|
||||||
// :authority pseudo-header. This value only works with WithInsecure and has no
|
// :authority pseudo-header. This value only works with WithInsecure and has no
|
||||||
// effect if TransportCredentials are present.
|
// effect if TransportCredentials are present.
|
||||||
func WithAuthority(a string) DialOption {
|
func WithAuthority(a string) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.authority = a
|
o.authority = a
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithChannelzParentID returns a DialOption that specifies the channelz ID of
|
// WithChannelzParentID returns a DialOption that specifies the channelz ID of
|
||||||
// current ClientConn's parent. This function is used in nested channel creation
|
// current ClientConn's parent. This function is used in nested channel creation
|
||||||
// (e.g. grpclb dial).
|
// (e.g. grpclb dial).
|
||||||
func WithChannelzParentID(id int64) DialOption {
|
func WithChannelzParentID(id int64) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.channelzParentID = id
|
o.channelzParentID = id
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithDisableServiceConfig returns a DialOption that causes grpc to ignore any
|
// WithDisableServiceConfig returns a DialOption that causes grpc to ignore any
|
||||||
// service config provided by the resolver and provides a hint to the resolver
|
// service config provided by the resolver and provides a hint to the resolver
|
||||||
// to not fetch service configs.
|
// to not fetch service configs.
|
||||||
func WithDisableServiceConfig() DialOption {
|
func WithDisableServiceConfig() DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.disableServiceConfig = true
|
o.disableServiceConfig = true
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithDisableRetry returns a DialOption that disables retries, even if the
|
// WithDisableRetry returns a DialOption that disables retries, even if the
|
||||||
|
@ -400,17 +426,17 @@ func WithDisableServiceConfig() DialOption {
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// This API is EXPERIMENTAL.
|
||||||
func WithDisableRetry() DialOption {
|
func WithDisableRetry() DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.disableRetry = true
|
o.disableRetry = true
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithMaxHeaderListSize returns a DialOption that specifies the maximum
|
// WithMaxHeaderListSize returns a DialOption that specifies the maximum
|
||||||
// (uncompressed) size of header list that the client is prepared to accept.
|
// (uncompressed) size of header list that the client is prepared to accept.
|
||||||
func WithMaxHeaderListSize(s uint32) DialOption {
|
func WithMaxHeaderListSize(s uint32) DialOption {
|
||||||
return func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.copts.MaxHeaderListSize = &s
|
o.copts.MaxHeaderListSize = &s
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultDialOptions() dialOptions {
|
func defaultDialOptions() dialOptions {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче