src/pkg/net/rpc/jsonrpc/client.go - The Go Programming Language

Golang

Source file src/pkg/net/rpc/jsonrpc/client.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 implements a JSON-RPC ClientCodec and ServerCodec
     6	// for the rpc package.
     7	package jsonrpc
     8	
     9	import (
    10		"encoding/json"
    11		"fmt"
    12		"io"
    13		"net"
    14		"net/rpc"
    15		"sync"
    16	)
    17	
    18	type clientCodec struct {
    19		dec *json.Decoder // for reading JSON values
    20		enc *json.Encoder // for writing JSON values
    21		c   io.Closer
    22	
    23		// temporary work space
    24		req  clientRequest
    25		resp clientResponse
    26	
    27		// JSON-RPC responses include the request id but not the request method.
    28		// Package rpc expects both.
    29		// We save the request method in pending when sending a request
    30		// and then look it up by request ID when filling out the rpc Response.
    31		mutex   sync.Mutex        // protects pending
    32		pending map[uint64]string // map request id to method name
    33	}
    34	
    35	// NewClientCodec returns a new rpc.ClientCodec using JSON-RPC on conn.
    36	func NewClientCodec(conn io.ReadWriteCloser) rpc.ClientCodec {
    37		return &clientCodec{
    38			dec:     json.NewDecoder(conn),
    39			enc:     json.NewEncoder(conn),
    40			c:       conn,
    41			pending: make(map[uint64]string),
    42		}
    43	}
    44	
    45	type clientRequest struct {
    46		Method string         `json:"method"`
    47		Params [1]interface{} `json:"params"`
    48		Id     uint64         `json:"id"`
    49	}
    50	
    51	func (c *clientCodec) WriteRequest(r *rpc.Request, param interface{}) error {
    52		c.mutex.Lock()
    53		c.pending[r.Seq] = r.ServiceMethod
    54		c.mutex.Unlock()
    55		c.req.Method = r.ServiceMethod
    56		c.req.Params[0] = param
    57		c.req.Id = r.Seq
    58		return c.enc.Encode(&c.req)
    59	}
    60	
    61	type clientResponse struct {
    62		Id     uint64           `json:"id"`
    63		Result *json.RawMessage `json:"result"`
    64		Error  interface{}      `json:"error"`
    65	}
    66	
    67	func (r *clientResponse) reset() {
    68		r.Id = 0
    69		r.Result = nil
    70		r.Error = nil
    71	}
    72	
    73	func (c *clientCodec) ReadResponseHeader(r *rpc.Response) error {
    74		c.resp.reset()
    75		if err := c.dec.Decode(&c.resp); err != nil {
    76			return err
    77		}
    78	
    79		c.mutex.Lock()
    80		r.ServiceMethod = c.pending[c.resp.Id]
    81		delete(c.pending, c.resp.Id)
    82		c.mutex.Unlock()
    83	
    84		r.Error = ""
    85		r.Seq = c.resp.Id
    86		if c.resp.Error != nil {
    87			x, ok := c.resp.Error.(string)
    88			if !ok {
    89				return fmt.Errorf("invalid error %v", c.resp.Error)
    90			}
    91			if x == "" {
    92				x = "unspecified error"
    93			}
    94			r.Error = x
    95		}
    96		return nil
    97	}
    98	
    99	func (c *clientCodec) ReadResponseBody(x interface{}) error {
   100		if x == nil {
   101			return nil
   102		}
   103		return json.Unmarshal(*c.resp.Result, x)
   104	}
   105	
   106	func (c *clientCodec) Close() error {
   107		return c.c.Close()
   108	}
   109	
   110	// NewClient returns a new rpc.Client to handle requests to the
   111	// set of services at the other end of the connection.
   112	func NewClient(conn io.ReadWriteCloser) *rpc.Client {
   113		return rpc.NewClientWithCodec(NewClientCodec(conn))
   114	}
   115	
   116	// Dial connects to a JSON-RPC server at the specified network address.
   117	func Dial(network, address string) (*rpc.Client, error) {
   118		conn, err := net.Dial(network, address)
   119		if err != nil {
   120			return nil, err
   121		}
   122		return NewClient(conn), err
   123	}