mirror of
https://github.com/golang/go
synced 2024-11-18 07:54:55 -07:00
dcbf2a9ed1
The spec states error data may be omitted. It is currently always encoded as null, despite having no usage. Omit the field if empty, and add a test to prove the behaviour. Fixes golang/go#39736 Change-Id: Icdb39409010f3a42f84d2372c2061e4bc7cc198e Reviewed-on: https://go-review.googlesource.com/c/tools/+/239059 Reviewed-by: Ian Cottrell <iancottrell@google.com>
163 lines
5.2 KiB
Go
163 lines
5.2 KiB
Go
// Copyright 2018 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package jsonrpc2
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
)
|
|
|
|
// this file contains the go forms of the wire specification
|
|
// see http://www.jsonrpc.org/specification for details
|
|
|
|
var (
|
|
// ErrUnknown should be used for all non coded errors.
|
|
ErrUnknown = NewError(-32001, "JSON RPC unknown error")
|
|
// ErrParse is used when invalid JSON was received by the server.
|
|
ErrParse = NewError(-32700, "JSON RPC parse error")
|
|
//ErrInvalidRequest is used when the JSON sent is not a valid Request object.
|
|
ErrInvalidRequest = NewError(-32600, "JSON RPC invalid request")
|
|
// ErrMethodNotFound should be returned by the handler when the method does
|
|
// not exist / is not available.
|
|
ErrMethodNotFound = NewError(-32601, "JSON RPC method not found")
|
|
// ErrInvalidParams should be returned by the handler when method
|
|
// parameter(s) were invalid.
|
|
ErrInvalidParams = NewError(-32602, "JSON RPC invalid params")
|
|
// ErrInternal is not currently returned but defined for completeness.
|
|
ErrInternal = NewError(-32603, "JSON RPC internal error")
|
|
|
|
//ErrServerOverloaded is returned when a message was refused due to a
|
|
//server being temporarily unable to accept any new messages.
|
|
ErrServerOverloaded = NewError(-32000, "JSON RPC overloaded")
|
|
)
|
|
|
|
// wireRequest is sent to a server to represent a Call or Notify operaton.
|
|
type wireRequest struct {
|
|
// VersionTag is always encoded as the string "2.0"
|
|
VersionTag wireVersionTag `json:"jsonrpc"`
|
|
// Method is a string containing the method name to invoke.
|
|
Method string `json:"method"`
|
|
// Params is either a struct or an array with the parameters of the method.
|
|
Params *json.RawMessage `json:"params,omitempty"`
|
|
// The id of this request, used to tie the Response back to the request.
|
|
// Will be either a string or a number. If not set, the Request is a notify,
|
|
// and no response is possible.
|
|
ID *ID `json:"id,omitempty"`
|
|
}
|
|
|
|
// WireResponse is a reply to a Request.
|
|
// It will always have the ID field set to tie it back to a request, and will
|
|
// have either the Result or Error fields set depending on whether it is a
|
|
// success or failure response.
|
|
type wireResponse struct {
|
|
// VersionTag is always encoded as the string "2.0"
|
|
VersionTag wireVersionTag `json:"jsonrpc"`
|
|
// Result is the response value, and is required on success.
|
|
Result *json.RawMessage `json:"result,omitempty"`
|
|
// Error is a structured error response if the call fails.
|
|
Error *wireError `json:"error,omitempty"`
|
|
// ID must be set and is the identifier of the Request this is a response to.
|
|
ID *ID `json:"id,omitempty"`
|
|
}
|
|
|
|
// wireCombined has all the fields of both Request and Response.
|
|
// We can decode this and then work out which it is.
|
|
type wireCombined struct {
|
|
VersionTag wireVersionTag `json:"jsonrpc"`
|
|
ID *ID `json:"id,omitempty"`
|
|
Method string `json:"method"`
|
|
Params *json.RawMessage `json:"params,omitempty"`
|
|
Result *json.RawMessage `json:"result,omitempty"`
|
|
Error *wireError `json:"error,omitempty"`
|
|
}
|
|
|
|
// wireError represents a structured error in a Response.
|
|
type wireError struct {
|
|
// Code is an error code indicating the type of failure.
|
|
Code int64 `json:"code"`
|
|
// Message is a short description of the error.
|
|
Message string `json:"message"`
|
|
// Data is optional structured data containing additional information about the error.
|
|
Data *json.RawMessage `json:"data,omitempty"`
|
|
}
|
|
|
|
// wireVersionTag is a special 0 sized struct that encodes as the jsonrpc version
|
|
// tag.
|
|
// It will fail during decode if it is not the correct version tag in the
|
|
// stream.
|
|
type wireVersionTag struct{}
|
|
|
|
// ID is a Request identifier.
|
|
type ID struct {
|
|
name string
|
|
number int64
|
|
}
|
|
|
|
func NewError(code int64, message string) error {
|
|
return &wireError{
|
|
Code: code,
|
|
Message: message,
|
|
}
|
|
}
|
|
|
|
func (err *wireError) Error() string {
|
|
return err.Message
|
|
}
|
|
|
|
func (wireVersionTag) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal("2.0")
|
|
}
|
|
|
|
func (wireVersionTag) UnmarshalJSON(data []byte) error {
|
|
version := ""
|
|
if err := json.Unmarshal(data, &version); err != nil {
|
|
return err
|
|
}
|
|
if version != "2.0" {
|
|
return fmt.Errorf("invalid RPC version %v", version)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
const invalidID int64 = math.MaxInt64
|
|
|
|
// NewIntID returns a new numerical request ID.
|
|
func NewIntID(v int64) ID { return ID{number: v} }
|
|
|
|
// NewStringID returns a new string request ID.
|
|
func NewStringID(v string) ID { return ID{name: v} }
|
|
|
|
// Format writes the ID to the formatter.
|
|
// If the rune is q the representation is non ambiguous,
|
|
// string forms are quoted, number forms are preceded by a #
|
|
func (id ID) Format(f fmt.State, r rune) {
|
|
numF, strF := `%d`, `%s`
|
|
if r == 'q' {
|
|
numF, strF = `#%d`, `%q`
|
|
}
|
|
switch {
|
|
case id.name != "":
|
|
fmt.Fprintf(f, strF, id.name)
|
|
default:
|
|
fmt.Fprintf(f, numF, id.number)
|
|
}
|
|
}
|
|
|
|
func (id *ID) MarshalJSON() ([]byte, error) {
|
|
if id.name != "" {
|
|
return json.Marshal(id.name)
|
|
}
|
|
return json.Marshal(id.number)
|
|
}
|
|
|
|
func (id *ID) UnmarshalJSON(data []byte) error {
|
|
*id = ID{}
|
|
if err := json.Unmarshal(data, &id.number); err == nil {
|
|
return nil
|
|
}
|
|
return json.Unmarshal(data, &id.name)
|
|
}
|