1// Copyright 2018 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 jsonrpc2
6
7import (
8	"encoding/json"
9	"fmt"
10	"math"
11)
12
13// this file contains the go forms of the wire specification
14// see http://www.jsonrpc.org/specification for details
15
16var (
17	// ErrUnknown should be used for all non coded errors.
18	ErrUnknown = NewError(-32001, "JSON RPC unknown error")
19	// ErrParse is used when invalid JSON was received by the server.
20	ErrParse = NewError(-32700, "JSON RPC parse error")
21	//ErrInvalidRequest is used when the JSON sent is not a valid Request object.
22	ErrInvalidRequest = NewError(-32600, "JSON RPC invalid request")
23	// ErrMethodNotFound should be returned by the handler when the method does
24	// not exist / is not available.
25	ErrMethodNotFound = NewError(-32601, "JSON RPC method not found")
26	// ErrInvalidParams should be returned by the handler when method
27	// parameter(s) were invalid.
28	ErrInvalidParams = NewError(-32602, "JSON RPC invalid params")
29	// ErrInternal is not currently returned but defined for completeness.
30	ErrInternal = NewError(-32603, "JSON RPC internal error")
31
32	//ErrServerOverloaded is returned when a message was refused due to a
33	//server being temporarily unable to accept any new messages.
34	ErrServerOverloaded = NewError(-32000, "JSON RPC overloaded")
35)
36
37// wireRequest is sent to a server to represent a Call or Notify operaton.
38type wireRequest struct {
39	// VersionTag is always encoded as the string "2.0"
40	VersionTag wireVersionTag `json:"jsonrpc"`
41	// Method is a string containing the method name to invoke.
42	Method string `json:"method"`
43	// Params is either a struct or an array with the parameters of the method.
44	Params *json.RawMessage `json:"params,omitempty"`
45	// The id of this request, used to tie the Response back to the request.
46	// Will be either a string or a number. If not set, the Request is a notify,
47	// and no response is possible.
48	ID *ID `json:"id,omitempty"`
49}
50
51// WireResponse is a reply to a Request.
52// It will always have the ID field set to tie it back to a request, and will
53// have either the Result or Error fields set depending on whether it is a
54// success or failure response.
55type wireResponse struct {
56	// VersionTag is always encoded as the string "2.0"
57	VersionTag wireVersionTag `json:"jsonrpc"`
58	// Result is the response value, and is required on success.
59	Result *json.RawMessage `json:"result,omitempty"`
60	// Error is a structured error response if the call fails.
61	Error *wireError `json:"error,omitempty"`
62	// ID must be set and is the identifier of the Request this is a response to.
63	ID *ID `json:"id,omitempty"`
64}
65
66// wireCombined has all the fields of both Request and Response.
67// We can decode this and then work out which it is.
68type wireCombined struct {
69	VersionTag wireVersionTag   `json:"jsonrpc"`
70	ID         *ID              `json:"id,omitempty"`
71	Method     string           `json:"method"`
72	Params     *json.RawMessage `json:"params,omitempty"`
73	Result     *json.RawMessage `json:"result,omitempty"`
74	Error      *wireError       `json:"error,omitempty"`
75}
76
77// wireError represents a structured error in a Response.
78type wireError struct {
79	// Code is an error code indicating the type of failure.
80	Code int64 `json:"code"`
81	// Message is a short description of the error.
82	Message string `json:"message"`
83	// Data is optional structured data containing additional information about the error.
84	Data *json.RawMessage `json:"data,omitempty"`
85}
86
87// wireVersionTag is a special 0 sized struct that encodes as the jsonrpc version
88// tag.
89// It will fail during decode if it is not the correct version tag in the
90// stream.
91type wireVersionTag struct{}
92
93// ID is a Request identifier.
94type ID struct {
95	name   string
96	number int64
97}
98
99func NewError(code int64, message string) error {
100	return &wireError{
101		Code:    code,
102		Message: message,
103	}
104}
105
106func (err *wireError) Error() string {
107	return err.Message
108}
109
110func (wireVersionTag) MarshalJSON() ([]byte, error) {
111	return json.Marshal("2.0")
112}
113
114func (wireVersionTag) UnmarshalJSON(data []byte) error {
115	version := ""
116	if err := json.Unmarshal(data, &version); err != nil {
117		return err
118	}
119	if version != "2.0" {
120		return fmt.Errorf("invalid RPC version %v", version)
121	}
122	return nil
123}
124
125const invalidID int64 = math.MaxInt64
126
127// NewIntID returns a new numerical request ID.
128func NewIntID(v int64) ID { return ID{number: v} }
129
130// NewStringID returns a new string request ID.
131func NewStringID(v string) ID { return ID{name: v} }
132
133// Format writes the ID to the formatter.
134// If the rune is q the representation is non ambiguous,
135// string forms are quoted, number forms are preceded by a #
136func (id ID) Format(f fmt.State, r rune) {
137	numF, strF := `%d`, `%s`
138	if r == 'q' {
139		numF, strF = `#%d`, `%q`
140	}
141	switch {
142	case id.name != "":
143		fmt.Fprintf(f, strF, id.name)
144	default:
145		fmt.Fprintf(f, numF, id.number)
146	}
147}
148
149func (id *ID) MarshalJSON() ([]byte, error) {
150	if id.name != "" {
151		return json.Marshal(id.name)
152	}
153	return json.Marshal(id.number)
154}
155
156func (id *ID) UnmarshalJSON(data []byte) error {
157	*id = ID{}
158	if err := json.Unmarshal(data, &id.number); err == nil {
159		return nil
160	}
161	return json.Unmarshal(data, &id.name)
162}
163