go.uber.org/yarpc@v1.72.1/encoding/thrift/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 thrift 22 23 import ( 24 "bytes" 25 "context" 26 "io" 27 28 "go.uber.org/thriftrw/protocol" 29 "go.uber.org/thriftrw/protocol/envelope" 30 "go.uber.org/thriftrw/wire" 31 encodingapi "go.uber.org/yarpc/api/encoding" 32 "go.uber.org/yarpc/api/transport" 33 "go.uber.org/yarpc/internal/bufferpool" 34 "go.uber.org/yarpc/pkg/errors" 35 ) 36 37 // thriftUnaryHandler wraps a Thrift Handler into a transport.UnaryHandler 38 type thriftUnaryHandler struct { 39 UnaryHandler UnaryHandler 40 Protocol protocol.Protocol 41 Enveloping bool 42 } 43 44 // thriftOnewayHandler wraps a Thrift Handler into a transport.OnewayHandler 45 type thriftOnewayHandler struct { 46 OnewayHandler OnewayHandler 47 Protocol protocol.Protocol 48 Enveloping bool 49 } 50 51 func (t thriftUnaryHandler) Handle(ctx context.Context, treq *transport.Request, rw transport.ResponseWriter) error { 52 ctx, call := encodingapi.NewInboundCall(ctx) 53 54 bodyReader, release, err := getReaderAt(treq.Body) 55 if err != nil { 56 return err 57 } 58 defer release() 59 60 reqValue, responder, err := decodeRequest(call, treq, bodyReader, wire.Call, t.Protocol, t.Enveloping) 61 if err != nil { 62 return err 63 } 64 65 res, err := t.UnaryHandler(ctx, reqValue) 66 if err != nil { 67 return err 68 } 69 70 if resType := res.Body.EnvelopeType(); resType != wire.Reply { 71 return errors.ResponseBodyEncodeError( 72 treq, errUnexpectedEnvelopeType(resType)) 73 } 74 75 value, err := res.Body.ToWire() 76 if err != nil { 77 return err 78 } 79 80 if res.IsApplicationError { 81 rw.SetApplicationError() 82 if applicationErrorMetaSetter, ok := rw.(transport.ApplicationErrorMetaSetter); ok { 83 applicationErrorMetaSetter.SetApplicationErrorMeta(&transport.ApplicationErrorMeta{ 84 Details: res.ApplicationErrorDetails, 85 Name: res.ApplicationErrorName, 86 Code: res.ApplicationErrorCode, 87 }) 88 } 89 } 90 91 if err := call.WriteToResponse(rw); err != nil { 92 // not reachable 93 return err 94 } 95 96 if err = responder.EncodeResponse(value, wire.Reply, rw); err != nil { 97 return errors.ResponseBodyEncodeError(treq, err) 98 } 99 100 return nil 101 } 102 103 func (t thriftOnewayHandler) HandleOneway(ctx context.Context, treq *transport.Request) error { 104 bodyReader, release, err := getReaderAt(treq.Body) 105 if err != nil { 106 return err 107 } 108 defer release() 109 110 ctx, call := encodingapi.NewInboundCall(ctx) 111 112 reqValue, _, err := decodeRequest(call, treq, bodyReader, wire.OneWay, t.Protocol, t.Enveloping) 113 if err != nil { 114 return err 115 } 116 117 return t.OnewayHandler(ctx, reqValue) 118 } 119 120 // decodeRequest is a utility shared by Unary and Oneway handlers, to decode 121 // the request, regardless of enveloping. 122 func decodeRequest( 123 // call is an inboundCall populated from the transport request and context. 124 call *encodingapi.InboundCall, 125 treq *transport.Request, 126 reader io.ReaderAt, 127 // reqEnvelopeType indicates the expected envelope type, if an envelope is 128 // present. 129 reqEnvelopeType wire.EnvelopeType, 130 // proto is the encoding protocol (e.g., Binary) or an 131 // EnvelopeAgnosticProtocol (e.g., EnvelopeAgnosticBinary) 132 proto protocol.Protocol, 133 // enveloping indicates that requests must be enveloped, used only if the 134 // protocol is not envelope agnostic. 135 enveloping bool, 136 ) ( 137 // the wire representation of the decoded request. 138 // decodeRequest does not surface the envelope. 139 wire.Value, 140 // how to encode the response, with the enveloping 141 // strategy corresponding to the request. It is not used for oneway handlers. 142 envelope.Responder, 143 error, 144 ) { 145 if err := errors.ExpectEncodings(treq, Encoding); err != nil { 146 return wire.Value{}, nil, err 147 } 148 149 if err := call.ReadFromRequest(treq); err != nil { 150 // not reachable 151 return wire.Value{}, nil, err 152 } 153 154 // Discover or choose the appropriate envelope 155 if agnosticProto, ok := proto.(protocol.EnvelopeAgnosticProtocol); ok { 156 return agnosticProto.DecodeRequest(reqEnvelopeType, reader) 157 } 158 if enveloping { 159 return decodeEnvelopedRequest(treq, reqEnvelopeType, proto, reader) 160 } 161 return decodeUnenvelopedRequest(proto, reader) 162 } 163 164 func decodeEnvelopedRequest( 165 treq *transport.Request, 166 reqEnvelopeType wire.EnvelopeType, 167 proto protocol.Protocol, 168 reader io.ReaderAt, 169 ) (wire.Value, envelope.Responder, error) { 170 var envelope wire.Envelope 171 envelope, err := proto.DecodeEnveloped(reader) 172 if err != nil { 173 return wire.Value{}, nil, err 174 } 175 if envelope.Type != reqEnvelopeType { 176 err := errors.RequestBodyDecodeError(treq, errUnexpectedEnvelopeType(envelope.Type)) 177 return wire.Value{}, nil, err 178 } 179 reqValue := envelope.Value 180 //lint:ignore SA1019 explicit use for known enveloping 181 responder := protocol.EnvelopeV1Responder{Name: envelope.Name, SeqID: envelope.SeqID} 182 return reqValue, responder, nil 183 } 184 185 func decodeUnenvelopedRequest( 186 proto protocol.Protocol, 187 reader io.ReaderAt, 188 ) (wire.Value, envelope.Responder, error) { 189 reqValue, err := proto.Decode(reader, wire.TStruct) 190 if err != nil { 191 return wire.Value{}, nil, err 192 } 193 //lint:ignore SA1019 explicit use for known enveloping 194 responder := protocol.NoEnvelopeResponder 195 return reqValue, responder, err 196 } 197 198 // closeReader calls Close is r implements io.Closer, does nothing otherwise. 199 func closeReader(r io.Reader) error { 200 closer, ok := r.(io.Closer) 201 if !ok { 202 return nil 203 } 204 205 return closer.Close() 206 } 207 208 // getReaderAt returns an io.ReaderAt compatible reader 209 // If the body is already readerAt compatible then reuse the same which 210 // avoids redundant copy. If not, it creates readerAt using bufferpool which 211 // must be released by caller after it has finished handling the request as 212 // thrift requests read sets, maps, and lists lazilly. 213 // This is mainly done as tchannel transport handler decodes the body into 214 // a io.ReaderAt compatible instance which gets resued here 215 func getReaderAt(body io.Reader) (reader io.ReaderAt, release func(), err error) { 216 release = func() {} 217 if readerBody, ok := body.(io.ReaderAt); ok { 218 reader = readerBody 219 return 220 } 221 222 buf := bufferpool.Get() 223 if _, err = buf.ReadFrom(body); err != nil { 224 return 225 } 226 if err = closeReader(body); err != nil { 227 return 228 } 229 reader = bytes.NewReader(buf.Bytes()) 230 release = func() { bufferpool.Put(buf) } 231 return 232 }