github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/src/net/rpc/jsonrpc/client.go (about) 1 // Copyright 2010 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package jsonrpc implements a JSON-RPC 1.0 ClientCodec and ServerCodec 6 // for the rpc package. 7 // For JSON-RPC 2.0 support, see https://godoc.org/?q=json-rpc+2.0 8 package jsonrpc 9 10 import ( 11 "encoding/json" 12 "fmt" 13 "io" 14 "net" 15 "net/rpc" 16 "sync" 17 ) 18 19 type clientCodec struct { 20 dec *json.Decoder // for reading JSON values 21 enc *json.Encoder // for writing JSON values 22 c io.Closer 23 24 // temporary work space 25 req clientRequest 26 resp clientResponse 27 28 // JSON-RPC responses include the request id but not the request method. 29 // Package rpc expects both. 30 // We save the request method in pending when sending a request 31 // and then look it up by request ID when filling out the rpc Response. 32 mutex sync.Mutex // protects pending 33 pending map[uint64]string // map request id to method name 34 } 35 36 // NewClientCodec returns a new rpc.ClientCodec using JSON-RPC on conn. 37 func NewClientCodec(conn io.ReadWriteCloser) rpc.ClientCodec { 38 return &clientCodec{ 39 dec: json.NewDecoder(conn), 40 enc: json.NewEncoder(conn), 41 c: conn, 42 pending: make(map[uint64]string), 43 } 44 } 45 46 type clientRequest struct { 47 Method string `json:"method"` 48 Params [1]interface{} `json:"params"` 49 Id uint64 `json:"id"` 50 } 51 52 func (c *clientCodec) WriteRequest(r *rpc.Request, param interface{}) error { 53 c.mutex.Lock() 54 c.pending[r.Seq] = r.ServiceMethod 55 c.mutex.Unlock() 56 c.req.Method = r.ServiceMethod 57 c.req.Params[0] = param 58 c.req.Id = r.Seq 59 return c.enc.Encode(&c.req) 60 } 61 62 type clientResponse struct { 63 Id uint64 `json:"id"` 64 Result *json.RawMessage `json:"result"` 65 Error interface{} `json:"error"` 66 } 67 68 func (r *clientResponse) reset() { 69 r.Id = 0 70 r.Result = nil 71 r.Error = nil 72 } 73 74 func (c *clientCodec) ReadResponseHeader(r *rpc.Response) error { 75 c.resp.reset() 76 if err := c.dec.Decode(&c.resp); err != nil { 77 return err 78 } 79 80 c.mutex.Lock() 81 r.ServiceMethod = c.pending[c.resp.Id] 82 delete(c.pending, c.resp.Id) 83 c.mutex.Unlock() 84 85 r.Error = "" 86 r.Seq = c.resp.Id 87 if c.resp.Error != nil || c.resp.Result == nil { 88 x, ok := c.resp.Error.(string) 89 if !ok { 90 return fmt.Errorf("invalid error %v", c.resp.Error) 91 } 92 if x == "" { 93 x = "unspecified error" 94 } 95 r.Error = x 96 } 97 return nil 98 } 99 100 func (c *clientCodec) ReadResponseBody(x interface{}) error { 101 if x == nil { 102 return nil 103 } 104 return json.Unmarshal(*c.resp.Result, x) 105 } 106 107 func (c *clientCodec) Close() error { 108 return c.c.Close() 109 } 110 111 // NewClient returns a new rpc.Client to handle requests to the 112 // set of services at the other end of the connection. 113 func NewClient(conn io.ReadWriteCloser) *rpc.Client { 114 return rpc.NewClientWithCodec(NewClientCodec(conn)) 115 } 116 117 // Dial connects to a JSON-RPC server at the specified network address. 118 func Dial(network, address string) (*rpc.Client, error) { 119 conn, err := net.Dial(network, address) 120 if err != nil { 121 return nil, err 122 } 123 return NewClient(conn), err 124 }