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

     1  package rpc
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"log"
     7  	"net"
     8  
     9  	"tractor.dev/toolkit-go/duplex/codec"
    10  	"tractor.dev/toolkit-go/duplex/mux"
    11  )
    12  
    13  // Server wraps a Handler and codec to respond to RPC calls.
    14  type Server struct {
    15  	Handler Handler
    16  	Codec   codec.Codec
    17  }
    18  
    19  // ServeMux will Accept sessions until the Listener is closed, and will Respond to accepted sessions in their own goroutine.
    20  func (s *Server) ServeMux(l mux.Listener) error {
    21  	for {
    22  		sess, err := l.Accept()
    23  		if err != nil {
    24  			return err
    25  		}
    26  		go s.Respond(sess, nil)
    27  	}
    28  }
    29  
    30  // Serve will Accept sessions until the Listener is closed, and will Respond to accepted sessions in their own goroutine.
    31  func (s *Server) Serve(l net.Listener) error {
    32  	return s.ServeMux(mux.ListenerFrom(l))
    33  }
    34  
    35  // Respond will Accept channels until the Session is closed and respond with the server handler in its own goroutine.
    36  // If Handler was not set, an empty RespondMux is used. If the handler does not initiate a response, a nil value is
    37  // returned. If the handler does not call Continue, the channel will be closed. Respond will panic if Codec is nil.
    38  //
    39  // If the context is not nil, it will be added to Calls. Otherwise the Call Context will be set to a context.Background().
    40  func (s *Server) Respond(sess mux.Session, ctx context.Context) {
    41  	defer sess.Close()
    42  
    43  	if s.Codec == nil {
    44  		panic("rpc.Respond: nil codec")
    45  	}
    46  
    47  	hn := s.Handler
    48  	if hn == nil {
    49  		hn = NewRespondMux()
    50  	}
    51  
    52  	for {
    53  		ch, err := sess.Accept()
    54  		if err != nil {
    55  			if err == io.EOF {
    56  				return
    57  			}
    58  			panic(err)
    59  		}
    60  		go s.respond(hn, sess, ch, ctx)
    61  	}
    62  }
    63  
    64  func (s *Server) respond(hn Handler, sess mux.Session, ch mux.Channel, ctx context.Context) {
    65  	framer := &FrameCodec{Codec: s.Codec}
    66  	dec := framer.Decoder(ch)
    67  
    68  	var call Call
    69  	err := dec.Decode(&call)
    70  	if err != nil {
    71  		log.Println("rpc.Respond:", err)
    72  		return
    73  	}
    74  
    75  	call.S = cleanSelector(call.Selector())
    76  	call.Decoder = dec
    77  	call.Caller = &Client{
    78  		Session: sess,
    79  		codec:   s.Codec,
    80  	}
    81  	if ctx == nil {
    82  		call.Context = context.Background()
    83  	} else {
    84  		call.Context = ctx
    85  	}
    86  	call.Channel = ch
    87  
    88  	header := &ResponseHeader{}
    89  	resp := &responder{
    90  		ch:     ch,
    91  		c:      framer,
    92  		header: header,
    93  	}
    94  
    95  	hn.RespondRPC(resp, &call)
    96  	if !resp.responded {
    97  		resp.Return()
    98  	}
    99  	if !resp.header.C {
   100  		ch.Close()
   101  	}
   102  }