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  }