Home | History | Annotate | Download | only in grpc
      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 
     19 package grpc
     20 
     21 import (
     22 	"encoding/json"
     23 	"fmt"
     24 	"strconv"
     25 	"strings"
     26 	"time"
     27 
     28 	"google.golang.org/grpc/grpclog"
     29 )
     30 
     31 const 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
     39 type 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
     67 type 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 
     80 func 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 
    124 type jsonName struct {
    125 	Service *string
    126 	Method  *string
    127 }
    128 
    129 func (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.
    141 type 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.
    150 type jsonSC struct {
    151 	LoadBalancingPolicy   *string
    152 	StickinessMetadataKey *string
    153 	MethodConfig          *[]jsonMC
    154 }
    155 
    156 func 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 
    211 func min(a, b *int) *int {
    212 	if *a < *b {
    213 		return a
    214 	}
    215 	return b
    216 }
    217 
    218 func 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 
    231 func newInt(b int) *int {
    232 	return &b
    233 }
    234