gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/grpc/internal/stubserver/stubserver.go (about) 1 /* 2 * 3 * Copyright 2020 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 stubserver is a stubbable implementation of 20 // google.golang.org/grpc/test/grpc_testing for testing purposes. 21 package stubserver 22 23 import ( 24 "context" 25 "fmt" 26 "gitee.com/ks-custle/core-gm/grpc/credentials/insecure" 27 "net" 28 "time" 29 30 "gitee.com/ks-custle/core-gm/grpc" 31 "gitee.com/ks-custle/core-gm/grpc/connectivity" 32 "gitee.com/ks-custle/core-gm/grpc/resolver" 33 "gitee.com/ks-custle/core-gm/grpc/resolver/manual" 34 "gitee.com/ks-custle/core-gm/grpc/serviceconfig" 35 36 testpb "gitee.com/ks-custle/core-gm/grpc/test/grpc_testing" 37 ) 38 39 // StubServer is a server that is easy to customize within individual test 40 // cases. 41 type StubServer struct { 42 // Guarantees we satisfy this interface; panics if unimplemented methods are called. 43 testpb.TestServiceServer 44 45 // Customizable implementations of server handlers. 46 EmptyCallF func(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) 47 UnaryCallF func(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) 48 FullDuplexCallF func(stream testpb.TestService_FullDuplexCallServer) error 49 50 // A client connected to this service the test may use. Created in Start(). 51 Client testpb.TestServiceClient 52 CC *grpc.ClientConn 53 S *grpc.Server 54 55 // Parameters for Listen and Dial. Defaults will be used if these are empty 56 // before Start. 57 Network string 58 Address string 59 Target string 60 61 cleanups []func() // Lambdas executed in Stop(); populated by Start(). 62 63 // Set automatically if Target == "" 64 R *manual.Resolver 65 } 66 67 // EmptyCall is the handler for testpb.EmptyCall 68 func (ss *StubServer) EmptyCall(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) { 69 return ss.EmptyCallF(ctx, in) 70 } 71 72 // UnaryCall is the handler for testpb.UnaryCall 73 func (ss *StubServer) UnaryCall(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) { 74 return ss.UnaryCallF(ctx, in) 75 } 76 77 // FullDuplexCall is the handler for testpb.FullDuplexCall 78 func (ss *StubServer) FullDuplexCall(stream testpb.TestService_FullDuplexCallServer) error { 79 return ss.FullDuplexCallF(stream) 80 } 81 82 // Start starts the server and creates a client connected to it. 83 func (ss *StubServer) Start(sopts []grpc.ServerOption, dopts ...grpc.DialOption) error { 84 if err := ss.StartServer(sopts...); err != nil { 85 return err 86 } 87 return ss.StartClient(dopts...) 88 } 89 90 // StartServer only starts the server. It does not create a client to it. 91 func (ss *StubServer) StartServer(sopts ...grpc.ServerOption) error { 92 if ss.Network == "" { 93 ss.Network = "tcp" 94 } 95 if ss.Address == "" { 96 ss.Address = "localhost:0" 97 } 98 if ss.Target == "" { 99 ss.R = manual.NewBuilderWithScheme("whatever") 100 } 101 102 lis, err := net.Listen(ss.Network, ss.Address) 103 if err != nil { 104 return fmt.Errorf("net.Listen(%q, %q) = %v", ss.Network, ss.Address, err) 105 } 106 ss.Address = lis.Addr().String() 107 ss.cleanups = append(ss.cleanups, func() { _ = lis.Close() }) 108 109 s := grpc.NewServer(sopts...) 110 testpb.RegisterTestServiceServer(s, ss) 111 go func() { 112 _ = s.Serve(lis) 113 }() 114 ss.cleanups = append(ss.cleanups, s.Stop) 115 ss.S = s 116 return nil 117 } 118 119 // StartClient creates a client connected to this service that the test may use. 120 // The newly created client will be available in the Client field of StubServer. 121 func (ss *StubServer) StartClient(dopts ...grpc.DialOption) error { 122 // grpc.WithInsecure is deprecated, use WithTransportCredentials and insecure.NewCredentials() instead. 123 //opts := append([]grpc.DialOption{grpc.WithInsecure()}, dopts...) 124 opts := append([]grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}, dopts...) 125 if ss.R != nil { 126 ss.Target = ss.R.Scheme() + ":///" + ss.Address 127 opts = append(opts, grpc.WithResolvers(ss.R)) 128 } 129 130 cc, err := grpc.Dial(ss.Target, opts...) 131 if err != nil { 132 return fmt.Errorf("grpc.Dial(%q) = %v", ss.Target, err) 133 } 134 ss.CC = cc 135 if ss.R != nil { 136 ss.R.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: ss.Address}}}) 137 } 138 if err := waitForReady(cc); err != nil { 139 return err 140 } 141 142 ss.cleanups = append(ss.cleanups, func() { _ = cc.Close() }) 143 144 ss.Client = testpb.NewTestServiceClient(cc) 145 return nil 146 } 147 148 // NewServiceConfig applies sc to ss.Client using the resolver (if present). 149 func (ss *StubServer) NewServiceConfig(sc string) { 150 if ss.R != nil { 151 ss.R.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: ss.Address}}, ServiceConfig: parseCfg(ss.R, sc)}) 152 } 153 } 154 155 func waitForReady(cc *grpc.ClientConn) error { 156 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 157 defer cancel() 158 for { 159 s := cc.GetState() 160 if s == connectivity.Ready { 161 return nil 162 } 163 if !cc.WaitForStateChange(ctx, s) { 164 // ctx got timeout or canceled. 165 return ctx.Err() 166 } 167 } 168 } 169 170 // Stop stops ss and cleans up all resources it consumed. 171 func (ss *StubServer) Stop() { 172 for i := len(ss.cleanups) - 1; i >= 0; i-- { 173 ss.cleanups[i]() 174 } 175 } 176 177 func parseCfg(r *manual.Resolver, s string) *serviceconfig.ParseResult { 178 g := r.CC.ParseServiceConfig(s) 179 if g.Err != nil { 180 panic(fmt.Sprintf("Error parsing config %q: %v", s, g.Err)) 181 } 182 return g 183 }