// 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) }