package grpc
import (
internalserviceconfig
)
const maxInt = int(^uint(0) >> 1)
type MethodConfig = internalserviceconfig.MethodConfig
type ServiceConfig struct {
serviceconfig.Config
lbConfig serviceconfig.LoadBalancingConfig
Methods map[string]MethodConfig
retryThrottling *retryThrottlingPolicy
healthCheckConfig *healthCheckConfig
rawJSONString string
}
type healthCheckConfig struct {
ServiceName string
}
type jsonRetryPolicy struct {
MaxAttempts int
InitialBackoff internalserviceconfig.Duration
MaxBackoff internalserviceconfig.Duration
BackoffMultiplier float64
RetryableStatusCodes []codes.Code
}
type retryThrottlingPolicy struct {
MaxTokens float64
TokenRatio float64
}
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 *internalserviceconfig.Duration
MaxRequestMessageBytes *int64
MaxResponseMessageBytes *int64
RetryPolicy *jsonRetryPolicy
}
type jsonSC struct {
LoadBalancingPolicy *string
LoadBalancingConfig *json.RawMessage
MethodConfig *[]jsonMC
RetryThrottling *retryThrottlingPolicy
HealthCheckConfig *healthCheckConfig
}
func () {
internal.ParseServiceConfig = func( string) *serviceconfig.ParseResult {
return parseServiceConfig(, defaultMaxCallAttempts)
}
}
func ( string, int) *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: unmarshalling service config %s: %v", , )
return &serviceconfig.ParseResult{Err: }
}
:= ServiceConfig{
Methods: make(map[string]MethodConfig),
retryThrottling: .RetryThrottling,
healthCheckConfig: .HealthCheckConfig,
rawJSONString: ,
}
:= .LoadBalancingConfig
if == nil {
:= pickfirst.Name
if .LoadBalancingPolicy != nil {
= *.LoadBalancingPolicy
}
if balancer.Get() == nil {
= pickfirst.Name
}
:= []map[string]any{{: struct{}{}}}
, := json.Marshal()
if != nil {
return &serviceconfig.ParseResult{Err: fmt.Errorf("unexpected error marshaling simple LB config: %w", )}
}
:= json.RawMessage()
= &
}
, := gracefulswitch.ParseConfig(*)
if != nil {
return &serviceconfig.ParseResult{Err: }
}
.lbConfig =
if .MethodConfig == nil {
return &serviceconfig.ParseResult{Config: &}
}
:= map[string]struct{}{}
for , := range *.MethodConfig {
if .Name == nil {
continue
}
:= MethodConfig{
WaitForReady: .WaitForReady,
Timeout: (*time.Duration)(.Timeout),
}
if .RetryPolicy, = convertRetryPolicy(.RetryPolicy, ); != nil {
logger.Warningf("grpc: unmarshalling 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 unmarshalling service config %s due to methodConfig[%d]: %v", , , )
return &serviceconfig.ParseResult{Err: }
}
if , := []; {
= errDuplicatedName
logger.Warningf("grpc: error unmarshalling 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) bool {
return .MaxAttempts > 1 &&
.InitialBackoff > 0 &&
.MaxBackoff > 0 &&
.BackoffMultiplier > 0 &&
len(.RetryableStatusCodes) > 0
}
func ( *jsonRetryPolicy, int) ( *internalserviceconfig.RetryPolicy, error) {
if == nil {
return nil, nil
}
if !isValidRetryPolicy() {
return nil, fmt.Errorf("invalid retry policy (%+v): ", )
}
if .MaxAttempts < {
= .MaxAttempts
}
:= &internalserviceconfig.RetryPolicy{
MaxAttempts: ,
InitialBackoff: time.Duration(.InitialBackoff),
MaxBackoff: time.Duration(.MaxBackoff),
BackoffMultiplier: .BackoffMultiplier,
RetryableStatusCodes: make(map[codes.Code]bool),
}
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 minPointers(, )
}
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(, )
}