tractor.dev/toolkit-go@v0.0.0-20241010005851-214d91207d07/duplex/rpc/rpc.go (about)

     1  package rpc
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  
     7  	"github.com/mitchellh/mapstructure"
     8  	"tractor.dev/toolkit-go/duplex/codec"
     9  	"tractor.dev/toolkit-go/duplex/mux"
    10  )
    11  
    12  // A Caller is able to perform remote calls.
    13  //
    14  // Call makes synchronous calls to the remote selector passing args and putting the response
    15  // value(s) in reply. Both args and reply can be nil. Args can be a channel of any
    16  // values for asynchronously streaming multiple values from another goroutine, however
    17  // the call will still block until a response is sent. If there is an error making the call
    18  // an error is returned, and if an error is returned by the remote handler a RemoteError
    19  // is returned. Multiple reply parameters can be provided in order to receive multi-valued
    20  // returns from the remote call.
    21  //
    22  // A Response is also returned for advanced operations. For example, you can check
    23  // if the call is continued, meaning the underlying channel will be kept open for either
    24  // streaming back more results or using the channel as a full duplex byte stream.
    25  type Caller interface {
    26  	Call(ctx context.Context, selector string, params any, reply ...any) (*Response, error)
    27  }
    28  
    29  // CallHeader is the first value encoded over the channel to make a call.
    30  type CallHeader struct {
    31  	S string // Selector
    32  }
    33  
    34  // Call is used on the responding side of a call and is passed to the handler.
    35  // Call has a Caller so it can be used to make calls back to the calling side.
    36  type Call struct {
    37  	CallHeader
    38  
    39  	Caller  Caller
    40  	Decoder codec.Decoder
    41  	Context context.Context
    42  
    43  	mux.Channel
    44  }
    45  
    46  func (c *Call) Selector() string {
    47  	return c.S
    48  }
    49  
    50  // Receive will decode an incoming value from the underlying channel. It can be
    51  // called more than once when multiple values are expected, but should always be
    52  // called once in a handler. It can be called with nil to discard the value.
    53  func (c *Call) Receive(v interface{}) error {
    54  	if v == nil {
    55  		var discard []byte
    56  		v = &discard
    57  	}
    58  	return c.Decoder.Decode(v)
    59  }
    60  
    61  // ResponseHeader is the value encoded over the channel to indicate a response.
    62  type ResponseHeader struct {
    63  	E *string // Error
    64  	C bool    // Continue: after parsing response, keep stream open for whatever protocol
    65  }
    66  
    67  // Response is used on the calling side to represent a response and allow access
    68  // to the ResponseHeader data, the reply value, the underlying channel, and methods
    69  // to send or receive encoded values over the channel if Continue was set on the
    70  // ResponseHeader.
    71  type Response struct {
    72  	ResponseHeader
    73  	Value   any
    74  	Channel mux.Channel
    75  
    76  	codec codec.Codec
    77  }
    78  
    79  func (r *Response) Err() error {
    80  	if r.E == nil {
    81  		return nil
    82  	}
    83  	return errors.New(*r.E)
    84  }
    85  
    86  func (r *Response) Continue() bool {
    87  	return r.C
    88  }
    89  
    90  // Send encodes a value over the underlying channel if it is still open.
    91  func (r *Response) Send(v interface{}) error {
    92  	return r.codec.Encoder(r.Channel).Encode(v)
    93  }
    94  
    95  // Receive decodes a value from the underlying channel if it is still open.
    96  func (r *Response) Receive(v interface{}) error {
    97  	return r.codec.Decoder(r.Channel).Decode(v)
    98  }
    99  
   100  func (r *Response) Close() error {
   101  	return r.Channel.Close()
   102  }
   103  
   104  func (r *Response) CloseWrite() error {
   105  	return r.Channel.CloseWrite()
   106  }
   107  
   108  // Responder is used by handlers to initiate a response and send values to the caller.
   109  type Responder interface {
   110  	// Return sends a return value, which can be an error, and closes the channel.
   111  	Return(...any) error
   112  
   113  	// Continue sets the response to keep the channel open after sending a return value,
   114  	// and returns the underlying channel for you to take control of. If called, you
   115  	// become responsible for closing the channel.
   116  	Continue(...any) (mux.Channel, error)
   117  
   118  	// Send encodes a value over the underlying channel, but does not initiate a response,
   119  	// so it must be used after calling Continue.
   120  	Send(interface{}) error
   121  }
   122  
   123  type responder struct {
   124  	responded bool
   125  	header    *ResponseHeader
   126  	ch        mux.Channel
   127  	c         codec.Codec
   128  }
   129  
   130  func (r *responder) Send(v interface{}) error {
   131  	return r.c.Encoder(r.ch).Encode(v)
   132  }
   133  
   134  func (r *responder) Return(v ...any) error {
   135  	return r.respond(v, false)
   136  }
   137  
   138  func (r *responder) Continue(v ...any) (mux.Channel, error) {
   139  	return r.ch, r.respond(v, true)
   140  }
   141  
   142  func (r *responder) respond(values []any, continue_ bool) error {
   143  	r.responded = true
   144  	r.header.C = continue_
   145  
   146  	// if values is a single error, set values to [nil]
   147  	// and put error in header
   148  	if len(values) == 1 {
   149  		var e error
   150  		var ok bool
   151  		if e, ok = values[0].(error); ok {
   152  			values = []any{nil}
   153  		}
   154  		if e != nil {
   155  			var errStr = e.Error()
   156  			r.header.E = &errStr
   157  		}
   158  	}
   159  
   160  	if err := r.Send(r.header); err != nil {
   161  		return err
   162  	}
   163  
   164  	// The original calling convention expects at least one return, so return
   165  	// `nil` if there is no other return value.
   166  	if len(values) == 0 {
   167  		values = []any{nil}
   168  	}
   169  	for _, v := range values {
   170  		if err := r.Send(v); err != nil {
   171  			return err
   172  		}
   173  	}
   174  
   175  	if !continue_ {
   176  		return r.ch.Close()
   177  	}
   178  
   179  	return nil
   180  }
   181  
   182  // ReceiveNotify takes a continued response and sends received values to a channel,
   183  // until an error is returned or the context finishes. In either case, the response
   184  // and the channel will be closed.
   185  func ReceiveNotify[T any](ctx context.Context, resp *Response, ch chan T) error {
   186  	defer close(ch)
   187  	defer resp.Close()
   188  	for {
   189  		var v any
   190  		if err := resp.Receive(&v); err != nil {
   191  			return err
   192  		}
   193  		var vv T
   194  		if err := mapstructure.Decode(v, &vv); err != nil {
   195  			return err
   196  		}
   197  		select {
   198  		case <-ctx.Done():
   199  			return nil
   200  		default:
   201  			ch <- vv
   202  		}
   203  	}
   204  }