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 }