| // 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 "method" |
| Params *json.RawMessage "params" |
| Id *json.RawMessage "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 "id" |
| Result interface{} "result" |
| Error interface{} "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)) |
| } |