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 5package jsonrpc2 6 7import ( 8 "encoding/json" 9 "fmt" 10 "math" 11) 12 13// this file contains the go forms of the wire specification 14// see http://www.jsonrpc.org/specification for details 15 16var ( 17 // ErrUnknown should be used for all non coded errors. 18 ErrUnknown = NewError(-32001, "JSON RPC unknown error") 19 // ErrParse is used when invalid JSON was received by the server. 20 ErrParse = NewError(-32700, "JSON RPC parse error") 21 //ErrInvalidRequest is used when the JSON sent is not a valid Request object. 22 ErrInvalidRequest = NewError(-32600, "JSON RPC invalid request") 23 // ErrMethodNotFound should be returned by the handler when the method does 24 // not exist / is not available. 25 ErrMethodNotFound = NewError(-32601, "JSON RPC method not found") 26 // ErrInvalidParams should be returned by the handler when method 27 // parameter(s) were invalid. 28 ErrInvalidParams = NewError(-32602, "JSON RPC invalid params") 29 // ErrInternal is not currently returned but defined for completeness. 30 ErrInternal = NewError(-32603, "JSON RPC internal error") 31 32 //ErrServerOverloaded is returned when a message was refused due to a 33 //server being temporarily unable to accept any new messages. 34 ErrServerOverloaded = NewError(-32000, "JSON RPC overloaded") 35) 36 37// wireRequest is sent to a server to represent a Call or Notify operaton. 38type wireRequest struct { 39 // VersionTag is always encoded as the string "2.0" 40 VersionTag wireVersionTag `json:"jsonrpc"` 41 // Method is a string containing the method name to invoke. 42 Method string `json:"method"` 43 // Params is either a struct or an array with the parameters of the method. 44 Params *json.RawMessage `json:"params,omitempty"` 45 // The id of this request, used to tie the Response back to the request. 46 // Will be either a string or a number. If not set, the Request is a notify, 47 // and no response is possible. 48 ID *ID `json:"id,omitempty"` 49} 50 51// WireResponse is a reply to a Request. 52// It will always have the ID field set to tie it back to a request, and will 53// have either the Result or Error fields set depending on whether it is a 54// success or failure response. 55type wireResponse struct { 56 // VersionTag is always encoded as the string "2.0" 57 VersionTag wireVersionTag `json:"jsonrpc"` 58 // Result is the response value, and is required on success. 59 Result *json.RawMessage `json:"result,omitempty"` 60 // Error is a structured error response if the call fails. 61 Error *wireError `json:"error,omitempty"` 62 // ID must be set and is the identifier of the Request this is a response to. 63 ID *ID `json:"id,omitempty"` 64} 65 66// wireCombined has all the fields of both Request and Response. 67// We can decode this and then work out which it is. 68type wireCombined struct { 69 VersionTag wireVersionTag `json:"jsonrpc"` 70 ID *ID `json:"id,omitempty"` 71 Method string `json:"method"` 72 Params *json.RawMessage `json:"params,omitempty"` 73 Result *json.RawMessage `json:"result,omitempty"` 74 Error *wireError `json:"error,omitempty"` 75} 76 77// wireError represents a structured error in a Response. 78type wireError struct { 79 // Code is an error code indicating the type of failure. 80 Code int64 `json:"code"` 81 // Message is a short description of the error. 82 Message string `json:"message"` 83 // Data is optional structured data containing additional information about the error. 84 Data *json.RawMessage `json:"data,omitempty"` 85} 86 87// wireVersionTag is a special 0 sized struct that encodes as the jsonrpc version 88// tag. 89// It will fail during decode if it is not the correct version tag in the 90// stream. 91type wireVersionTag struct{} 92 93// ID is a Request identifier. 94type ID struct { 95 name string 96 number int64 97} 98 99func NewError(code int64, message string) error { 100 return &wireError{ 101 Code: code, 102 Message: message, 103 } 104} 105 106func (err *wireError) Error() string { 107 return err.Message 108} 109 110func (wireVersionTag) MarshalJSON() ([]byte, error) { 111 return json.Marshal("2.0") 112} 113 114func (wireVersionTag) UnmarshalJSON(data []byte) error { 115 version := "" 116 if err := json.Unmarshal(data, &version); err != nil { 117 return err 118 } 119 if version != "2.0" { 120 return fmt.Errorf("invalid RPC version %v", version) 121 } 122 return nil 123} 124 125const invalidID int64 = math.MaxInt64 126 127// NewIntID returns a new numerical request ID. 128func NewIntID(v int64) ID { return ID{number: v} } 129 130// NewStringID returns a new string request ID. 131func NewStringID(v string) ID { return ID{name: v} } 132 133// Format writes the ID to the formatter. 134// If the rune is q the representation is non ambiguous, 135// string forms are quoted, number forms are preceded by a # 136func (id ID) Format(f fmt.State, r rune) { 137 numF, strF := `%d`, `%s` 138 if r == 'q' { 139 numF, strF = `#%d`, `%q` 140 } 141 switch { 142 case id.name != "": 143 fmt.Fprintf(f, strF, id.name) 144 default: 145 fmt.Fprintf(f, numF, id.number) 146 } 147} 148 149func (id *ID) MarshalJSON() ([]byte, error) { 150 if id.name != "" { 151 return json.Marshal(id.name) 152 } 153 return json.Marshal(id.number) 154} 155 156func (id *ID) UnmarshalJSON(data []byte) error { 157 *id = ID{} 158 if err := json.Unmarshal(data, &id.number); err == nil { 159 return nil 160 } 161 return json.Unmarshal(data, &id.name) 162} 163