package grpc
import (
internalserviceconfig
)
const maxInt = int(^uint(0) >> 1)
type MethodConfig = internalserviceconfig.MethodConfig
type lbConfig struct {
name string
cfg serviceconfig.LoadBalancingConfig
}
type ServiceConfig struct {
serviceconfig.Config
LB *string
lbConfig *lbConfig
Methods map[string]MethodConfig
retryThrottling *retryThrottlingPolicy
healthCheckConfig *healthCheckConfig
rawJSONString string
}
type healthCheckConfig struct {
ServiceName string
}
type jsonRetryPolicy struct {
MaxAttempts int
InitialBackoff string
MaxBackoff string
BackoffMultiplier float64
RetryableStatusCodes []codes.Code
}
type retryThrottlingPolicy struct {
MaxTokens float64
TokenRatio float64
}
func ( *string) (*time.Duration, error) {
if == nil {
return nil, nil
}
if !strings.HasSuffix(*, "s") {
return nil, fmt.Errorf("malformed duration %q", *)
}
:= strings.SplitN((*)[:len(*)-1], ".", 3)
if len() > 2 {
return nil, fmt.Errorf("malformed duration %q", *)
}
:= false
var time.Duration
if len([0]) > 0 {
, := strconv.ParseInt([0], 10, 32)
if != nil {
return nil, fmt.Errorf("malformed duration %q: %v", *, )
}
= time.Duration() * time.Second
= true
}
if len() == 2 && len([1]) > 0 {
if len([1]) > 9 {
return nil, fmt.Errorf("malformed duration %q", *)
}
, := strconv.ParseInt([1], 10, 64)
if != nil {
return nil, fmt.Errorf("malformed duration %q: %v", *, )
}
for := 9; > len([1]); -- {
*= 10
}
+= time.Duration()
= true
}
if ! {
return nil, fmt.Errorf("malformed duration %q", *)
}
return &, nil
}
type jsonName struct {
Service string
Method string
}
var (
errDuplicatedName = errors.New("duplicated name")
errEmptyServiceNonEmptyMethod = errors.New("cannot combine empty 'service' and non-empty 'method'")
)
func ( jsonName) () (string, error) {
if .Service == "" {
if .Method != "" {
return "", errEmptyServiceNonEmptyMethod
}
return "", nil
}
:= "/" + .Service + "/"
if .Method != "" {
+= .Method
}
return , nil
}
type jsonMC struct {
Name *[]jsonName
WaitForReady *bool
Timeout *string
MaxRequestMessageBytes *int64
MaxResponseMessageBytes *int64
RetryPolicy *jsonRetryPolicy
}
type jsonSC struct {
LoadBalancingPolicy *string
LoadBalancingConfig *internalserviceconfig.BalancerConfig
MethodConfig *[]jsonMC
RetryThrottling *retryThrottlingPolicy
HealthCheckConfig *healthCheckConfig
}
func () {
internal.ParseServiceConfig = parseServiceConfig
}
func ( string) *serviceconfig.ParseResult {
if len() == 0 {
return &serviceconfig.ParseResult{Err: fmt.Errorf("no JSON service config provided")}
}
var jsonSC
:= json.Unmarshal([]byte(), &)
if != nil {
logger.Warningf("grpc: unmarshaling service config %s: %v", , )
return &serviceconfig.ParseResult{Err: }
}
:= ServiceConfig{
LB: .LoadBalancingPolicy,
Methods: make(map[string]MethodConfig),
retryThrottling: .RetryThrottling,
healthCheckConfig: .HealthCheckConfig,
rawJSONString: ,
}
if := .LoadBalancingConfig; != nil {
.lbConfig = &lbConfig{
name: .Name,
cfg: .Config,
}
}
if .MethodConfig == nil {
return &serviceconfig.ParseResult{Config: &}
}
:= map[string]struct{}{}
for , := range *.MethodConfig {
if .Name == nil {
continue
}
, := parseDuration(.Timeout)
if != nil {
logger.Warningf("grpc: unmarshaling service config %s: %v", , )
return &serviceconfig.ParseResult{Err: }
}
:= MethodConfig{
WaitForReady: .WaitForReady,
Timeout: ,
}
if .RetryPolicy, = convertRetryPolicy(.RetryPolicy); != nil {
logger.Warningf("grpc: unmarshaling service config %s: %v", , )
return &serviceconfig.ParseResult{Err: }
}
if .MaxRequestMessageBytes != nil {
if *.MaxRequestMessageBytes > int64(maxInt) {
.MaxReqSize = newInt(maxInt)
} else {
.MaxReqSize = newInt(int(*.MaxRequestMessageBytes))
}
}
if .MaxResponseMessageBytes != nil {
if *.MaxResponseMessageBytes > int64(maxInt) {
.MaxRespSize = newInt(maxInt)
} else {
.MaxRespSize = newInt(int(*.MaxResponseMessageBytes))
}
}
for , := range *.Name {
, := .generatePath()
if != nil {
logger.Warningf("grpc: error unmarshaling service config %s due to methodConfig[%d]: %v", , , )
return &serviceconfig.ParseResult{Err: }
}
if , := []; {
= errDuplicatedName
logger.Warningf("grpc: error unmarshaling service config %s due to methodConfig[%d]: %v", , , )
return &serviceconfig.ParseResult{Err: }
}
[] = struct{}{}
.Methods[] =
}
}
if .retryThrottling != nil {
if := .retryThrottling.MaxTokens; <= 0 || > 1000 {
return &serviceconfig.ParseResult{Err: fmt.Errorf("invalid retry throttling config: maxTokens (%v) out of range (0, 1000]", )}
}
if := .retryThrottling.TokenRatio; <= 0 {
return &serviceconfig.ParseResult{Err: fmt.Errorf("invalid retry throttling config: tokenRatio (%v) may not be negative", )}
}
}
return &serviceconfig.ParseResult{Config: &}
}
func ( *jsonRetryPolicy) ( *internalserviceconfig.RetryPolicy, error) {
if == nil {
return nil, nil
}
, := parseDuration(&.InitialBackoff)
if != nil {
return nil,
}
, := parseDuration(&.MaxBackoff)
if != nil {
return nil,
}
if .MaxAttempts <= 1 ||
* <= 0 ||
* <= 0 ||
.BackoffMultiplier <= 0 ||
len(.RetryableStatusCodes) == 0 {
logger.Warningf("grpc: ignoring retry policy %v due to illegal configuration", )
return nil, nil
}
:= &internalserviceconfig.RetryPolicy{
MaxAttempts: .MaxAttempts,
InitialBackoff: *,
MaxBackoff: *,
BackoffMultiplier: .BackoffMultiplier,
RetryableStatusCodes: make(map[codes.Code]bool),
}
if .MaxAttempts > 5 {
.MaxAttempts = 5
}
for , := range .RetryableStatusCodes {
.RetryableStatusCodes[] = true
}
return , nil
}
func (, *int) *int {
if * < * {
return
}
return
}
func (, *int, int) *int {
if == nil && == nil {
return &
}
if != nil && != nil {
return min(, )
}
if != nil {
return
}
return
}
func ( int) *int {
return &
}
func () {
internal.EqualServiceConfigForTesting = equalServiceConfig
}
func (, serviceconfig.Config) bool {
if == nil && == nil {
return true
}
, := .(*ServiceConfig)
if ! {
return false
}
, := .(*ServiceConfig)
if ! {
return false
}
:= .rawJSONString
.rawJSONString = ""
:= .rawJSONString
.rawJSONString = ""
defer func() {
.rawJSONString =
.rawJSONString =
}()
return reflect.DeepEqual(, )
}