go.uber.org/yarpc@v1.72.1/api/encoding/inbound_call.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 encoding
    22  
    23  import (
    24  	"context"
    25  
    26  	"go.uber.org/yarpc/api/transport"
    27  	"go.uber.org/yarpc/internal/inboundcall"
    28  	"go.uber.org/yarpc/yarpcerrors"
    29  )
    30  
    31  // InboundCall holds information about the inbound call and its response.
    32  //
    33  // Encoding authors may use InboundCall to provide information about the
    34  // incoming request on the Context and send response headers through
    35  // WriteResponseHeader.
    36  type InboundCall struct {
    37  	resHeaders             []keyValuePair
    38  	req                    *transport.Request
    39  	disableResponseHeaders bool
    40  }
    41  
    42  // NewInboundCall builds a new InboundCall with the given context.
    43  //
    44  // A request context is returned and must be used in place of the original.
    45  func NewInboundCall(ctx context.Context) (context.Context, *InboundCall) {
    46  	return NewInboundCallWithOptions(ctx)
    47  }
    48  
    49  // InboundCallOption is an option for configuring an InboundCall.
    50  type InboundCallOption interface {
    51  	apply(call *InboundCall)
    52  }
    53  
    54  type inboundCallOptionFunc func(*InboundCall)
    55  
    56  func (i inboundCallOptionFunc) apply(call *InboundCall) { i(call) }
    57  
    58  // DisableResponseHeaders disables response headers for inbound calls.
    59  func DisableResponseHeaders() InboundCallOption {
    60  	return inboundCallOptionFunc(func(call *InboundCall) {
    61  		call.disableResponseHeaders = true
    62  	})
    63  }
    64  
    65  // NewInboundCallWithOptions builds a new InboundCall with the given context and
    66  // options.
    67  //
    68  // A request context is returned and must be used in place of the original.
    69  func NewInboundCallWithOptions(ctx context.Context, opts ...InboundCallOption) (context.Context, *InboundCall) {
    70  	call := &InboundCall{}
    71  	for _, opt := range opts {
    72  		opt.apply(call)
    73  	}
    74  	return inboundcall.WithMetadata(ctx, (*inboundCallMetadata)(call)), call
    75  }
    76  
    77  // ReadFromRequest reads information from the given request.
    78  //
    79  // This information may be queried on the context using functions like Caller,
    80  // Service, Procedure, etc.
    81  func (ic *InboundCall) ReadFromRequest(req *transport.Request) error {
    82  	// TODO(abg): Maybe we should copy attributes over so that changes to the
    83  	// Request don't change the output.
    84  	ic.req = req
    85  	return nil
    86  }
    87  
    88  // ReadFromRequestMeta reads information from the given request.
    89  //
    90  // This information may be queried on the context using functions like Caller,
    91  // Service, Procedure, etc.
    92  func (ic *InboundCall) ReadFromRequestMeta(reqMeta *transport.RequestMeta) error {
    93  	ic.req = reqMeta.ToRequest()
    94  	return nil
    95  }
    96  
    97  // WriteToResponse writes response information from the InboundCall onto the
    98  // given ResponseWriter.
    99  //
   100  // If used, this must be called before writing the response body to the
   101  // ResponseWriter.
   102  func (ic *InboundCall) WriteToResponse(resw transport.ResponseWriter) error {
   103  	var headers transport.Headers
   104  	for _, h := range ic.resHeaders {
   105  		headers = headers.With(h.k, h.v)
   106  	}
   107  
   108  	if headers.Len() > 0 {
   109  		resw.AddHeaders(headers)
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  // inboundCallMetadata wraps an InboundCall to implement inboundcall.Metadata.
   116  type inboundCallMetadata InboundCall
   117  
   118  var _ inboundcall.Metadata = (*inboundCallMetadata)(nil)
   119  
   120  func (ic *inboundCallMetadata) Caller() string {
   121  	return ic.req.Caller
   122  }
   123  
   124  func (ic *inboundCallMetadata) Service() string {
   125  	return ic.req.Service
   126  }
   127  
   128  func (ic *inboundCallMetadata) Transport() string {
   129  	return ic.req.Transport
   130  }
   131  
   132  func (ic *inboundCallMetadata) Procedure() string {
   133  	return ic.req.Procedure
   134  }
   135  
   136  func (ic *inboundCallMetadata) Encoding() transport.Encoding {
   137  	return ic.req.Encoding
   138  }
   139  
   140  func (ic *inboundCallMetadata) Headers() transport.Headers {
   141  	return ic.req.Headers
   142  }
   143  
   144  func (ic *inboundCallMetadata) ShardKey() string {
   145  	return ic.req.ShardKey
   146  }
   147  
   148  func (ic *inboundCallMetadata) RoutingKey() string {
   149  	return ic.req.RoutingKey
   150  }
   151  
   152  func (ic *inboundCallMetadata) RoutingDelegate() string {
   153  	return ic.req.RoutingDelegate
   154  }
   155  
   156  func (ic *inboundCallMetadata) CallerProcedure() string {
   157  	return ic.req.CallerProcedure
   158  }
   159  
   160  func (ic *inboundCallMetadata) WriteResponseHeader(k, v string) error {
   161  	if ic.disableResponseHeaders {
   162  		return yarpcerrors.InvalidArgumentErrorf("call does not support setting response headers")
   163  	}
   164  	ic.resHeaders = append(ic.resHeaders, keyValuePair{k: k, v: v})
   165  	return nil
   166  }