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
5package jsonrpc
6
7import (
8	"encoding/json"
9	"errors"
10	"io"
11	"net/rpc"
12	"sync"
13)
14
15var errMissingParams = errors.New("jsonrpc: request body missing params")
16
17type 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.
37func 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
46type serverRequest struct {
47	Method string           `json:"method"`
48	Params *json.RawMessage `json:"params"`
49	Id     *json.RawMessage `json:"id"`
50}
51
52func (r *serverRequest) reset() {
53	r.Method = ""
54	r.Params = nil
55	r.Id = nil
56}
57
58type serverResponse struct {
59	Id     *json.RawMessage `json:"id"`
60	Result any              `json:"result"`
61	Error  any              `json:"error"`
62}
63
64func (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
84func (c *serverCodec) ReadRequestBody(x any) 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]any
96	params[0] = x
97	return json.Unmarshal(*c.req.Params, &params)
98}
99
100var null = json.RawMessage([]byte("null"))
101
102func (c *serverCodec) WriteResponse(r *rpc.Response, x any) 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
125func (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.
132func ServeConn(conn io.ReadWriteCloser) {
133	rpc.ServeCodec(NewServerCodec(conn))
134}
135