go.uber.org/yarpc@v1.72.1/transport/grpc/inbound.go (about)

     1  // Copyright (c) 2022 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package grpc
    22  
    23  import (
    24  	"errors"
    25  	"net"
    26  	"sync"
    27  
    28  	"go.uber.org/yarpc/api/transport"
    29  	yarpctls "go.uber.org/yarpc/api/transport/tls"
    30  	"go.uber.org/yarpc/api/x/introspection"
    31  	"go.uber.org/yarpc/pkg/lifecycle"
    32  	"go.uber.org/yarpc/transport/internal/tls/muxlistener"
    33  	"go.uber.org/yarpc/yarpcerrors"
    34  	"go.uber.org/zap"
    35  	"google.golang.org/grpc"
    36  )
    37  
    38  var (
    39  	errRouterNotSet = yarpcerrors.Newf(yarpcerrors.CodeInternal, "router not set")
    40  
    41  	_ introspection.IntrospectableInbound = (*Inbound)(nil)
    42  	_ transport.Inbound                   = (*Inbound)(nil)
    43  )
    44  
    45  // Inbound is a grpc transport.Inbound.
    46  type Inbound struct {
    47  	once     *lifecycle.Once
    48  	lock     sync.RWMutex
    49  	t        *Transport
    50  	listener net.Listener
    51  	options  *inboundOptions
    52  	router   transport.Router
    53  	server   *grpc.Server
    54  }
    55  
    56  // newInbound returns a new Inbound for the given listener.
    57  func newInbound(t *Transport, listener net.Listener, options ...InboundOption) *Inbound {
    58  	return &Inbound{
    59  		once:     lifecycle.NewOnce(),
    60  		t:        t,
    61  		listener: listener,
    62  		options:  newInboundOptions(options),
    63  	}
    64  }
    65  
    66  // Start implements transport.Lifecycle#Start.
    67  func (i *Inbound) Start() error {
    68  	return i.once.Start(i.start)
    69  }
    70  
    71  // Stop implements transport.Lifecycle#Stop.
    72  func (i *Inbound) Stop() error {
    73  	return i.once.Stop(i.stop)
    74  }
    75  
    76  // IsRunning implements transport.Lifecycle#IsRunning.
    77  func (i *Inbound) IsRunning() bool {
    78  	return i.once.IsRunning()
    79  }
    80  
    81  // SetRouter implements transport.Inbound#SetRouter.
    82  func (i *Inbound) SetRouter(router transport.Router) {
    83  	i.lock.Lock()
    84  	defer i.lock.Unlock()
    85  	i.router = router
    86  }
    87  
    88  // Addr returns the address on which the server is listening.
    89  //
    90  // Returns nil if Start has not been called yet
    91  func (i *Inbound) Addr() net.Addr {
    92  	i.lock.RLock()
    93  	defer i.lock.RUnlock()
    94  	// i.server is set in start, so checking against nil checks
    95  	// if Start has been called
    96  	// we check if i.listener is nil just for safety
    97  	if i.server == nil || i.listener == nil {
    98  		return nil
    99  	}
   100  	return i.listener.Addr()
   101  }
   102  
   103  // Transports implements transport.Inbound#Transports.
   104  func (i *Inbound) Transports() []transport.Transport {
   105  	return []transport.Transport{i.t}
   106  }
   107  
   108  func (i *Inbound) start() error {
   109  	i.lock.Lock()
   110  	defer i.lock.Unlock()
   111  	if i.router == nil {
   112  		return errRouterNotSet
   113  	}
   114  
   115  	handler := newHandler(i, i.t.options.logger)
   116  
   117  	serverOptions := []grpc.ServerOption{
   118  		//lint:ignore SA1019 explicit use of customCodec, CustomCodec API is available throughout 1.x of grpc-go
   119  		grpc.CustomCodec(customCodec{}),
   120  		grpc.UnknownServiceHandler(handler.handle),
   121  		grpc.MaxRecvMsgSize(i.t.options.serverMaxRecvMsgSize),
   122  		grpc.MaxSendMsgSize(i.t.options.serverMaxSendMsgSize),
   123  	}
   124  
   125  	listener := i.listener
   126  
   127  	if i.options.creds != nil {
   128  		serverOptions = append(serverOptions, grpc.Creds(i.options.creds))
   129  	} else if i.options.tlsMode != yarpctls.Disabled {
   130  		if i.options.tlsConfig == nil {
   131  			return errors.New("gRPC TLS enabled but configuration not provided")
   132  		}
   133  
   134  		listener = muxlistener.NewListener(muxlistener.Config{
   135  			Listener:      listener,
   136  			TLSConfig:     i.options.tlsConfig.Clone(),
   137  			Logger:        i.t.options.logger,
   138  			Meter:         i.t.options.meter,
   139  			ServiceName:   i.t.options.serviceName,
   140  			TransportName: TransportName,
   141  			Mode:          i.options.tlsMode,
   142  		})
   143  	}
   144  
   145  	if i.t.options.serverMaxHeaderListSize != nil {
   146  		serverOptions = append(serverOptions, grpc.MaxHeaderListSize(*i.t.options.serverMaxHeaderListSize))
   147  	}
   148  
   149  	server := grpc.NewServer(serverOptions...)
   150  
   151  	go func() {
   152  		i.t.options.logger.Info("started GRPC inbound", zap.Stringer("address", i.listener.Addr()))
   153  		if len(i.router.Procedures()) == 0 {
   154  			i.t.options.logger.Warn("no procedures specified for GRPC inbound")
   155  		}
   156  		// TODO there should be some mechanism to block here
   157  		// there is a race because the listener gets set in the grpc
   158  		// Server implementation and we should be able to block
   159  		// until Serve initialization is done
   160  		//
   161  		// It would be even better if we could do this outside the
   162  		// lock in i
   163  		//
   164  		// TODO Server always returns a non-nil error but should
   165  		// we do something with some or all errors?
   166  		_ = server.Serve(listener)
   167  	}()
   168  	i.server = server
   169  	return nil
   170  }
   171  
   172  func (i *Inbound) stop() error {
   173  	i.lock.Lock()
   174  	defer i.lock.Unlock()
   175  	if i.server != nil {
   176  		i.server.GracefulStop()
   177  	}
   178  	i.server = nil
   179  	return nil
   180  }
   181  
   182  // Introspect returns the current state of the inbound.
   183  func (i *Inbound) Introspect() introspection.InboundStatus {
   184  	state := "Stopped"
   185  	if i.IsRunning() {
   186  		state = "Started"
   187  	}
   188  	var addrString string
   189  	if addr := i.Addr(); addr != nil {
   190  		addrString = addr.String()
   191  	}
   192  	return introspection.InboundStatus{
   193  		Transport: TransportName,
   194  		Endpoint:  addrString,
   195  		State:     state,
   196  	}
   197  }