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	"fmt"
23	"strings"
24
25	"google.golang.org/grpc/grpclog"
26	"google.golang.org/grpc/resolver"
27)
28
29// ccResolverWrapper is a wrapper on top of cc for resolvers.
30// It implements resolver.ClientConnection interface.
31type ccResolverWrapper struct {
32	cc       *ClientConn
33	resolver resolver.Resolver
34	addrCh   chan []resolver.Address
35	scCh     chan string
36	done     chan struct{}
37}
38
39// split2 returns the values from strings.SplitN(s, sep, 2).
40// If sep is not found, it returns ("", s, false) instead.
41func split2(s, sep string) (string, string, bool) {
42	spl := strings.SplitN(s, sep, 2)
43	if len(spl) < 2 {
44		return "", "", false
45	}
46	return spl[0], spl[1], true
47}
48
49// parseTarget splits target into a struct containing scheme, authority and
50// endpoint.
51func parseTarget(target string) (ret resolver.Target) {
52	var ok bool
53	ret.Scheme, ret.Endpoint, ok = split2(target, "://")
54	if !ok {
55		return resolver.Target{Endpoint: target}
56	}
57	ret.Authority, ret.Endpoint, _ = split2(ret.Endpoint, "/")
58	return ret
59}
60
61// newCCResolverWrapper parses cc.target for scheme and gets the resolver
62// builder for this scheme. It then builds the resolver and starts the
63// monitoring goroutine for it.
64func newCCResolverWrapper(cc *ClientConn) (*ccResolverWrapper, error) {
65	grpclog.Infof("dialing to target with scheme: %q", cc.parsedTarget.Scheme)
66
67	rb := resolver.Get(cc.parsedTarget.Scheme)
68	if rb == nil {
69		return nil, fmt.Errorf("could not get resolver for scheme: %q", cc.parsedTarget.Scheme)
70	}
71
72	ccr := &ccResolverWrapper{
73		cc:     cc,
74		addrCh: make(chan []resolver.Address, 1),
75		scCh:   make(chan string, 1),
76		done:   make(chan struct{}),
77	}
78
79	var err error
80	ccr.resolver, err = rb.Build(cc.parsedTarget, ccr, resolver.BuildOption{})
81	if err != nil {
82		return nil, err
83	}
84	go ccr.watcher()
85	return ccr, nil
86}
87
88// watcher processes address updates and service config updates sequencially.
89// Otherwise, we need to resolve possible races between address and service
90// config (e.g. they specify different balancer types).
91func (ccr *ccResolverWrapper) watcher() {
92	for {
93		select {
94		case <-ccr.done:
95			return
96		default:
97		}
98
99		select {
100		case addrs := <-ccr.addrCh:
101			select {
102			case <-ccr.done:
103				return
104			default:
105			}
106			grpclog.Infof("ccResolverWrapper: sending new addresses to cc: %v", addrs)
107			ccr.cc.handleResolvedAddrs(addrs, nil)
108		case sc := <-ccr.scCh:
109			select {
110			case <-ccr.done:
111				return
112			default:
113			}
114			grpclog.Infof("ccResolverWrapper: got new service config: %v", sc)
115			ccr.cc.handleServiceConfig(sc)
116		case <-ccr.done:
117			return
118		}
119	}
120}
121
122func (ccr *ccResolverWrapper) close() {
123	ccr.resolver.Close()
124	close(ccr.done)
125}
126
127// NewAddress is called by the resolver implemenetion to send addresses to gRPC.
128func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
129	select {
130	case <-ccr.addrCh:
131	default:
132	}
133	ccr.addrCh <- addrs
134}
135
136// NewServiceConfig is called by the resolver implemenetion to send service
137// configs to gPRC.
138func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
139	select {
140	case <-ccr.scCh:
141	default:
142	}
143	ccr.scCh <- sc
144}
145