359 lines
8.3 KiB
Go
359 lines
8.3 KiB
Go
|
// SPDX-FileCopyrightText: 2021 The Go Language Server Authors
|
||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||
|
|
||
|
package jsonrpc2
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
|
||
|
"github.com/segmentio/encoding/json"
|
||
|
)
|
||
|
|
||
|
// Message is the interface to all JSON-RPC message types.
|
||
|
//
|
||
|
// They share no common functionality, but are a closed set of concrete types
|
||
|
// that are allowed to implement this interface.
|
||
|
//
|
||
|
// The message types are *Call, *Response and *Notification.
|
||
|
type Message interface {
|
||
|
// jsonrpc2Message is used to make the set of message implementations a
|
||
|
// closed set.
|
||
|
jsonrpc2Message()
|
||
|
}
|
||
|
|
||
|
// Request is the shared interface to jsonrpc2 messages that request
|
||
|
// a method be invoked.
|
||
|
//
|
||
|
// The request types are a closed set of *Call and *Notification.
|
||
|
type Request interface {
|
||
|
Message
|
||
|
|
||
|
// Method is a string containing the method name to invoke.
|
||
|
Method() string
|
||
|
// Params is either a struct or an array with the parameters of the method.
|
||
|
Params() json.RawMessage
|
||
|
|
||
|
// jsonrpc2Request is used to make the set of request implementations closed.
|
||
|
jsonrpc2Request()
|
||
|
}
|
||
|
|
||
|
// Call is a request that expects a response.
|
||
|
//
|
||
|
// The response will have a matching ID.
|
||
|
type Call struct {
|
||
|
// Method is a string containing the method name to invoke.
|
||
|
method string
|
||
|
// Params is either a struct or an array with the parameters of the method.
|
||
|
params json.RawMessage
|
||
|
// id of this request, used to tie the Response back to the request.
|
||
|
id ID
|
||
|
}
|
||
|
|
||
|
// make sure a Call implements the Request, json.Marshaler and json.Unmarshaler and interfaces.
|
||
|
var (
|
||
|
_ Request = (*Call)(nil)
|
||
|
_ json.Marshaler = (*Call)(nil)
|
||
|
_ json.Unmarshaler = (*Call)(nil)
|
||
|
)
|
||
|
|
||
|
// NewCall constructs a new Call message for the supplied ID, method and
|
||
|
// parameters.
|
||
|
func NewCall(id ID, method string, params interface{}) (*Call, error) {
|
||
|
p, merr := marshalInterface(params)
|
||
|
req := &Call{
|
||
|
id: id,
|
||
|
method: method,
|
||
|
params: p,
|
||
|
}
|
||
|
return req, merr
|
||
|
}
|
||
|
|
||
|
// ID returns the current call id.
|
||
|
func (c *Call) ID() ID { return c.id }
|
||
|
|
||
|
// Method implements Request.
|
||
|
func (c *Call) Method() string { return c.method }
|
||
|
|
||
|
// Params implements Request.
|
||
|
func (c *Call) Params() json.RawMessage { return c.params }
|
||
|
|
||
|
// jsonrpc2Message implements Request.
|
||
|
func (Call) jsonrpc2Message() {}
|
||
|
|
||
|
// jsonrpc2Request implements Request.
|
||
|
func (Call) jsonrpc2Request() {}
|
||
|
|
||
|
// MarshalJSON implements json.Marshaler.
|
||
|
func (c Call) MarshalJSON() ([]byte, error) {
|
||
|
req := wireRequest{
|
||
|
Method: c.method,
|
||
|
Params: &c.params,
|
||
|
ID: &c.id,
|
||
|
}
|
||
|
data, err := json.Marshal(req)
|
||
|
if err != nil {
|
||
|
return data, fmt.Errorf("marshaling call: %w", err)
|
||
|
}
|
||
|
|
||
|
return data, nil
|
||
|
}
|
||
|
|
||
|
// UnmarshalJSON implements json.Unmarshaler.
|
||
|
func (c *Call) UnmarshalJSON(data []byte) error {
|
||
|
var req wireRequest
|
||
|
dec := json.NewDecoder(bytes.NewReader(data))
|
||
|
dec.ZeroCopy()
|
||
|
if err := dec.Decode(&req); err != nil {
|
||
|
return fmt.Errorf("unmarshaling call: %w", err)
|
||
|
}
|
||
|
|
||
|
c.method = req.Method
|
||
|
if req.Params != nil {
|
||
|
c.params = *req.Params
|
||
|
}
|
||
|
if req.ID != nil {
|
||
|
c.id = *req.ID
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Response is a reply to a Request.
|
||
|
//
|
||
|
// It will have the same ID as the call it is a response to.
|
||
|
type Response struct {
|
||
|
// result is the content of the response.
|
||
|
result json.RawMessage
|
||
|
// err is set only if the call failed.
|
||
|
err error
|
||
|
// ID of the request this is a response to.
|
||
|
id ID
|
||
|
}
|
||
|
|
||
|
// make sure a Response implements the Message, json.Marshaler and json.Unmarshaler and interfaces.
|
||
|
var (
|
||
|
_ Message = (*Response)(nil)
|
||
|
_ json.Marshaler = (*Response)(nil)
|
||
|
_ json.Unmarshaler = (*Response)(nil)
|
||
|
)
|
||
|
|
||
|
// NewResponse constructs a new Response message that is a reply to the
|
||
|
// supplied. If err is set result may be ignored.
|
||
|
func NewResponse(id ID, result interface{}, err error) (*Response, error) {
|
||
|
r, merr := marshalInterface(result)
|
||
|
resp := &Response{
|
||
|
id: id,
|
||
|
result: r,
|
||
|
err: err,
|
||
|
}
|
||
|
return resp, merr
|
||
|
}
|
||
|
|
||
|
// ID returns the current response id.
|
||
|
func (r *Response) ID() ID { return r.id }
|
||
|
|
||
|
// Result returns the Response result.
|
||
|
func (r *Response) Result() json.RawMessage { return r.result }
|
||
|
|
||
|
// Err returns the Response error.
|
||
|
func (r *Response) Err() error { return r.err }
|
||
|
|
||
|
// jsonrpc2Message implements Message.
|
||
|
func (r *Response) jsonrpc2Message() {}
|
||
|
|
||
|
// MarshalJSON implements json.Marshaler.
|
||
|
func (r Response) MarshalJSON() ([]byte, error) {
|
||
|
resp := &wireResponse{
|
||
|
Error: toError(r.err),
|
||
|
ID: &r.id,
|
||
|
}
|
||
|
if resp.Error == nil {
|
||
|
resp.Result = &r.result
|
||
|
}
|
||
|
|
||
|
data, err := json.Marshal(resp)
|
||
|
if err != nil {
|
||
|
return data, fmt.Errorf("marshaling notification: %w", err)
|
||
|
}
|
||
|
|
||
|
return data, nil
|
||
|
}
|
||
|
|
||
|
// UnmarshalJSON implements json.Unmarshaler.
|
||
|
func (r *Response) UnmarshalJSON(data []byte) error {
|
||
|
var resp wireResponse
|
||
|
dec := json.NewDecoder(bytes.NewReader(data))
|
||
|
dec.ZeroCopy()
|
||
|
if err := dec.Decode(&resp); err != nil {
|
||
|
return fmt.Errorf("unmarshaling jsonrpc response: %w", err)
|
||
|
}
|
||
|
|
||
|
if resp.Result != nil {
|
||
|
r.result = *resp.Result
|
||
|
}
|
||
|
if resp.Error != nil {
|
||
|
r.err = resp.Error
|
||
|
}
|
||
|
if resp.ID != nil {
|
||
|
r.id = *resp.ID
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func toError(err error) *Error {
|
||
|
if err == nil {
|
||
|
// no error, the response is complete
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
var wrapped *Error
|
||
|
if errors.As(err, &wrapped) {
|
||
|
// already a wire error, just use it
|
||
|
return wrapped
|
||
|
}
|
||
|
|
||
|
result := &Error{Message: err.Error()}
|
||
|
if errors.As(err, &wrapped) {
|
||
|
// if we wrapped a wire error, keep the code from the wrapped error
|
||
|
// but the message from the outer error
|
||
|
result.Code = wrapped.Code
|
||
|
}
|
||
|
|
||
|
return result
|
||
|
}
|
||
|
|
||
|
// Notification is a request for which a response cannot occur, and as such
|
||
|
// it has not ID.
|
||
|
type Notification struct {
|
||
|
// Method is a string containing the method name to invoke.
|
||
|
method string
|
||
|
|
||
|
params json.RawMessage
|
||
|
}
|
||
|
|
||
|
// make sure a Notification implements the Request, json.Marshaler and json.Unmarshaler and interfaces.
|
||
|
var (
|
||
|
_ Request = (*Notification)(nil)
|
||
|
_ json.Marshaler = (*Notification)(nil)
|
||
|
_ json.Unmarshaler = (*Notification)(nil)
|
||
|
)
|
||
|
|
||
|
// NewNotification constructs a new Notification message for the supplied
|
||
|
// method and parameters.
|
||
|
func NewNotification(method string, params interface{}) (*Notification, error) {
|
||
|
p, merr := marshalInterface(params)
|
||
|
notify := &Notification{
|
||
|
method: method,
|
||
|
params: p,
|
||
|
}
|
||
|
return notify, merr
|
||
|
}
|
||
|
|
||
|
// Method implements Request.
|
||
|
func (n *Notification) Method() string { return n.method }
|
||
|
|
||
|
// Params implements Request.
|
||
|
func (n *Notification) Params() json.RawMessage { return n.params }
|
||
|
|
||
|
// jsonrpc2Message implements Request.
|
||
|
func (Notification) jsonrpc2Message() {}
|
||
|
|
||
|
// jsonrpc2Request implements Request.
|
||
|
func (Notification) jsonrpc2Request() {}
|
||
|
|
||
|
// MarshalJSON implements json.Marshaler.
|
||
|
func (n Notification) MarshalJSON() ([]byte, error) {
|
||
|
req := wireRequest{
|
||
|
Method: n.method,
|
||
|
Params: &n.params,
|
||
|
}
|
||
|
data, err := json.Marshal(req)
|
||
|
if err != nil {
|
||
|
return data, fmt.Errorf("marshaling notification: %w", err)
|
||
|
}
|
||
|
|
||
|
return data, nil
|
||
|
}
|
||
|
|
||
|
// UnmarshalJSON implements json.Unmarshaler.
|
||
|
func (n *Notification) UnmarshalJSON(data []byte) error {
|
||
|
var req wireRequest
|
||
|
dec := json.NewDecoder(bytes.NewReader(data))
|
||
|
dec.ZeroCopy()
|
||
|
if err := dec.Decode(&req); err != nil {
|
||
|
return fmt.Errorf("unmarshaling notification: %w", err)
|
||
|
}
|
||
|
|
||
|
n.method = req.Method
|
||
|
if req.Params != nil {
|
||
|
n.params = *req.Params
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// DecodeMessage decodes data to Message.
|
||
|
func DecodeMessage(data []byte) (Message, error) {
|
||
|
var msg combined
|
||
|
dec := json.NewDecoder(bytes.NewReader(data))
|
||
|
dec.ZeroCopy()
|
||
|
if err := dec.Decode(&msg); err != nil {
|
||
|
return nil, fmt.Errorf("unmarshaling jsonrpc message: %w", err)
|
||
|
}
|
||
|
|
||
|
if msg.Method == "" {
|
||
|
// no method, should be a response
|
||
|
if msg.ID == nil {
|
||
|
return nil, ErrInvalidRequest
|
||
|
}
|
||
|
|
||
|
resp := &Response{
|
||
|
id: *msg.ID,
|
||
|
}
|
||
|
if msg.Error != nil {
|
||
|
resp.err = msg.Error
|
||
|
}
|
||
|
if msg.Result != nil {
|
||
|
resp.result = *msg.Result
|
||
|
}
|
||
|
|
||
|
return resp, nil
|
||
|
}
|
||
|
|
||
|
// has a method, must be a request
|
||
|
if msg.ID == nil {
|
||
|
// request with no ID is a notify
|
||
|
notify := &Notification{
|
||
|
method: msg.Method,
|
||
|
}
|
||
|
if msg.Params != nil {
|
||
|
notify.params = *msg.Params
|
||
|
}
|
||
|
|
||
|
return notify, nil
|
||
|
}
|
||
|
|
||
|
// request with an ID, must be a call
|
||
|
call := &Call{
|
||
|
method: msg.Method,
|
||
|
id: *msg.ID,
|
||
|
}
|
||
|
if msg.Params != nil {
|
||
|
call.params = *msg.Params
|
||
|
}
|
||
|
|
||
|
return call, nil
|
||
|
}
|
||
|
|
||
|
// marshalInterface marshal obj to json.RawMessage.
|
||
|
func marshalInterface(obj interface{}) (json.RawMessage, error) {
|
||
|
data, err := json.Marshal(obj)
|
||
|
if err != nil {
|
||
|
return json.RawMessage{}, fmt.Errorf("failed to marshal json: %w", err)
|
||
|
}
|
||
|
return json.RawMessage(data), nil
|
||
|
}
|