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