1/* 2 * 3 * Copyright 2017 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 "encoding/json" 23 "fmt" 24 "strconv" 25 "strings" 26 "time" 27 28 "google.golang.org/grpc/grpclog" 29) 30 31const maxInt = int(^uint(0) >> 1) 32 33// MethodConfig defines the configuration recommended by the service providers for a 34// particular method. 35// 36// Deprecated: Users should not use this struct. Service config should be received 37// through name resolver, as specified here 38// https://github.com/grpc/grpc/blob/master/doc/service_config.md 39type MethodConfig struct { 40 // WaitForReady indicates whether RPCs sent to this method should wait until 41 // the connection is ready by default (!failfast). The value specified via the 42 // gRPC client API will override the value set here. 43 WaitForReady *bool 44 // Timeout is the default timeout for RPCs sent to this method. The actual 45 // deadline used will be the minimum of the value specified here and the value 46 // set by the application via the gRPC client API. If either one is not set, 47 // then the other will be used. If neither is set, then the RPC has no deadline. 48 Timeout *time.Duration 49 // MaxReqSize is the maximum allowed payload size for an individual request in a 50 // stream (client->server) in bytes. The size which is measured is the serialized 51 // payload after per-message compression (but before stream compression) in bytes. 52 // The actual value used is the minimum of the value specified here and the value set 53 // by the application via the gRPC client API. If either one is not set, then the other 54 // will be used. If neither is set, then the built-in default is used. 55 MaxReqSize *int 56 // MaxRespSize is the maximum allowed payload size for an individual response in a 57 // stream (server->client) in bytes. 58 MaxRespSize *int 59} 60 61// ServiceConfig is provided by the service provider and contains parameters for how 62// clients that connect to the service should behave. 63// 64// Deprecated: Users should not use this struct. Service config should be received 65// through name resolver, as specified here 66// https://github.com/grpc/grpc/blob/master/doc/service_config.md 67type ServiceConfig struct { 68 // LB is the load balancer the service providers recommends. The balancer specified 69 // via grpc.WithBalancer will override this. 70 LB *string 71 // Methods contains a map for the methods in this service. 72 // If there is an exact match for a method (i.e. /service/method) in the map, use the corresponding MethodConfig. 73 // If there's no exact match, look for the default config for the service (/service/) and use the corresponding MethodConfig if it exists. 74 // Otherwise, the method has no MethodConfig to use. 75 Methods map[string]MethodConfig 76 77 stickinessMetadataKey *string 78} 79 80func parseDuration(s *string) (*time.Duration, error) { 81 if s == nil { 82 return nil, nil 83 } 84 if !strings.HasSuffix(*s, "s") { 85 return nil, fmt.Errorf("malformed duration %q", *s) 86 } 87 ss := strings.SplitN((*s)[:len(*s)-1], ".", 3) 88 if len(ss) > 2 { 89 return nil, fmt.Errorf("malformed duration %q", *s) 90 } 91 // hasDigits is set if either the whole or fractional part of the number is 92 // present, since both are optional but one is required. 93 hasDigits := false 94 var d time.Duration 95 if len(ss[0]) > 0 { 96 i, err := strconv.ParseInt(ss[0], 10, 32) 97 if err != nil { 98 return nil, fmt.Errorf("malformed duration %q: %v", *s, err) 99 } 100 d = time.Duration(i) * time.Second 101 hasDigits = true 102 } 103 if len(ss) == 2 && len(ss[1]) > 0 { 104 if len(ss[1]) > 9 { 105 return nil, fmt.Errorf("malformed duration %q", *s) 106 } 107 f, err := strconv.ParseInt(ss[1], 10, 64) 108 if err != nil { 109 return nil, fmt.Errorf("malformed duration %q: %v", *s, err) 110 } 111 for i := 9; i > len(ss[1]); i-- { 112 f *= 10 113 } 114 d += time.Duration(f) 115 hasDigits = true 116 } 117 if !hasDigits { 118 return nil, fmt.Errorf("malformed duration %q", *s) 119 } 120 121 return &d, nil 122} 123 124type jsonName struct { 125 Service *string 126 Method *string 127} 128 129func (j jsonName) generatePath() (string, bool) { 130 if j.Service == nil { 131 return "", false 132 } 133 res := "/" + *j.Service + "/" 134 if j.Method != nil { 135 res += *j.Method 136 } 137 return res, true 138} 139 140// TODO(lyuxuan): delete this struct after cleaning up old service config implementation. 141type jsonMC struct { 142 Name *[]jsonName 143 WaitForReady *bool 144 Timeout *string 145 MaxRequestMessageBytes *int64 146 MaxResponseMessageBytes *int64 147} 148 149// TODO(lyuxuan): delete this struct after cleaning up old service config implementation. 150type jsonSC struct { 151 LoadBalancingPolicy *string 152 StickinessMetadataKey *string 153 MethodConfig *[]jsonMC 154} 155 156func parseServiceConfig(js string) (ServiceConfig, error) { 157 var rsc jsonSC 158 err := json.Unmarshal([]byte(js), &rsc) 159 if err != nil { 160 grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err) 161 return ServiceConfig{}, err 162 } 163 sc := ServiceConfig{ 164 LB: rsc.LoadBalancingPolicy, 165 Methods: make(map[string]MethodConfig), 166 167 stickinessMetadataKey: rsc.StickinessMetadataKey, 168 } 169 if rsc.MethodConfig == nil { 170 return sc, nil 171 } 172 173 for _, m := range *rsc.MethodConfig { 174 if m.Name == nil { 175 continue 176 } 177 d, err := parseDuration(m.Timeout) 178 if err != nil { 179 grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err) 180 return ServiceConfig{}, err 181 } 182 183 mc := MethodConfig{ 184 WaitForReady: m.WaitForReady, 185 Timeout: d, 186 } 187 if m.MaxRequestMessageBytes != nil { 188 if *m.MaxRequestMessageBytes > int64(maxInt) { 189 mc.MaxReqSize = newInt(maxInt) 190 } else { 191 mc.MaxReqSize = newInt(int(*m.MaxRequestMessageBytes)) 192 } 193 } 194 if m.MaxResponseMessageBytes != nil { 195 if *m.MaxResponseMessageBytes > int64(maxInt) { 196 mc.MaxRespSize = newInt(maxInt) 197 } else { 198 mc.MaxRespSize = newInt(int(*m.MaxResponseMessageBytes)) 199 } 200 } 201 for _, n := range *m.Name { 202 if path, valid := n.generatePath(); valid { 203 sc.Methods[path] = mc 204 } 205 } 206 } 207 208 return sc, nil 209} 210 211func min(a, b *int) *int { 212 if *a < *b { 213 return a 214 } 215 return b 216} 217 218func getMaxSize(mcMax, doptMax *int, defaultVal int) *int { 219 if mcMax == nil && doptMax == nil { 220 return &defaultVal 221 } 222 if mcMax != nil && doptMax != nil { 223 return min(mcMax, doptMax) 224 } 225 if mcMax != nil { 226 return mcMax 227 } 228 return doptMax 229} 230 231func newInt(b int) *int { 232 return &b 233} 234