github.com/ugorji/go/codec@v1.2.13-0.20240307214044-07c54c229a5a/rpc.go (about)

     1  // Copyright (c) 2012-2020 Ugorji Nwoke. All rights reserved.
     2  // Use of this source code is governed by a MIT license found in the LICENSE file.
     3  
     4  package codec
     5  
     6  import (
     7  	"bufio"
     8  	"errors"
     9  	"io"
    10  	"net/rpc"
    11  )
    12  
    13  var (
    14  	errRpcIsClosed = errors.New("rpc - connection has been closed")
    15  	errRpcNoConn   = errors.New("rpc - no connection")
    16  
    17  	rpcSpaceArr = [1]byte{' '}
    18  )
    19  
    20  // Rpc provides a rpc Server or Client Codec for rpc communication.
    21  type Rpc interface {
    22  	ServerCodec(conn io.ReadWriteCloser, h Handle) rpc.ServerCodec
    23  	ClientCodec(conn io.ReadWriteCloser, h Handle) rpc.ClientCodec
    24  }
    25  
    26  // RPCOptions holds options specific to rpc functionality
    27  type RPCOptions struct {
    28  	// RPCNoBuffer configures whether we attempt to buffer reads and writes during RPC calls.
    29  	//
    30  	// Set RPCNoBuffer=true to turn buffering off.
    31  	// Buffering can still be done if buffered connections are passed in, or
    32  	// buffering is configured on the handle.
    33  	RPCNoBuffer bool
    34  }
    35  
    36  // rpcCodec defines the struct members and common methods.
    37  type rpcCodec struct {
    38  	c io.Closer
    39  	r io.Reader
    40  	w io.Writer
    41  	f ioFlusher
    42  
    43  	dec *Decoder
    44  	enc *Encoder
    45  	h   Handle
    46  
    47  	cls atomicClsErr
    48  }
    49  
    50  func newRPCCodec(conn io.ReadWriteCloser, h Handle) rpcCodec {
    51  	return newRPCCodec2(conn, conn, conn, h)
    52  }
    53  
    54  func newRPCCodec2(r io.Reader, w io.Writer, c io.Closer, h Handle) rpcCodec {
    55  	bh := h.getBasicHandle()
    56  	// if the writer can flush, ensure we leverage it, else
    57  	// we may hang waiting on read if write isn't flushed.
    58  	// var f ioFlusher
    59  	f, ok := w.(ioFlusher)
    60  	if !bh.RPCNoBuffer {
    61  		if bh.WriterBufferSize <= 0 {
    62  			if !ok { // a flusher means there's already a buffer
    63  				bw := bufio.NewWriter(w)
    64  				f, w = bw, bw
    65  			}
    66  		}
    67  		if bh.ReaderBufferSize <= 0 {
    68  			if _, ok = w.(ioBuffered); !ok {
    69  				r = bufio.NewReader(r)
    70  			}
    71  		}
    72  	}
    73  	return rpcCodec{
    74  		c:   c,
    75  		w:   w,
    76  		r:   r,
    77  		f:   f,
    78  		h:   h,
    79  		enc: NewEncoder(w, h),
    80  		dec: NewDecoder(r, h),
    81  	}
    82  }
    83  
    84  func (c *rpcCodec) write(obj ...interface{}) (err error) {
    85  	err = c.ready()
    86  	if err != nil {
    87  		return
    88  	}
    89  	if c.f != nil {
    90  		defer func() {
    91  			flushErr := c.f.Flush()
    92  			if flushErr != nil && err == nil {
    93  				err = flushErr
    94  			}
    95  		}()
    96  	}
    97  
    98  	for _, o := range obj {
    99  		err = c.enc.Encode(o)
   100  		if err != nil {
   101  			return
   102  		}
   103  		// defensive: ensure a space is always written after each encoding,
   104  		// in case the value was a number, and encoding a value right after
   105  		// without a space will lead to invalid output.
   106  		if c.h.isJson() {
   107  			_, err = c.w.Write(rpcSpaceArr[:])
   108  			if err != nil {
   109  				return
   110  			}
   111  		}
   112  	}
   113  	return
   114  }
   115  
   116  func (c *rpcCodec) read(obj interface{}) (err error) {
   117  	err = c.ready()
   118  	if err == nil {
   119  		//If nil is passed in, we should read and discard
   120  		if obj == nil {
   121  			// return c.dec.Decode(&obj)
   122  			err = c.dec.swallowErr()
   123  		} else {
   124  			err = c.dec.Decode(obj)
   125  		}
   126  	}
   127  	return
   128  }
   129  
   130  func (c *rpcCodec) Close() (err error) {
   131  	if c.c != nil {
   132  		cls := c.cls.load()
   133  		if !cls.closed {
   134  			cls.err = c.c.Close()
   135  			cls.closed = true
   136  			c.cls.store(cls)
   137  		}
   138  		err = cls.err
   139  	}
   140  	return
   141  }
   142  
   143  func (c *rpcCodec) ready() (err error) {
   144  	if c.c == nil {
   145  		err = errRpcNoConn
   146  	} else {
   147  		cls := c.cls.load()
   148  		if cls.closed {
   149  			if err = cls.err; err == nil {
   150  				err = errRpcIsClosed
   151  			}
   152  		}
   153  	}
   154  	return
   155  }
   156  
   157  func (c *rpcCodec) ReadResponseBody(body interface{}) error {
   158  	return c.read(body)
   159  }
   160  
   161  // -------------------------------------
   162  
   163  type goRpcCodec struct {
   164  	rpcCodec
   165  }
   166  
   167  func (c *goRpcCodec) WriteRequest(r *rpc.Request, body interface{}) error {
   168  	return c.write(r, body)
   169  }
   170  
   171  func (c *goRpcCodec) WriteResponse(r *rpc.Response, body interface{}) error {
   172  	return c.write(r, body)
   173  }
   174  
   175  func (c *goRpcCodec) ReadResponseHeader(r *rpc.Response) error {
   176  	return c.read(r)
   177  }
   178  
   179  func (c *goRpcCodec) ReadRequestHeader(r *rpc.Request) error {
   180  	return c.read(r)
   181  }
   182  
   183  func (c *goRpcCodec) ReadRequestBody(body interface{}) error {
   184  	return c.read(body)
   185  }
   186  
   187  // -------------------------------------
   188  
   189  // goRpc is the implementation of Rpc that uses the communication protocol
   190  // as defined in net/rpc package.
   191  type goRpc struct{}
   192  
   193  // GoRpc implements Rpc using the communication protocol defined in net/rpc package.
   194  //
   195  // Note: network connection (from net.Dial, of type io.ReadWriteCloser) is not buffered.
   196  //
   197  // For performance, you should configure WriterBufferSize and ReaderBufferSize on the handle.
   198  // This ensures we use an adequate buffer during reading and writing.
   199  // If not configured, we will internally initialize and use a buffer during reads and writes.
   200  // This can be turned off via the RPCNoBuffer option on the Handle.
   201  //
   202  //	var handle codec.JsonHandle
   203  //	handle.RPCNoBuffer = true // turns off attempt by rpc module to initialize a buffer
   204  //
   205  // Example 1: one way of configuring buffering explicitly:
   206  //
   207  //	var handle codec.JsonHandle // codec handle
   208  //	handle.ReaderBufferSize = 1024
   209  //	handle.WriterBufferSize = 1024
   210  //	var conn io.ReadWriteCloser // connection got from a socket
   211  //	var serverCodec = GoRpc.ServerCodec(conn, handle)
   212  //	var clientCodec = GoRpc.ClientCodec(conn, handle)
   213  //
   214  // Example 2: you can also explicitly create a buffered connection yourself,
   215  // and not worry about configuring the buffer sizes in the Handle.
   216  //
   217  //	var handle codec.Handle     // codec handle
   218  //	var conn io.ReadWriteCloser // connection got from a socket
   219  //	var bufconn = struct {      // bufconn here is a buffered io.ReadWriteCloser
   220  //	    io.Closer
   221  //	    *bufio.Reader
   222  //	    *bufio.Writer
   223  //	}{conn, bufio.NewReader(conn), bufio.NewWriter(conn)}
   224  //	var serverCodec = GoRpc.ServerCodec(bufconn, handle)
   225  //	var clientCodec = GoRpc.ClientCodec(bufconn, handle)
   226  var GoRpc goRpc
   227  
   228  func (x goRpc) ServerCodec(conn io.ReadWriteCloser, h Handle) rpc.ServerCodec {
   229  	return &goRpcCodec{newRPCCodec(conn, h)}
   230  }
   231  
   232  func (x goRpc) ClientCodec(conn io.ReadWriteCloser, h Handle) rpc.ClientCodec {
   233  	return &goRpcCodec{newRPCCodec(conn, h)}
   234  }