|
|
@@ -4,6 +4,7 @@
|
|
|
package codec
|
|
|
|
|
|
import (
|
|
|
+ "bufio"
|
|
|
"errors"
|
|
|
"io"
|
|
|
"net/rpc"
|
|
|
@@ -16,27 +17,12 @@ type Rpc interface {
|
|
|
ClientCodec(conn io.ReadWriteCloser, h Handle) rpc.ClientCodec
|
|
|
}
|
|
|
|
|
|
-// // RpcCodecBuffered allows access to the underlying bufio.Reader/Writer
|
|
|
-// // used by the rpc connection. It accommodates use-cases where the connection
|
|
|
-// // should be used by rpc and non-rpc functions, e.g. streaming a file after
|
|
|
-// // sending an rpc response.
|
|
|
-// type RpcCodecBuffered interface {
|
|
|
-// BufferedReader() *bufio.Reader
|
|
|
-// BufferedWriter() *bufio.Writer
|
|
|
-// }
|
|
|
-
|
|
|
-// -------------------------------------
|
|
|
-
|
|
|
-type rpcFlusher interface {
|
|
|
- Flush() error
|
|
|
-}
|
|
|
-
|
|
|
// rpcCodec defines the struct members and common methods.
|
|
|
type rpcCodec struct {
|
|
|
c io.Closer
|
|
|
r io.Reader
|
|
|
w io.Writer
|
|
|
- f rpcFlusher
|
|
|
+ f ioFlusher
|
|
|
|
|
|
dec *Decoder
|
|
|
enc *Encoder
|
|
|
@@ -60,7 +46,13 @@ func newRPCCodec2(r io.Reader, w io.Writer, c io.Closer, h Handle) rpcCodec {
|
|
|
if jsonH, ok := h.(*JsonHandle); ok && !jsonH.TermWhitespace {
|
|
|
panic(errors.New("rpc requires a JsonHandle with TermWhitespace set to true"))
|
|
|
}
|
|
|
- f, _ := w.(rpcFlusher)
|
|
|
+ // always ensure that we use a flusher, and always flush what was written to the connection.
|
|
|
+ // we lose nothing by using a buffered writer internally.
|
|
|
+ f, ok := w.(ioFlusher)
|
|
|
+ if !ok {
|
|
|
+ bw := bufio.NewWriter(w)
|
|
|
+ f, w = bw, bw
|
|
|
+ }
|
|
|
return rpcCodec{
|
|
|
c: c,
|
|
|
w: w,
|
|
|
@@ -72,17 +64,9 @@ func newRPCCodec2(r io.Reader, w io.Writer, c io.Closer, h Handle) rpcCodec {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// func (c *rpcCodec) BufferedReader() *bufio.Reader {
|
|
|
-// return c.br
|
|
|
-// }
|
|
|
-
|
|
|
-// func (c *rpcCodec) BufferedWriter() *bufio.Writer {
|
|
|
-// return c.bw
|
|
|
-// }
|
|
|
-
|
|
|
-func (c *rpcCodec) write(obj1, obj2 interface{}, writeObj2, doFlush bool) (err error) {
|
|
|
+func (c *rpcCodec) write(obj1, obj2 interface{}, writeObj2 bool) (err error) {
|
|
|
if c.isClosed() {
|
|
|
- return io.EOF
|
|
|
+ return c.clsErr
|
|
|
}
|
|
|
if err = c.enc.Encode(obj1); err != nil {
|
|
|
return
|
|
|
@@ -92,7 +76,7 @@ func (c *rpcCodec) write(obj1, obj2 interface{}, writeObj2, doFlush bool) (err e
|
|
|
return
|
|
|
}
|
|
|
}
|
|
|
- if doFlush && c.f != nil {
|
|
|
+ if c.f != nil {
|
|
|
return c.f.Flush()
|
|
|
}
|
|
|
return
|
|
|
@@ -100,24 +84,29 @@ func (c *rpcCodec) write(obj1, obj2 interface{}, writeObj2, doFlush bool) (err e
|
|
|
|
|
|
func (c *rpcCodec) read(obj interface{}) (err error) {
|
|
|
if c.isClosed() {
|
|
|
- return io.EOF
|
|
|
+ return c.clsErr
|
|
|
}
|
|
|
- //If nil is passed in, we should still attempt to read content to nowhere.
|
|
|
+ //If nil is passed in, we should read and discard
|
|
|
if obj == nil {
|
|
|
- var obj2 interface{}
|
|
|
- return c.dec.Decode(&obj2)
|
|
|
+ // var obj2 interface{}
|
|
|
+ // return c.dec.Decode(&obj2)
|
|
|
+ func() {
|
|
|
+ defer panicToErr(&err)
|
|
|
+ c.dec.swallow()
|
|
|
+ }()
|
|
|
+ return
|
|
|
}
|
|
|
return c.dec.Decode(obj)
|
|
|
}
|
|
|
|
|
|
-func (c *rpcCodec) isClosed() bool {
|
|
|
+func (c *rpcCodec) isClosed() (b bool) {
|
|
|
if c.c == nil {
|
|
|
return false
|
|
|
}
|
|
|
c.clsmu.RLock()
|
|
|
- x := c.cls
|
|
|
+ b = c.cls
|
|
|
c.clsmu.RUnlock()
|
|
|
- return x
|
|
|
+ return
|
|
|
}
|
|
|
|
|
|
func (c *rpcCodec) Close() error {
|
|
|
@@ -156,13 +145,13 @@ func (c *goRpcCodec) WriteRequest(r *rpc.Request, body interface{}) error {
|
|
|
// Must protect for concurrent access as per API
|
|
|
c.mu.Lock()
|
|
|
defer c.mu.Unlock()
|
|
|
- return c.write(r, body, true, true)
|
|
|
+ return c.write(r, body, true)
|
|
|
}
|
|
|
|
|
|
func (c *goRpcCodec) WriteResponse(r *rpc.Response, body interface{}) error {
|
|
|
c.mu.Lock()
|
|
|
defer c.mu.Unlock()
|
|
|
- return c.write(r, body, true, true)
|
|
|
+ return c.write(r, body, true)
|
|
|
}
|
|
|
|
|
|
func (c *goRpcCodec) ReadResponseHeader(r *rpc.Response) error {
|
|
|
@@ -184,13 +173,23 @@ func (c *goRpcCodec) ReadRequestBody(body interface{}) error {
|
|
|
type goRpc struct{}
|
|
|
|
|
|
// GoRpc implements Rpc using the communication protocol defined in net/rpc package.
|
|
|
-// Its methods (ServerCodec and ClientCodec) return values that implement RpcCodecBuffered.
|
|
|
//
|
|
|
-// By default, the conn parameter got from a network is not buffered.
|
|
|
-// For performance, considering using a buffered value e.g.
|
|
|
+// Note: network connection (from net.Dial, of type io.ReadWriteCloser) is not buffered.
|
|
|
+// We will internally use a buffer during writes, for performance, if the non-buffered
|
|
|
+// connection is passed in.
|
|
|
+//
|
|
|
+// However, you may consider explicitly passing in a buffered value e.g.
|
|
|
+// var handle codec.Handle // codec handle
|
|
|
// var conn io.ReadWriteCloser // connection got from a socket
|
|
|
-// conn2 := codec.NewReadWriteCloser(conn, conn, 1024, 1024) // wrapped in 1024-byte bufer
|
|
|
-// var h = GoRpc.ServerCodec(conn2, handle)
|
|
|
+// var bufconn = struct { // bufconn here is a buffered io.ReadWriteCloser
|
|
|
+// io.Closer
|
|
|
+// *bufio.Reader
|
|
|
+// *bufio.Writer
|
|
|
+// }{conn, bufio.NewReader(conn), bufio.NewWriter(conn)}
|
|
|
+// var serverCodec = GoRpc.ServerCodec(bufconn, handle)
|
|
|
+// var clientCodec = GoRpc.ClientCodec(bufconn, handle)
|
|
|
+//
|
|
|
+// If all you care about is buffered writes, this is done automatically for you.
|
|
|
var GoRpc goRpc
|
|
|
|
|
|
func (x goRpc) ServerCodec(conn io.ReadWriteCloser, h Handle) rpc.ServerCodec {
|
|
|
@@ -200,5 +199,3 @@ func (x goRpc) ServerCodec(conn io.ReadWriteCloser, h Handle) rpc.ServerCodec {
|
|
|
func (x goRpc) ClientCodec(conn io.ReadWriteCloser, h Handle) rpc.ClientCodec {
|
|
|
return &goRpcCodec{newRPCCodec(conn, h)}
|
|
|
}
|
|
|
-
|
|
|
-// var _ RpcCodecBuffered = (*rpcCodec)(nil) // ensure *rpcCodec implements RpcCodecBuffered
|