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 | |
5 | package jsonrpc2 |
6 | |
7 | import ( |
8 | "encoding/json" |
9 | "errors" |
10 | "fmt" |
11 | ) |
12 | |
13 | // Message is the interface to all jsonrpc2 message types. |
14 | // They share no common functionality, but are a closed set of concrete types |
15 | // that are allowed to implement this interface. The message types are *Call, |
16 | // *Notification and *Response. |
17 | type Message interface { |
18 | // isJSONRPC2Message is used to make the set of message implementations a |
19 | // closed set. |
20 | isJSONRPC2Message() |
21 | } |
22 | |
23 | // Request is the shared interface to jsonrpc2 messages that request |
24 | // a method be invoked. |
25 | // The request types are a closed set of *Call and *Notification. |
26 | type Request interface { |
27 | Message |
28 | // Method is a string containing the method name to invoke. |
29 | Method() string |
30 | // Params is either a struct or an array with the parameters of the method. |
31 | Params() json.RawMessage |
32 | // isJSONRPC2Request is used to make the set of request implementations closed. |
33 | isJSONRPC2Request() |
34 | } |
35 | |
36 | // Notification is a request for which a response cannot occur, and as such |
37 | // it has not ID. |
38 | type Notification struct { |
39 | // Method is a string containing the method name to invoke. |
40 | method string |
41 | params json.RawMessage |
42 | } |
43 | |
44 | // Call is a request that expects a response. |
45 | // The response will have a matching ID. |
46 | type Call struct { |
47 | // Method is a string containing the method name to invoke. |
48 | method string |
49 | // Params is either a struct or an array with the parameters of the method. |
50 | params json.RawMessage |
51 | // id of this request, used to tie the Response back to the request. |
52 | id ID |
53 | } |
54 | |
55 | // Response is a reply to a Call. |
56 | // It will have the same ID as the call it is a response to. |
57 | type Response struct { |
58 | // result is the content of the response. |
59 | result json.RawMessage |
60 | // err is set only if the call failed. |
61 | err error |
62 | // ID of the request this is a response to. |
63 | id ID |
64 | } |
65 | |
66 | // NewNotification constructs a new Notification message for the supplied |
67 | // method and parameters. |
68 | func NewNotification(method string, params interface{}) (*Notification, error) { |
69 | p, merr := marshalToRaw(params) |
70 | return &Notification{method: method, params: p}, merr |
71 | } |
72 | |
73 | func (msg *Notification) Method() string { return msg.method } |
74 | func (msg *Notification) Params() json.RawMessage { return msg.params } |
75 | func (msg *Notification) isJSONRPC2Message() {} |
76 | func (msg *Notification) isJSONRPC2Request() {} |
77 | |
78 | func (n *Notification) MarshalJSON() ([]byte, error) { |
79 | msg := wireRequest{Method: n.method, Params: &n.params} |
80 | data, err := json.Marshal(msg) |
81 | if err != nil { |
82 | return data, fmt.Errorf("marshaling notification: %w", err) |
83 | } |
84 | return data, nil |
85 | } |
86 | |
87 | func (n *Notification) UnmarshalJSON(data []byte) error { |
88 | msg := wireRequest{} |
89 | if err := json.Unmarshal(data, &msg); err != nil { |
90 | return fmt.Errorf("unmarshaling notification: %w", err) |
91 | } |
92 | n.method = msg.Method |
93 | if msg.Params != nil { |
94 | n.params = *msg.Params |
95 | } |
96 | return nil |
97 | } |
98 | |
99 | // NewCall constructs a new Call message for the supplied ID, method and |
100 | // parameters. |
101 | func NewCall(id ID, method string, params interface{}) (*Call, error) { |
102 | p, merr := marshalToRaw(params) |
103 | return &Call{id: id, method: method, params: p}, merr |
104 | } |
105 | |
106 | func (msg *Call) Method() string { return msg.method } |
107 | func (msg *Call) Params() json.RawMessage { return msg.params } |
108 | func (msg *Call) ID() ID { return msg.id } |
109 | func (msg *Call) isJSONRPC2Message() {} |
110 | func (msg *Call) isJSONRPC2Request() {} |
111 | |
112 | func (c *Call) MarshalJSON() ([]byte, error) { |
113 | msg := wireRequest{Method: c.method, Params: &c.params, ID: &c.id} |
114 | data, err := json.Marshal(msg) |
115 | if err != nil { |
116 | return data, fmt.Errorf("marshaling call: %w", err) |
117 | } |
118 | return data, nil |
119 | } |
120 | |
121 | func (c *Call) UnmarshalJSON(data []byte) error { |
122 | msg := wireRequest{} |
123 | if err := json.Unmarshal(data, &msg); err != nil { |
124 | return fmt.Errorf("unmarshaling call: %w", err) |
125 | } |
126 | c.method = msg.Method |
127 | if msg.Params != nil { |
128 | c.params = *msg.Params |
129 | } |
130 | if msg.ID != nil { |
131 | c.id = *msg.ID |
132 | } |
133 | return nil |
134 | } |
135 | |
136 | // NewResponse constructs a new Response message that is a reply to the |
137 | // supplied. If err is set result may be ignored. |
138 | func NewResponse(id ID, result interface{}, err error) (*Response, error) { |
139 | r, merr := marshalToRaw(result) |
140 | return &Response{id: id, result: r, err: err}, merr |
141 | } |
142 | |
143 | func (msg *Response) ID() ID { return msg.id } |
144 | func (msg *Response) Result() json.RawMessage { return msg.result } |
145 | func (msg *Response) Err() error { return msg.err } |
146 | func (msg *Response) isJSONRPC2Message() {} |
147 | |
148 | func (r *Response) MarshalJSON() ([]byte, error) { |
149 | msg := &wireResponse{Error: toWireError(r.err), ID: &r.id} |
150 | if msg.Error == nil { |
151 | msg.Result = &r.result |
152 | } |
153 | data, err := json.Marshal(msg) |
154 | if err != nil { |
155 | return data, fmt.Errorf("marshaling notification: %w", err) |
156 | } |
157 | return data, nil |
158 | } |
159 | |
160 | func toWireError(err error) *wireError { |
161 | if err == nil { |
162 | // no error, the response is complete |
163 | return nil |
164 | } |
165 | if err, ok := err.(*wireError); ok { |
166 | // already a wire error, just use it |
167 | return err |
168 | } |
169 | result := &wireError{Message: err.Error()} |
170 | var wrapped *wireError |
171 | if errors.As(err, &wrapped) { |
172 | // if we wrapped a wire error, keep the code from the wrapped error |
173 | // but the message from the outer error |
174 | result.Code = wrapped.Code |
175 | } |
176 | return result |
177 | } |
178 | |
179 | func (r *Response) UnmarshalJSON(data []byte) error { |
180 | msg := wireResponse{} |
181 | if err := json.Unmarshal(data, &msg); err != nil { |
182 | return fmt.Errorf("unmarshaling jsonrpc response: %w", err) |
183 | } |
184 | if msg.Result != nil { |
185 | r.result = *msg.Result |
186 | } |
187 | if msg.Error != nil { |
188 | r.err = msg.Error |
189 | } |
190 | if msg.ID != nil { |
191 | r.id = *msg.ID |
192 | } |
193 | return nil |
194 | } |
195 | |
196 | func DecodeMessage(data []byte) (Message, error) { |
197 | msg := wireCombined{} |
198 | if err := json.Unmarshal(data, &msg); err != nil { |
199 | return nil, fmt.Errorf("unmarshaling jsonrpc message: %w", err) |
200 | } |
201 | if msg.Method == "" { |
202 | // no method, should be a response |
203 | if msg.ID == nil { |
204 | return nil, ErrInvalidRequest |
205 | } |
206 | response := &Response{id: *msg.ID} |
207 | if msg.Error != nil { |
208 | response.err = msg.Error |
209 | } |
210 | if msg.Result != nil { |
211 | response.result = *msg.Result |
212 | } |
213 | return response, nil |
214 | } |
215 | // has a method, must be a request |
216 | if msg.ID == nil { |
217 | // request with no ID is a notify |
218 | notify := &Notification{method: msg.Method} |
219 | if msg.Params != nil { |
220 | notify.params = *msg.Params |
221 | } |
222 | return notify, nil |
223 | } |
224 | // request with an ID, must be a call |
225 | call := &Call{method: msg.Method, id: *msg.ID} |
226 | if msg.Params != nil { |
227 | call.params = *msg.Params |
228 | } |
229 | return call, nil |
230 | } |
231 | |
232 | func marshalToRaw(obj interface{}) (json.RawMessage, error) { |
233 | data, err := json.Marshal(obj) |
234 | if err != nil { |
235 | return json.RawMessage{}, err |
236 | } |
237 | return json.RawMessage(data), nil |
238 | } |
239 |
Members