go.uber.org/yarpc@v1.72.1/transport/tchannel/config.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 tchannel 22 23 import ( 24 "errors" 25 "fmt" 26 "time" 27 28 "go.uber.org/yarpc/api/peer" 29 "go.uber.org/yarpc/api/transport" 30 yarpctls "go.uber.org/yarpc/api/transport/tls" 31 "go.uber.org/yarpc/peer/hostport" 32 "go.uber.org/yarpc/yarpcconfig" 33 ) 34 35 // TransportConfig configures a shared TChannel transport. This is shared 36 // between all TChannel outbounds and inbounds of a Dispatcher. 37 // 38 // transports: 39 // tchannel: 40 // connTimeout: 500ms 41 // connBackoff: 42 // exponential: 43 // first: 10ms 44 // max: 30s 45 type TransportConfig struct { 46 ConnTimeout time.Duration `config:"connTimeout"` 47 ConnBackoff yarpcconfig.Backoff `config:"connBackoff"` 48 } 49 50 // InboundConfig configures a TChannel inbound. 51 // 52 // inbounds: 53 // tchannel: 54 // address: :4040 55 // tls: 56 // mode: permissive 57 // 58 // At most one TChannel inbound may be defined in a single YARPC service. 59 type InboundConfig struct { 60 // Address to listen on. Defaults to ":0" (all network interfaces and a 61 // random OS-assigned port). 62 Address string `config:"address,interpolate"` 63 // TLS configuration of the inbound. 64 TLS InboundTLSConfig `config:"tls"` 65 } 66 67 // InboundTLSConfig specifies the TLS configuration of the tchannel inbound. 68 type InboundTLSConfig struct { 69 // Mode when set to Permissive or Enforced enables TLS inbound and 70 // TLS configuration must be passed as an inbound option. 71 Mode yarpctls.Mode `config:"mode,interpolate"` 72 } 73 74 // OutboundConfig configures a TChannel outbound. 75 // 76 // outbounds: 77 // myservice: 78 // tchannel: 79 // peer: 127.0.0.1:4040 80 // 81 type OutboundConfig struct { 82 yarpcconfig.PeerChooser 83 // TLS config enables TLS outbound. 84 // 85 // tchannel: 86 // peer: 127.0.0.1:4040 87 // tls: 88 // mode: enforced 89 // spiffe-ids: 90 // - destination-id 91 TLS OutboundTLSConfig `config:"tls"` 92 93 // EnableBufferReuse config controls usage of a buffer pool 94 // to read the response body. 95 EnableBufferReuse bool `config:"enable-buffer-reuse"` 96 } 97 98 // OutboundTLSConfig configures TLS for a TChannel outbound. 99 type OutboundTLSConfig struct { 100 // Mode when set to Enforced enables TLS outbound and 101 // outbound TLS configuration provider will be used for fetching tls.Config. 102 Mode yarpctls.Mode `config:"mode,interpolate"` 103 // SpiffeIDs is a list of the accepted server spiffe IDs. 104 SpiffeIDs []string `config:"spiffe-ids"` 105 } 106 107 // getPeerTransport returns peer transport to be used in peer chooser creation. 108 func (c OutboundConfig) getPeerTransport(transport *Transport, destName string) (peer.Transport, error) { 109 if c.TLS.Mode == yarpctls.Disabled { 110 return transport, nil 111 } 112 113 if c.TLS.Mode == yarpctls.Permissive { 114 return nil, errors.New("outbound does not support permissive TLS mode") 115 } 116 117 if transport.outboundTLSConfigProvider == nil { 118 return nil, errors.New("outbound TLS enforced but outbound TLS config provider is nil") 119 } 120 121 config, err := transport.outboundTLSConfigProvider.ClientTLSConfig(c.TLS.SpiffeIDs) 122 if err != nil { 123 return nil, err 124 } 125 126 return transport.CreateTLSOutboundChannel(config, destName) 127 } 128 129 // TransportSpec returns a TransportSpec for the TChannel unary transport. 130 func TransportSpec(opts ...Option) yarpcconfig.TransportSpec { 131 var ts transportSpec 132 for _, o := range opts { 133 switch opt := o.(type) { 134 case TransportOption: 135 ts.transportOptions = append(ts.transportOptions, opt) 136 default: 137 panic(fmt.Sprintf("unknown option of type %T: %v", o, o)) 138 } 139 } 140 return ts.Spec() 141 } 142 143 // transportSpec holds the configurable parts of the TChannel TransportSpec. 144 // 145 // These are usually runtime dependencies that cannot be parsed from 146 // configuration. 147 type transportSpec struct { 148 transportOptions []TransportOption 149 } 150 151 func (ts *transportSpec) Spec() yarpcconfig.TransportSpec { 152 return yarpcconfig.TransportSpec{ 153 Name: TransportName, 154 BuildTransport: ts.buildTransport, 155 BuildInbound: ts.buildInbound, 156 BuildUnaryOutbound: ts.buildUnaryOutbound, 157 } 158 } 159 160 func (ts *transportSpec) buildTransport(tc *TransportConfig, k *yarpcconfig.Kit) (transport.Transport, error) { 161 options := newTransportOptions() 162 163 for _, opt := range ts.transportOptions { 164 opt(&options) 165 } 166 167 if tc.ConnTimeout != 0 { 168 options.connTimeout = tc.ConnTimeout 169 } 170 171 strategy, err := tc.ConnBackoff.Strategy() 172 if err != nil { 173 return nil, err 174 } 175 options.connBackoffStrategy = strategy 176 177 if options.name != "" { 178 return nil, fmt.Errorf("TChannel TransportSpec does not accept ServiceName") 179 } 180 181 if options.addr != "" { 182 return nil, fmt.Errorf("TChannel TransportSpec does not accept ListenAddr") 183 } 184 185 if options.ch != nil { 186 return nil, fmt.Errorf("TChannel TransportSpec does not accept WithChannel") 187 } 188 189 options.name = k.ServiceName() 190 return options.newTransport(), nil 191 } 192 193 func (ts *transportSpec) buildInbound(c *InboundConfig, t transport.Transport, k *yarpcconfig.Kit) (transport.Inbound, error) { 194 if c.Address == "" { 195 return nil, fmt.Errorf("inbound address is required") 196 } 197 198 trans := t.(*Transport) 199 if trans.addr != "" { 200 // We ensure that trans.addr is empty when buildTransport is called, 201 // so if the string is non-empty right now, another TChannel inbound 202 // already filled it with a value. 203 return nil, fmt.Errorf("at most one TChannel inbound may be specified") 204 } 205 206 trans.addr = c.Address 207 // Override inbound TLS mode when not set by an option. 208 if trans.inboundTLSMode == nil { 209 trans.inboundTLSMode = &c.TLS.Mode 210 } 211 return trans.NewInbound(), nil 212 } 213 214 func (ts *transportSpec) buildUnaryOutbound(oc *OutboundConfig, t transport.Transport, k *yarpcconfig.Kit) (transport.UnaryOutbound, error) { 215 x := t.(*Transport) 216 pt, err := oc.getPeerTransport(x, k.OutboundServiceName()) 217 if err != nil { 218 return nil, err 219 } 220 chooser, err := oc.BuildPeerChooser(pt, hostport.Identify, k) 221 if err != nil { 222 return nil, err 223 } 224 return x.NewOutbound(chooser, WithReuseBuffer(oc.EnableBufferReuse)), nil 225 }