Source file src/pkg/net/rpc/jsonrpc/client.go
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 {
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 }