go.uber.org/yarpc@v1.72.1/api/middleware/outbound.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 middleware
    22  
    23  import (
    24  	"context"
    25  
    26  	"go.uber.org/yarpc/api/transport"
    27  	"go.uber.org/yarpc/api/x/introspection"
    28  )
    29  
    30  var (
    31  	_ transport.Namer = (*unaryOutboundWithMiddleware)(nil)
    32  	_ transport.Namer = (*onewayOutboundWithMiddleware)(nil)
    33  	_ transport.Namer = (*streamOutboundWithMiddleware)(nil)
    34  )
    35  
    36  // UnaryOutbound defines transport-level middleware for
    37  // `UnaryOutbound`s.
    38  //
    39  // UnaryOutbound middleware MAY do zero or more of the following: change the
    40  // context, change the request, change the returned response, handle the
    41  // returned error, call the given outbound zero or more times.
    42  //
    43  // UnaryOutbound middleware MUST always return a non-nil Response or error,
    44  // and they MUST be thread-safe
    45  //
    46  // UnaryOutbound middleware is re-used across requests and MAY be called
    47  // multiple times on the same request.
    48  type UnaryOutbound interface {
    49  	Call(ctx context.Context, request *transport.Request, out transport.UnaryOutbound) (*transport.Response, error)
    50  }
    51  
    52  // NopUnaryOutbound is a unary outbound middleware that does not do
    53  // anything special. It simply calls the underlying UnaryOutbound.
    54  var NopUnaryOutbound UnaryOutbound = nopUnaryOutbound{}
    55  
    56  // ApplyUnaryOutbound applies the given UnaryOutbound middleware to
    57  // the given UnaryOutbound transport.
    58  func ApplyUnaryOutbound(o transport.UnaryOutbound, f UnaryOutbound) transport.UnaryOutbound {
    59  	if f == nil {
    60  		return o
    61  	}
    62  
    63  	var name string
    64  	if namer, ok := o.(transport.Namer); ok {
    65  		name = namer.TransportName()
    66  	}
    67  
    68  	return unaryOutboundWithMiddleware{o: o, f: f, name: name}
    69  }
    70  
    71  // UnaryOutboundFunc adapts a function into a UnaryOutbound middleware.
    72  type UnaryOutboundFunc func(context.Context, *transport.Request, transport.UnaryOutbound) (*transport.Response, error)
    73  
    74  // Call for UnaryOutboundFunc.
    75  func (f UnaryOutboundFunc) Call(ctx context.Context, request *transport.Request, out transport.UnaryOutbound) (*transport.Response, error) {
    76  	return f(ctx, request, out)
    77  }
    78  
    79  type unaryOutboundWithMiddleware struct {
    80  	name string
    81  	o    transport.UnaryOutbound
    82  	f    UnaryOutbound
    83  }
    84  
    85  func (fo unaryOutboundWithMiddleware) TransportName() string {
    86  	return fo.name
    87  }
    88  
    89  func (fo unaryOutboundWithMiddleware) Transports() []transport.Transport {
    90  	return fo.o.Transports()
    91  }
    92  
    93  func (fo unaryOutboundWithMiddleware) Start() error {
    94  	return fo.o.Start()
    95  }
    96  
    97  func (fo unaryOutboundWithMiddleware) Stop() error {
    98  	return fo.o.Stop()
    99  }
   100  
   101  func (fo unaryOutboundWithMiddleware) IsRunning() bool {
   102  	return fo.o.IsRunning()
   103  }
   104  
   105  func (fo unaryOutboundWithMiddleware) Introspect() introspection.OutboundStatus {
   106  	if o, ok := fo.o.(introspection.IntrospectableOutbound); ok {
   107  		return o.Introspect()
   108  	}
   109  	return introspection.OutboundStatusNotSupported
   110  }
   111  
   112  func (fo unaryOutboundWithMiddleware) Call(ctx context.Context, request *transport.Request) (*transport.Response, error) {
   113  	return fo.f.Call(ctx, request, fo.o)
   114  }
   115  
   116  type nopUnaryOutbound struct{}
   117  
   118  func (nopUnaryOutbound) Call(ctx context.Context, request *transport.Request, out transport.UnaryOutbound) (*transport.Response, error) {
   119  	return out.Call(ctx, request)
   120  }
   121  
   122  // OnewayOutbound defines transport-level middleware for `OnewayOutbound`s.
   123  //
   124  // OnewayOutbound middleware MAY do zero or more of the following: change the
   125  // context, change the request, change the returned ack, handle the returned
   126  // error, call the given outbound zero or more times.
   127  //
   128  // OnewayOutbound middleware MUST always return an Ack (nil or not) or an
   129  // error, and they MUST be thread-safe.
   130  //
   131  // OnewayOutbound middleware is re-used across requests and MAY be called
   132  // multiple times on the same request.
   133  type OnewayOutbound interface {
   134  	CallOneway(ctx context.Context, request *transport.Request, out transport.OnewayOutbound) (transport.Ack, error)
   135  }
   136  
   137  // NopOnewayOutbound is a oneway outbound middleware that does not do
   138  // anything special. It simply calls the underlying OnewayOutbound transport.
   139  var NopOnewayOutbound OnewayOutbound = nopOnewayOutbound{}
   140  
   141  // ApplyOnewayOutbound applies the given OnewayOutbound middleware to
   142  // the given OnewayOutbound transport.
   143  func ApplyOnewayOutbound(o transport.OnewayOutbound, f OnewayOutbound) transport.OnewayOutbound {
   144  	if f == nil {
   145  		return o
   146  	}
   147  
   148  	var name string
   149  	if namer, ok := o.(transport.Namer); ok {
   150  		name = namer.TransportName()
   151  	}
   152  
   153  	return onewayOutboundWithMiddleware{o: o, f: f, name: name}
   154  }
   155  
   156  // OnewayOutboundFunc adapts a function into a OnewayOutbound middleware.
   157  type OnewayOutboundFunc func(context.Context, *transport.Request, transport.OnewayOutbound) (transport.Ack, error)
   158  
   159  // CallOneway for OnewayOutboundFunc.
   160  func (f OnewayOutboundFunc) CallOneway(ctx context.Context, request *transport.Request, out transport.OnewayOutbound) (transport.Ack, error) {
   161  	return f(ctx, request, out)
   162  }
   163  
   164  type onewayOutboundWithMiddleware struct {
   165  	name string
   166  	o    transport.OnewayOutbound
   167  	f    OnewayOutbound
   168  }
   169  
   170  func (fo onewayOutboundWithMiddleware) TransportName() string {
   171  	return fo.name
   172  }
   173  
   174  func (fo onewayOutboundWithMiddleware) Transports() []transport.Transport {
   175  	return fo.o.Transports()
   176  }
   177  
   178  func (fo onewayOutboundWithMiddleware) Start() error {
   179  	return fo.o.Start()
   180  }
   181  
   182  func (fo onewayOutboundWithMiddleware) Stop() error {
   183  	return fo.o.Stop()
   184  }
   185  
   186  func (fo onewayOutboundWithMiddleware) IsRunning() bool {
   187  	return fo.o.IsRunning()
   188  }
   189  
   190  func (fo onewayOutboundWithMiddleware) CallOneway(ctx context.Context, request *transport.Request) (transport.Ack, error) {
   191  	return fo.f.CallOneway(ctx, request, fo.o)
   192  }
   193  
   194  func (fo onewayOutboundWithMiddleware) Introspect() introspection.OutboundStatus {
   195  	if o, ok := fo.o.(introspection.IntrospectableOutbound); ok {
   196  		return o.Introspect()
   197  	}
   198  	return introspection.OutboundStatusNotSupported
   199  }
   200  
   201  type nopOnewayOutbound struct{}
   202  
   203  func (nopOnewayOutbound) CallOneway(ctx context.Context, request *transport.Request, out transport.OnewayOutbound) (transport.Ack, error) {
   204  	return out.CallOneway(ctx, request)
   205  }
   206  
   207  // StreamOutbound defines transport-level middleware for
   208  // `StreamOutbound`s.
   209  //
   210  // StreamOutbound middleware MAY do zero or more of the following: change the
   211  // context, change the requestMeta, change the returned Stream, handle the
   212  // returned error, call the given outbound zero or more times.
   213  //
   214  // StreamOutbound middleware MUST always return a non-nil Stream or error,
   215  // and they MUST be thread-safe
   216  //
   217  // StreamOutbound middleware is re-used across requests and MAY be called
   218  // multiple times on the same request.
   219  type StreamOutbound interface {
   220  	CallStream(ctx context.Context, request *transport.StreamRequest, out transport.StreamOutbound) (*transport.ClientStream, error)
   221  }
   222  
   223  // NopStreamOutbound is a stream outbound middleware that does not do
   224  // anything special. It simply calls the underlying StreamOutbound.
   225  var NopStreamOutbound StreamOutbound = nopStreamOutbound{}
   226  
   227  // ApplyStreamOutbound applies the given StreamOutbound middleware to
   228  // the given StreamOutbound transport.
   229  func ApplyStreamOutbound(o transport.StreamOutbound, f StreamOutbound) transport.StreamOutbound {
   230  	if f == nil {
   231  		return o
   232  	}
   233  
   234  	var name string
   235  	if namer, ok := o.(transport.Namer); ok {
   236  		name = namer.TransportName()
   237  	}
   238  
   239  	return streamOutboundWithMiddleware{o: o, f: f, name: name}
   240  }
   241  
   242  // StreamOutboundFunc adapts a function into a StreamOutbound middleware.
   243  type StreamOutboundFunc func(context.Context, *transport.StreamRequest, transport.StreamOutbound) (*transport.ClientStream, error)
   244  
   245  // CallStream for StreamOutboundFunc.
   246  func (f StreamOutboundFunc) CallStream(ctx context.Context, request *transport.StreamRequest, out transport.StreamOutbound) (*transport.ClientStream, error) {
   247  	return f(ctx, request, out)
   248  }
   249  
   250  type streamOutboundWithMiddleware struct {
   251  	name string
   252  	o    transport.StreamOutbound
   253  	f    StreamOutbound
   254  }
   255  
   256  func (fo streamOutboundWithMiddleware) TransportName() string {
   257  	return fo.name
   258  }
   259  
   260  func (fo streamOutboundWithMiddleware) Transports() []transport.Transport {
   261  	return fo.o.Transports()
   262  }
   263  
   264  func (fo streamOutboundWithMiddleware) Start() error {
   265  	return fo.o.Start()
   266  }
   267  
   268  func (fo streamOutboundWithMiddleware) Stop() error {
   269  	return fo.o.Stop()
   270  }
   271  
   272  func (fo streamOutboundWithMiddleware) IsRunning() bool {
   273  	return fo.o.IsRunning()
   274  }
   275  
   276  func (fo streamOutboundWithMiddleware) CallStream(ctx context.Context, request *transport.StreamRequest) (*transport.ClientStream, error) {
   277  	return fo.f.CallStream(ctx, request, fo.o)
   278  }
   279  
   280  type nopStreamOutbound struct{}
   281  
   282  func (nopStreamOutbound) CallStream(ctx context.Context, request *transport.StreamRequest, out transport.StreamOutbound) (*transport.ClientStream, error) {
   283  	return out.CallStream(ctx, request)
   284  }