...
Run Format

Source file src/net/rpc/jsonrpc/client.go

Documentation: net/rpc/jsonrpc

  // 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 1.0 ClientCodec and ServerCodec
  // for the rpc package.
  // For JSON-RPC 2.0 support, see https://godoc.org/?q=json-rpc+2.0
  package jsonrpc
  
  import (
  	"encoding/json"
  	"fmt"
  	"io"
  	"net"
  	"net/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         `json:"method"`
  	Params [1]interface{} `json:"params"`
  	Id     uint64         `json:"id"`
  }
  
  func (c *clientCodec) WriteRequest(r *rpc.Request, param interface{}) 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           `json:"id"`
  	Result *json.RawMessage `json:"result"`
  	Error  interface{}      `json:"error"`
  }
  
  func (r *clientResponse) reset() {
  	r.Id = 0
  	r.Result = nil
  	r.Error = nil
  }
  
  func (c *clientCodec) ReadResponseHeader(r *rpc.Response) 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]
  	delete(c.pending, c.resp.Id)
  	c.mutex.Unlock()
  
  	r.Error = ""
  	r.Seq = c.resp.Id
  	if c.resp.Error != nil || c.resp.Result == nil {
  		x, ok := c.resp.Error.(string)
  		if !ok {
  			return fmt.Errorf("invalid error %v", c.resp.Error)
  		}
  		if x == "" {
  			x = "unspecified error"
  		}
  		r.Error = x
  	}
  	return nil
  }
  
  func (c *clientCodec) ReadResponseBody(x interface{}) error {
  	if x == nil {
  		return nil
  	}
  	return json.Unmarshal(*c.resp.Result, x)
  }
  
  func (c *clientCodec) Close() 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, error) {
  	conn, err := net.Dial(network, address)
  	if err != nil {
  		return nil, err
  	}
  	return NewClient(conn), err
  }
  

View as plain text