Index: src/pkg/rpc/jsonrpc/client.go |
=================================================================== |
new file mode 100644 |
--- /dev/null |
+++ b/src/pkg/rpc/jsonrpc/client.go |
@@ -0,0 +1,110 @@ |
+// 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 implements a JSON-RPC ClientCodec and ServerCodec |
+// for the rpc package. |
+package jsonrpc |
+ |
+import ( |
+ "io" |
+ "json" |
+ "net" |
+ "os" |
+ "rpc" |
+ "sync" |
+) |
+ |
+type clientCodec struct { |
+ dec *json.Decoder // for reading JSON values |
+ enc *json.Encoder // for writing JSON values |
+ c io.Closer |
+ |
+ // temporary work space |
+ req clientRequest |
+ resp clientResponse |
+ |
+ // JSON-RPC responses include the request id but not the request method. |
+ // Package rpc expects both. |
+ // We save the request method in pending when sending a request |
+ // and then look it up by request ID when filling out the rpc Response. |
+ mutex sync.Mutex // protects pending |
+ pending map[uint64]string // map request id to method name |
+} |
+ |
+// NewClientCodec returns a new rpc.ClientCodec using JSON-RPC on conn. |
+func NewClientCodec(conn io.ReadWriteCloser) rpc.ClientCodec { |
+ return &clientCodec{ |
+ dec: json.NewDecoder(conn), |
+ enc: json.NewEncoder(conn), |
+ c: conn, |
+ pending: make(map[uint64]string), |
+ } |
+} |
+ |
+type clientRequest struct { |
+ Method string "method" |
+ Params [1]interface{} "params" |
+ Id uint64 "id" |
+} |
+ |
+func (c *clientCodec) WriteRequest(r *rpc.Request, param interface{}) os.Error { |
+ c.mutex.Lock() |
+ c.pending[r.Seq] = r.ServiceMethod |
+ c.mutex.Unlock() |
+ c.req.Method = r.ServiceMethod |
+ c.req.Params[0] = param |
+ c.req.Id = r.Seq |
+ return c.enc.Encode(&c.req) |
+} |
+ |
+type clientResponse struct { |
+ Id uint64 "id" |
+ Result *json.RawMessage "result" |
+ Error string "error" |
+} |
+ |
+func (r *clientResponse) reset() { |
+ r.Id = 0 |
+ r.Result = nil |
+ r.Error = "" |
+} |
+ |
+func (c *clientCodec) ReadResponseHeader(r *rpc.Response) os.Error { |
+ c.resp.reset() |
+ if err := c.dec.Decode(&c.resp); err != nil { |
+ return err |
+ } |
+ |
+ c.mutex.Lock() |
+ r.ServiceMethod = c.pending[c.resp.Id] |
+ c.pending[c.resp.Id] = "", false |
+ c.mutex.Unlock() |
+ |
+ r.Seq = c.resp.Id |
+ r.Error = c.resp.Error |
+ return nil |
+} |
+ |
+func (c *clientCodec) ReadResponseBody(x interface{}) os.Error { |
+ return json.Unmarshal(*c.resp.Result, x) |
+} |
+ |
+func (c *clientCodec) Close() os.Error { |
+ return c.c.Close() |
+} |
+ |
+// NewClient returns a new rpc.Client to handle requests to the |
+// set of services at the other end of the connection. |
+func NewClient(conn io.ReadWriteCloser) *rpc.Client { |
+ return rpc.NewClientWithCodec(NewClientCodec(conn)) |
+} |
+ |
+// Dial connects to a JSON-RPC server at the specified network address. |
+func Dial(network, address string) (*rpc.Client, os.Error) { |
+ conn, err := net.Dial(network, "", address) |
+ if err != nil { |
+ return nil, err |
+ } |
+ return NewClient(conn), err |
+} |