2021-03-12 12:33:32 +00:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
2021-05-20 15:51:28 +00:00
|
|
|
"crypto/tls"
|
2021-05-27 12:21:58 +00:00
|
|
|
"net/url"
|
2021-03-12 12:33:32 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
)
|
|
|
|
|
2021-05-27 12:21:58 +00:00
|
|
|
const (
|
|
|
|
grpcScheme = "grpc"
|
|
|
|
grpcTLSScheme = "grpcs"
|
|
|
|
)
|
|
|
|
|
2021-03-12 12:33:32 +00:00
|
|
|
// Option is a Client's option.
|
|
|
|
type Option func(*cfg)
|
|
|
|
|
|
|
|
type cfg struct {
|
|
|
|
addr string
|
|
|
|
|
|
|
|
dialTimeout time.Duration
|
2021-12-24 10:18:57 +00:00
|
|
|
rwTimeout time.Duration
|
2021-03-12 12:33:32 +00:00
|
|
|
|
2021-05-20 15:51:28 +00:00
|
|
|
tlsCfg *tls.Config
|
|
|
|
|
2021-03-12 12:33:32 +00:00
|
|
|
conn *grpc.ClientConn
|
|
|
|
}
|
|
|
|
|
2021-12-24 10:18:57 +00:00
|
|
|
const (
|
|
|
|
defaultDialTimeout = 5 * time.Second
|
|
|
|
defaultRWTimeout = 1 * time.Minute
|
|
|
|
)
|
2021-03-12 12:33:32 +00:00
|
|
|
|
|
|
|
func defaultCfg() *cfg {
|
|
|
|
return &cfg{
|
|
|
|
dialTimeout: defaultDialTimeout,
|
2021-12-24 10:18:57 +00:00
|
|
|
rwTimeout: defaultRWTimeout,
|
2021-03-12 12:33:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithNetworkAddress returns option to specify
|
|
|
|
// network address of the remote server.
|
|
|
|
//
|
|
|
|
// Ignored if WithGRPCConn is provided.
|
|
|
|
func WithNetworkAddress(v string) Option {
|
|
|
|
return func(c *cfg) {
|
|
|
|
if v != "" {
|
|
|
|
c.addr = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-27 12:21:58 +00:00
|
|
|
// WithNetworkURIAddress combines WithNetworkAddress and WithTLSCfg options
|
|
|
|
// based on arguments.
|
|
|
|
//
|
|
|
|
// Do not use along with WithNetworkAddress and WithTLSCfg.
|
|
|
|
//
|
|
|
|
// Ignored if WithGRPCConn is provided.
|
|
|
|
func WithNetworkURIAddress(addr string, tlsCfg *tls.Config) []Option {
|
|
|
|
uri, err := url.ParseRequestURI(addr)
|
|
|
|
if err != nil {
|
|
|
|
return []Option{WithNetworkAddress(addr)}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if passed string was parsed correctly
|
|
|
|
// URIs that do not start with a slash after the scheme are interpreted as:
|
|
|
|
// `scheme:opaque` => if `opaque` is not empty, then it is supposed that URI
|
|
|
|
// is in `host:port` format
|
|
|
|
if uri.Opaque != "" {
|
|
|
|
return []Option{WithNetworkAddress(addr)}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch uri.Scheme {
|
|
|
|
case grpcScheme:
|
|
|
|
tlsCfg = nil
|
|
|
|
case grpcTLSScheme:
|
|
|
|
if tlsCfg == nil {
|
|
|
|
tlsCfg = &tls.Config{}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
// not supported scheme
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return []Option{
|
|
|
|
WithNetworkAddress(uri.Host),
|
|
|
|
WithTLSCfg(tlsCfg),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-12 12:33:32 +00:00
|
|
|
// WithDialTimeout returns option to specify
|
|
|
|
// dial timeout of the remote server connection.
|
|
|
|
//
|
|
|
|
// Ignored if WithGRPCConn is provided.
|
|
|
|
func WithDialTimeout(v time.Duration) Option {
|
|
|
|
return func(c *cfg) {
|
|
|
|
if v > 0 {
|
|
|
|
c.dialTimeout = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-24 10:18:57 +00:00
|
|
|
// WithRWTimeout returns option to specify timeout
|
|
|
|
// for reading and writing single gRPC message.
|
|
|
|
func WithRWTimeout(v time.Duration) Option {
|
|
|
|
return func(c *cfg) {
|
|
|
|
if v > 0 {
|
|
|
|
c.rwTimeout = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-20 15:51:28 +00:00
|
|
|
// WithTLSCfg returns option to specify
|
|
|
|
// TLS configuration.
|
|
|
|
//
|
|
|
|
// Ignored if WithGRPCConn is provided.
|
|
|
|
func WithTLSCfg(v *tls.Config) Option {
|
|
|
|
return func(c *cfg) {
|
2021-05-27 12:21:58 +00:00
|
|
|
c.tlsCfg = v
|
2021-05-20 15:51:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-12 12:33:32 +00:00
|
|
|
// WithGRPCConn returns option to specify
|
|
|
|
// gRPC virtual connection.
|
|
|
|
func WithGRPCConn(v *grpc.ClientConn) Option {
|
|
|
|
return func(c *cfg) {
|
|
|
|
if v != nil {
|
|
|
|
c.conn = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|