github.com/renbou/grpcbridge@v0.0.2-0.20240416012907-bcbd8b12648a/bridge.go (about)

     1  package grpcbridge
     2  
     3  import (
     4  	"log/slog"
     5  	"net/http"
     6  
     7  	"github.com/renbou/grpcbridge/bridgelog"
     8  	"github.com/renbou/grpcbridge/routing"
     9  	"github.com/renbou/grpcbridge/transcoding"
    10  	"github.com/renbou/grpcbridge/webbridge"
    11  )
    12  
    13  var _ = slog.Logger{}
    14  
    15  // Option configures common grpcbridge options, such as the logger.
    16  type Option interface {
    17  	RouterOption
    18  	ProxyOption
    19  	BridgeOption
    20  }
    21  
    22  // Router unifies the [routing.GRPCRouter] and [routing.HTTPRouter] interfaces,
    23  // providing routing support for both [GRPCProxy] and [WebBridge] for all kinds of incoming calls.
    24  // It is implemented by [ReflectionRouter], which is the default routing component used by grpcbridge itself.
    25  type Router interface {
    26  	routing.GRPCRouter
    27  	routing.HTTPRouter
    28  }
    29  
    30  // BridgeOption configures the various bridging handlers used by [WebBridge].
    31  type BridgeOption interface {
    32  	applyBridge(o *bridgeOptions)
    33  }
    34  
    35  // WebBridge provides a single entrypoint for all web-originating requests which are bridged to target gRPC services with various applied transformations.
    36  type WebBridge struct {
    37  	transcodedHTTPBridge *webbridge.TranscodedHTTPBridge
    38  }
    39  
    40  // NewWebBridge constructs a new [*WebBridge] with the given router and options.
    41  // When no options are provided, the [transcoding.StandardTranscoder] and the
    42  // underlying bridge handlers from [webbridge] will be initialized with their default options.
    43  func NewWebBridge(router Router, opts ...BridgeOption) *WebBridge {
    44  	options := defaultBridgeOptions()
    45  
    46  	for _, opt := range opts {
    47  		opt.applyBridge(&options)
    48  	}
    49  
    50  	transcoder := transcoding.NewStandardTranscoder(options.transcoderOpts)
    51  	transcodedHTTPBridge := webbridge.NewTranscodedHTTPBridge(router, transcoder, webbridge.TranscodedHTTPBridgeOpts{Logger: options.common.logger})
    52  
    53  	return &WebBridge{transcodedHTTPBridge: transcodedHTTPBridge}
    54  }
    55  
    56  // ServeHTTP implements [net/http.Handler] and routes the request to the appropriate bridging handler according to these rules:
    57  //  1. All requests are currently handled by [webbridge.TranscodedHTTPBridge].
    58  func (b *WebBridge) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    59  	b.transcodedHTTPBridge.ServeHTTP(w, r)
    60  }
    61  
    62  // WithMarshalers allows using custom marshalers for transcoding-based handlers,
    63  // which will be picked according to the content types they support.
    64  // By default, [transcoding.DefaultJSONMarshaler] is used.
    65  func WithMarshalers(marshalers []transcoding.Marshaler) BridgeOption {
    66  	return newFuncBridgeOption(func(o *bridgeOptions) {
    67  		o.transcoderOpts.Marshalers = marshalers
    68  	})
    69  }
    70  
    71  // WithDefaultMarshaler allows setting a custom default marshaler for transcoding-based handlers,
    72  // which will be used for requests which do not specify the Content-Type header.
    73  // By default, [transcoding.DefaultJSONMarshaler] is used.
    74  func WithDefaultMarshaler(m transcoding.Marshaler) BridgeOption {
    75  	return newFuncBridgeOption(func(o *bridgeOptions) {
    76  		o.transcoderOpts.DefaultMarshaler = m
    77  	})
    78  }
    79  
    80  // WithLogger configures the logger to be used by grpcbridge components. By default all logs are discarded.
    81  //
    82  // Taking the full Logger interface allows you to configure all functionality however you want,
    83  // however you can also use [bridgelog.WrapPlainLogger] to wrap a basic logger such as [slog.Logger].
    84  func WithLogger(logger bridgelog.Logger) Option {
    85  	return newFuncOption(func(o *options) {
    86  		o.logger = logger
    87  	})
    88  }
    89  
    90  type options struct {
    91  	logger bridgelog.Logger
    92  }
    93  
    94  func defaultOptions() options {
    95  	return options{logger: bridgelog.Discard()}
    96  }
    97  
    98  type funcOption struct {
    99  	f func(*options)
   100  }
   101  
   102  func (f *funcOption) applyRouter(o *routerOptions) {
   103  	f.f(&o.common)
   104  }
   105  
   106  func (f *funcOption) applyProxy(o *proxyOptions) {
   107  	f.f(&o.common)
   108  }
   109  
   110  func (f *funcOption) applyBridge(o *bridgeOptions) {
   111  	f.f(&o.common)
   112  }
   113  
   114  func newFuncOption(f func(*options)) Option {
   115  	return &funcOption{f: f}
   116  }
   117  
   118  type bridgeOptions struct {
   119  	common         options
   120  	transcoderOpts transcoding.StandardTranscoderOpts
   121  }
   122  
   123  func defaultBridgeOptions() bridgeOptions {
   124  	return bridgeOptions{common: defaultOptions()}
   125  }
   126  
   127  type funcBridgeOption struct {
   128  	f func(*bridgeOptions)
   129  }
   130  
   131  func (f *funcBridgeOption) applyBridge(o *bridgeOptions) {
   132  	f.f(o)
   133  }
   134  
   135  func newFuncBridgeOption(f func(*bridgeOptions)) BridgeOption {
   136  	return &funcBridgeOption{f: f}
   137  }