Home | History | Annotate | Download | only in jsonrpc
      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
      6 
      7 import (
      8 	"encoding/json"
      9 	"errors"
     10 	"io"
     11 	"net/rpc"
     12 	"sync"
     13 )
     14 
     15 var errMissingParams = errors.New("jsonrpc: request body missing params")
     16 
     17 type serverCodec struct {
     18 	dec *json.Decoder // for reading JSON values
     19 	enc *json.Encoder // for writing JSON values
     20 	c   io.Closer
     21 
     22 	// temporary work space
     23 	req serverRequest
     24 
     25 	// JSON-RPC clients can use arbitrary json values as request IDs.
     26 	// Package rpc expects uint64 request IDs.
     27 	// We assign uint64 sequence numbers to incoming requests
     28 	// but save the original request ID in the pending map.
     29 	// When rpc responds, we use the sequence number in
     30 	// the response to find the original request ID.
     31 	mutex   sync.Mutex // protects seq, pending
     32 	seq     uint64
     33 	pending map[uint64]*json.RawMessage
     34 }
     35 
     36 // NewServerCodec returns a new rpc.ServerCodec using JSON-RPC on conn.
     37 func NewServerCodec(conn io.ReadWriteCloser) rpc.ServerCodec {
     38 	return &serverCodec{
     39 		dec:     json.NewDecoder(conn),
     40 		enc:     json.NewEncoder(conn),
     41 		c:       conn,
     42 		pending: make(map[uint64]*json.RawMessage),
     43 	}
     44 }
     45 
     46 type serverRequest struct {
     47 	Method string           `json:"method"`
     48 	Params *json.RawMessage `json:"params"`
     49 	Id     *json.RawMessage `json:"id"`
     50 }
     51 
     52 func (r *serverRequest) reset() {
     53 	r.Method = ""
     54 	r.Params = nil
     55 	r.Id = nil
     56 }
     57 
     58 type serverResponse struct {
     59 	Id     *json.RawMessage `json:"id"`
     60 	Result interface{}      `json:"result"`
     61 	Error  interface{}      `json:"error"`
     62 }
     63 
     64 func (c *serverCodec) ReadRequestHeader(r *rpc.Request) error {
     65 	c.req.reset()
     66 	if err := c.dec.Decode(&c.req); err != nil {
     67 		return err
     68 	}
     69 	r.ServiceMethod = c.req.Method
     70 
     71 	// JSON request id can be any JSON value;
     72 	// RPC package expects uint64.  Translate to
     73 	// internal uint64 and save JSON on the side.
     74 	c.mutex.Lock()
     75 	c.seq++
     76 	c.pending[c.seq] = c.req.Id
     77 	c.req.Id = nil
     78 	r.Seq = c.seq
     79 	c.mutex.Unlock()
     80 
     81 	return nil
     82 }
     83 
     84 func (c *serverCodec) ReadRequestBody(x interface{}) error {
     85 	if x == nil {
     86 		return nil
     87 	}
     88 	if c.req.Params == nil {
     89 		return errMissingParams
     90 	}
     91 	// JSON params is array value.
     92 	// RPC params is struct.
     93 	// Unmarshal into array containing struct for now.
     94 	// Should think about making RPC more general.
     95 	var params [1]interface{}
     96 	params[0] = x
     97 	return json.Unmarshal(*c.req.Params, &params)
     98 }
     99 
    100 var null = json.RawMessage([]byte("null"))
    101 
    102 func (c *serverCodec) WriteResponse(r *rpc.Response, x interface{}) error {
    103 	c.mutex.Lock()
    104 	b, ok := c.pending[r.Seq]
    105 	if !ok {
    106 		c.mutex.Unlock()
    107 		return errors.New("invalid sequence number in response")
    108 	}
    109 	delete(c.pending, r.Seq)
    110 	c.mutex.Unlock()
    111 
    112 	if b == nil {
    113 		// Invalid request so no id.  Use JSON null.
    114 		b = &null
    115 	}
    116 	resp := serverResponse{Id: b}
    117 	if r.Error == "" {
    118 		resp.Result = x
    119 	} else {
    120 		resp.Error = r.Error
    121 	}
    122 	return c.enc.Encode(resp)
    123 }
    124 
    125 func (c *serverCodec) Close() error {
    126 	return c.c.Close()
    127 }
    128 
    129 // ServeConn runs the JSON-RPC server on a single connection.
    130 // ServeConn blocks, serving the connection until the client hangs up.
    131 // The caller typically invokes ServeConn in a go statement.
    132 func ServeConn(conn io.ReadWriteCloser) {
    133 	rpc.ServeCodec(NewServerCodec(conn))
    134 }
    135