stubserver.go 6.6 KB

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