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 }