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  }