Source file src/pkg/net/rpc/jsonrpc/server.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
6
7 import (
8 "encoding/json"
9 "errors"
10 "io"
11 "net/rpc"
12 "sync"
13 )
14
15 type serverCodec struct {
16 dec *json.Decoder // for reading JSON values
17 enc *json.Encoder // for writing JSON values
18 c io.Closer
19
20 // temporary work space
21 req serverRequest
22 resp serverResponse
23
24 // JSON-RPC clients can use arbitrary json values as request IDs.
25 // Package rpc expects uint64 request IDs.
26 // We assign uint64 sequence numbers to incoming requests
27 // but save the original request ID in the pending map.
28 // When rpc responds, we use the sequence number in
29 // the response to find the original request ID.
30 mutex sync.Mutex // protects seq, pending
31 seq uint64
32 pending map[uint64]*json.RawMessage
33 }
34
35 // NewServerCodec returns a new rpc.ServerCodec using JSON-RPC on conn.
36 func NewServerCodec(conn io.ReadWriteCloser) rpc.ServerCodec {
37 return &serverCodec{
38 dec: json.NewDecoder(conn),
39 enc: json.NewEncoder(conn),
40 c: conn,
41 pending: make(map[uint64]*json.RawMessage),
42 }
43 }
44
45 type serverRequest struct {
46 Method string `json:"method"`
47 Params *json.RawMessage `json:"params"`
48 Id *json.RawMessage `json:"id"`
49 }
50
51 func (r *serverRequest) reset() {
52 r.Method = ""
53 if r.Params != nil {
54 *r.Params = (*r.Params)[0:0]
55 }
56 if r.Id != nil {
57 *r.Id = (*r.Id)[0:0]
58 }
59 }
60
61 type serverResponse struct {
62 Id *json.RawMessage `json:"id"`
63 Result interface{} `json:"result"`
64 Error interface{} `json:"error"`
65 }
66
67 func (c *serverCodec) ReadRequestHeader(r *rpc.Request) error {
68 c.req.reset()
69 if err := c.dec.Decode(&c.req); err != nil {
70 return err
71 }
72 r.ServiceMethod = c.req.Method
73
74 // JSON request id can be any JSON value;
75 // RPC package expects uint64. Translate to
76 // internal uint64 and save JSON on the side.
77 c.mutex.Lock()
78 c.seq++
79 c.pending[c.seq] = c.req.Id
80 c.req.Id = nil
81 r.Seq = c.seq
82 c.mutex.Unlock()
83
84 return nil
85 }
86
87 func (c *serverCodec) ReadRequestBody(x interface{}) error {
88 if x == nil {
89 return nil
90 }
91 // JSON params is array value.
92 // RPC params is struct.
93 // Unmarshal into array containing struct for now.
94 // Should think about making RPC more general.
95 var params [1]interface{}
96 params[0] = x
97 return json.Unmarshal(*c.req.Params, ¶ms)
98 }
99
100 var null = json.RawMessage([]byte("null"))
101
102 func (c *serverCodec) WriteResponse(r *rpc.Response, x interface{}) error {
103 var resp serverResponse
104 c.mutex.Lock()
105 b, ok := c.pending[r.Seq]
106 if !ok {
107 c.mutex.Unlock()
108 return errors.New("invalid sequence number in response")
109 }
110 delete(c.pending, r.Seq)
111 c.mutex.Unlock()
112
113 if b == nil {
114 // Invalid request so no id. Use JSON null.
115 b = &null
116 }
117 resp.Id = b
118 resp.Result = x
119 if r.Error == "" {
120 resp.Error = nil
121 } else {
122 resp.Error = r.Error
123 }
124 return c.enc.Encode(resp)
125 }
126
127 func (c *serverCodec) Close() error {
128 return c.c.Close()
129 }
130
131 // ServeConn runs the JSON-RPC server on a single connection.
132 // ServeConn blocks, serving the connection until the client hangs up.
133 // The caller typically invokes ServeConn in a go statement.
134 func ServeConn(conn io.ReadWriteCloser) {
135 rpc.ServeCodec(NewServerCodec(conn))
136 }