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