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