| // Copyright 2010 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package jsonrpc | |
| import ( | |
| "io" | |
| "json" | |
| "os" | |
| "rpc" | |
| "sync" | |
| ) | |
| type serverCodec struct { | |
| dec *json.Decoder // for reading JSON values | |
| enc *json.Encoder // for writing JSON values | |
| c io.Closer | |
| // temporary work space | |
| req serverRequest | |
| resp serverResponse | |
| // JSON-RPC clients can use arbitrary json values as request IDs. | |
| // Package rpc expects uint64 request IDs. | |
| // We assign uint64 sequence numbers to incoming requests | |
| // but save the original request ID in the pending map. | |
| // When rpc responds, we use the sequence number in | |
| // the response to find the original request ID. | |
| mutex sync.Mutex // protects seq, pending | |
| seq uint64 | |
| pending map[uint64]*json.RawMessage | |
| } | |
| // NewServerCodec returns a new rpc.ServerCodec using JSON-RPC on conn. | |
| func NewServerCodec(conn io.ReadWriteCloser) rpc.ServerCodec { | |
| return &serverCodec{ | |
| dec: json.NewDecoder(conn), | |
| enc: json.NewEncoder(conn), | |
| c: conn, | |
| pending: make(map[uint64]*json.RawMessage), | |
| } | |
| } | |
| type serverRequest struct { | |
| Method string `json:"method"` | |
| Params *json.RawMessage `json:"params"` | |
| Id *json.RawMessage `json:"id"` | |
| } | |
| func (r *serverRequest) reset() { | |
| r.Method = "" | |
| if r.Params != nil { | |
| *r.Params = (*r.Params)[0:0] | |
| } | |
| if r.Id != nil { | |
| *r.Id = (*r.Id)[0:0] | |
| } | |
| } | |
| type serverResponse struct { | |
| Id *json.RawMessage `json:"id"` | |
| Result interface{} `json:"result"` | |
| Error interface{} `json:"error"` | |
| } | |
| func (c *serverCodec) ReadRequestHeader(r *rpc.Request) os.Error { | |
| c.req.reset() | |
| if err := c.dec.Decode(&c.req); err != nil { | |
| return err | |
| } | |
| r.ServiceMethod = c.req.Method | |
| // JSON request id can be any JSON value; | |
| // RPC package expects uint64. Translate to | |
| // internal uint64 and save JSON on the side. | |
| c.mutex.Lock() | |
| c.seq++ | |
| c.pending[c.seq] = c.req.Id | |
| c.req.Id = nil | |
| r.Seq = c.seq | |
| c.mutex.Unlock() | |
| return nil | |
| } | |
| func (c *serverCodec) ReadRequestBody(x interface{}) os.Error { | |
| if x == nil { | |
| return nil | |
| } | |
| // JSON params is array value. | |
| // RPC params is struct. | |
| // Unmarshal into array containing struct for now. | |
| // Should think about making RPC more general. | |
| var params [1]interface{} | |
| params[0] = x | |
| return json.Unmarshal(*c.req.Params, ¶ms) | |
| } | |
| var null = json.RawMessage([]byte("null")) | |
| func (c *serverCodec) WriteResponse(r *rpc.Response, x interface{}) os.Error { | |
| var resp serverResponse | |
| c.mutex.Lock() | |
| b, ok := c.pending[r.Seq] | |
| if !ok { | |
| c.mutex.Unlock() | |
| return os.NewError("invalid sequence number in response") | |
| } | |
| c.pending[r.Seq] = nil, false | |
| c.mutex.Unlock() | |
| if b == nil { | |
| // Invalid request so no id. Use JSON null. | |
| b = &null | |
| } | |
| resp.Id = b | |
| resp.Result = x | |
| if r.Error == "" { | |
| resp.Error = nil | |
| } else { | |
| resp.Error = r.Error | |
| } | |
| return c.enc.Encode(resp) | |
| } | |
| func (c *serverCodec) Close() os.Error { | |
| return c.c.Close() | |
| } | |
| // ServeConn runs the JSON-RPC server on a single connection. | |
| // ServeConn blocks, serving the connection until the client hangs up. | |
| // The caller typically invokes ServeConn in a go statement. | |
| func ServeConn(conn io.ReadWriteCloser) { | |
| rpc.ServeCodec(NewServerCodec(conn)) | |
| } |