github.com/MetalBlockchain/metalgo@v1.11.9/vms/rpcchainvm/grpcutils/client.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package grpcutils
     5  
     6  import (
     7  	"math"
     8  	"time"
     9  
    10  	"google.golang.org/grpc"
    11  	"google.golang.org/grpc/credentials/insecure"
    12  	"google.golang.org/grpc/keepalive"
    13  )
    14  
    15  const (
    16  	// After a duration of this time if the client doesn't see any activity it
    17  	// pings the server to see if the transport is still alive.
    18  	// If set below 10s, a minimum value of 10s will be used instead.
    19  	// grpc-go default infinity
    20  	defaultClientKeepAliveTime = 30 * time.Second
    21  	// After having pinged for keepalive check, the client waits for a duration
    22  	// of Timeout and if no activity is seen even after that the connection is
    23  	// closed. grpc-go default 20s
    24  	defaultClientKeepAliveTimeOut = 10 * time.Second
    25  	// If true, client sends keepalive pings even with no active RPCs. If false,
    26  	// when there are no active RPCs, Time and Timeout will be ignored and no
    27  	// keepalive pings will be sent. grpc-go default false
    28  	defaultPermitWithoutStream = true
    29  	// WaitForReady configures the action to take when an RPC is attempted on
    30  	// broken connections or unreachable servers. If waitForReady is false and
    31  	// the connection is in the TRANSIENT_FAILURE state, the RPC will fail
    32  	// immediately. Otherwise, the RPC client will block the call until a
    33  	// connection is available (or the call is canceled or times out) and will
    34  	// retry the call if it fails due to a transient error. gRPC will not retry
    35  	// if data was written to the wire unless the server indicates it did not
    36  	// process the data. Please refer to
    37  	// https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md.
    38  	//
    39  	// gRPC default behavior is to NOT "wait for ready".
    40  	defaultWaitForReady = true
    41  )
    42  
    43  var DefaultDialOptions = []grpc.DialOption{
    44  	grpc.WithDefaultCallOptions(
    45  		grpc.MaxCallRecvMsgSize(math.MaxInt),
    46  		grpc.MaxCallSendMsgSize(math.MaxInt),
    47  		grpc.WaitForReady(defaultWaitForReady),
    48  	),
    49  	grpc.WithKeepaliveParams(keepalive.ClientParameters{
    50  		Time:                defaultClientKeepAliveTime,
    51  		Timeout:             defaultClientKeepAliveTimeOut,
    52  		PermitWithoutStream: defaultPermitWithoutStream,
    53  	}),
    54  	grpc.WithTransportCredentials(insecure.NewCredentials()),
    55  }
    56  
    57  // gRPC clients created from this ClientConn will wait forever for the Server to
    58  // become Ready. If you desire a dial timeout ensure context is properly plumbed
    59  // to the client and use context.WithTimeout.
    60  //
    61  // Dial returns a gRPC ClientConn with the dial options as defined by
    62  // DefaultDialOptions. DialOption can also optionally be passed.
    63  func Dial(addr string, opts ...DialOption) (*grpc.ClientConn, error) {
    64  	return grpc.Dial("passthrough:///"+addr, newDialOpts(opts...)...)
    65  }
    66  
    67  // DialOptions are options which can be applied to a gRPC client in addition to
    68  // the defaults set by DefaultDialOptions.
    69  type DialOptions struct {
    70  	opts []grpc.DialOption
    71  }
    72  
    73  // append(DefaultDialOptions, ...) will always allocate a new slice and will
    74  // not overwrite any potential data that may have previously been appended to
    75  // DefaultServerOptions https://go.dev/ref/spec#Composite_literals
    76  func newDialOpts(opts ...DialOption) []grpc.DialOption {
    77  	d := &DialOptions{opts: DefaultDialOptions}
    78  	d.applyOpts(opts)
    79  	return d.opts
    80  }
    81  
    82  func (d *DialOptions) applyOpts(opts []DialOption) {
    83  	for _, opt := range opts {
    84  		opt(d)
    85  	}
    86  }
    87  
    88  type DialOption func(*DialOptions)
    89  
    90  // WithChainUnaryInterceptor takes a list of unary client interceptors which
    91  // are added to the dial options.
    92  func WithChainUnaryInterceptor(interceptors ...grpc.UnaryClientInterceptor) DialOption {
    93  	return func(d *DialOptions) {
    94  		d.opts = append(d.opts, grpc.WithChainUnaryInterceptor(interceptors...))
    95  	}
    96  }
    97  
    98  // WithChainStreamInterceptor takes a list of stream client interceptors which
    99  // are added to the dial options.
   100  func WithChainStreamInterceptor(interceptors ...grpc.StreamClientInterceptor) DialOption {
   101  	return func(d *DialOptions) {
   102  		d.opts = append(d.opts, grpc.WithChainStreamInterceptor(interceptors...))
   103  	}
   104  }