github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/rpc/jsonrpc/types/types.go (about) 1 package types 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "net/http" 8 "reflect" 9 "strings" 10 11 tmjson "github.com/tendermint/tendermint/libs/json" 12 ) 13 14 // a wrapper to emulate a sum type: jsonrpcid = string | int 15 // TODO: refactor when Go 2.0 arrives https://github.com/golang/go/issues/19412 16 type jsonrpcid interface { 17 isJSONRPCID() 18 } 19 20 // JSONRPCStringID a wrapper for JSON-RPC string IDs 21 type JSONRPCStringID string 22 23 func (JSONRPCStringID) isJSONRPCID() {} 24 func (id JSONRPCStringID) String() string { return string(id) } 25 26 // JSONRPCIntID a wrapper for JSON-RPC integer IDs 27 type JSONRPCIntID int 28 29 func (JSONRPCIntID) isJSONRPCID() {} 30 func (id JSONRPCIntID) String() string { return fmt.Sprintf("%d", id) } 31 32 func idFromInterface(idInterface interface{}) (jsonrpcid, error) { 33 switch id := idInterface.(type) { 34 case string: 35 return JSONRPCStringID(id), nil 36 case float64: 37 // json.Unmarshal uses float64 for all numbers 38 // (https://golang.org/pkg/encoding/json/#Unmarshal), 39 // but the JSONRPC2.0 spec says the id SHOULD NOT contain 40 // decimals - so we truncate the decimals here. 41 return JSONRPCIntID(int(id)), nil 42 default: 43 typ := reflect.TypeOf(id) 44 return nil, fmt.Errorf("json-rpc ID (%v) is of unknown type (%v)", id, typ) 45 } 46 } 47 48 //---------------------------------------- 49 // REQUEST 50 51 type RPCRequest struct { 52 JSONRPC string `json:"jsonrpc"` 53 ID jsonrpcid `json:"id,omitempty"` 54 Method string `json:"method"` 55 Params json.RawMessage `json:"params"` // must be map[string]interface{} or []interface{} 56 } 57 58 // UnmarshalJSON custom JSON unmarshalling due to jsonrpcid being string or int 59 func (req *RPCRequest) UnmarshalJSON(data []byte) error { 60 unsafeReq := &struct { 61 JSONRPC string `json:"jsonrpc"` 62 ID interface{} `json:"id,omitempty"` 63 Method string `json:"method"` 64 Params json.RawMessage `json:"params"` // must be map[string]interface{} or []interface{} 65 }{} 66 err := json.Unmarshal(data, &unsafeReq) 67 if err != nil { 68 return err 69 } 70 req.JSONRPC = unsafeReq.JSONRPC 71 req.Method = unsafeReq.Method 72 req.Params = unsafeReq.Params 73 if unsafeReq.ID == nil { 74 return nil 75 } 76 id, err := idFromInterface(unsafeReq.ID) 77 if err != nil { 78 return err 79 } 80 req.ID = id 81 return nil 82 } 83 84 func NewRPCRequest(id jsonrpcid, method string, params json.RawMessage) RPCRequest { 85 return RPCRequest{ 86 JSONRPC: "2.0", 87 ID: id, 88 Method: method, 89 Params: params, 90 } 91 } 92 93 func (req RPCRequest) String() string { 94 return fmt.Sprintf("RPCRequest{%s %s/%X}", req.ID, req.Method, req.Params) 95 } 96 97 func MapToRequest(id jsonrpcid, method string, params map[string]interface{}) (RPCRequest, error) { 98 var paramsMap = make(map[string]json.RawMessage, len(params)) 99 for name, value := range params { 100 valueJSON, err := tmjson.Marshal(value) 101 if err != nil { 102 return RPCRequest{}, err 103 } 104 paramsMap[name] = valueJSON 105 } 106 107 payload, err := json.Marshal(paramsMap) 108 if err != nil { 109 return RPCRequest{}, err 110 } 111 112 return NewRPCRequest(id, method, payload), nil 113 } 114 115 func ArrayToRequest(id jsonrpcid, method string, params []interface{}) (RPCRequest, error) { 116 var paramsMap = make([]json.RawMessage, len(params)) 117 for i, value := range params { 118 valueJSON, err := tmjson.Marshal(value) 119 if err != nil { 120 return RPCRequest{}, err 121 } 122 paramsMap[i] = valueJSON 123 } 124 125 payload, err := json.Marshal(paramsMap) 126 if err != nil { 127 return RPCRequest{}, err 128 } 129 130 return NewRPCRequest(id, method, payload), nil 131 } 132 133 //---------------------------------------- 134 // RESPONSE 135 136 type RPCError struct { 137 Code int `json:"code"` 138 Message string `json:"message"` 139 Data string `json:"data,omitempty"` 140 } 141 142 func (err RPCError) Error() string { 143 const baseFormat = "RPC error %v - %s" 144 if err.Data != "" { 145 return fmt.Sprintf(baseFormat+": %s", err.Code, err.Message, err.Data) 146 } 147 return fmt.Sprintf(baseFormat, err.Code, err.Message) 148 } 149 150 type RPCResponse struct { 151 JSONRPC string `json:"jsonrpc"` 152 ID jsonrpcid `json:"id,omitempty"` 153 Result json.RawMessage `json:"result,omitempty"` 154 Error *RPCError `json:"error,omitempty"` 155 } 156 157 // UnmarshalJSON custom JSON unmarshalling due to jsonrpcid being string or int 158 func (resp *RPCResponse) UnmarshalJSON(data []byte) error { 159 unsafeResp := &struct { 160 JSONRPC string `json:"jsonrpc"` 161 ID interface{} `json:"id,omitempty"` 162 Result json.RawMessage `json:"result,omitempty"` 163 Error *RPCError `json:"error,omitempty"` 164 }{} 165 err := json.Unmarshal(data, &unsafeResp) 166 if err != nil { 167 return err 168 } 169 resp.JSONRPC = unsafeResp.JSONRPC 170 resp.Error = unsafeResp.Error 171 resp.Result = unsafeResp.Result 172 if unsafeResp.ID == nil { 173 return nil 174 } 175 id, err := idFromInterface(unsafeResp.ID) 176 if err != nil { 177 return err 178 } 179 resp.ID = id 180 return nil 181 } 182 183 func NewRPCSuccessResponse(id jsonrpcid, res interface{}) RPCResponse { 184 var rawMsg json.RawMessage 185 186 if res != nil { 187 var js []byte 188 js, err := tmjson.Marshal(res) 189 if err != nil { 190 return RPCInternalError(id, fmt.Errorf("error marshalling response: %w", err)) 191 } 192 rawMsg = json.RawMessage(js) 193 } 194 195 return RPCResponse{JSONRPC: "2.0", ID: id, Result: rawMsg} 196 } 197 198 func NewRPCErrorResponse(id jsonrpcid, code int, msg string, data string) RPCResponse { 199 return RPCResponse{ 200 JSONRPC: "2.0", 201 ID: id, 202 Error: &RPCError{Code: code, Message: msg, Data: data}, 203 } 204 } 205 206 func (resp RPCResponse) String() string { 207 if resp.Error == nil { 208 return fmt.Sprintf("RPCResponse{%s %v}", resp.ID, resp.Result) 209 } 210 return fmt.Sprintf("RPCResponse{%s %v}", resp.ID, resp.Error) 211 } 212 213 // From the JSON-RPC 2.0 spec: 214 // If there was an error in detecting the id in the Request object (e.g. Parse 215 // error/Invalid Request), it MUST be Null. 216 func RPCParseError(err error) RPCResponse { 217 return NewRPCErrorResponse(nil, -32700, "Parse error. Invalid JSON", err.Error()) 218 } 219 220 // From the JSON-RPC 2.0 spec: 221 // If there was an error in detecting the id in the Request object (e.g. Parse 222 // error/Invalid Request), it MUST be Null. 223 func RPCInvalidRequestError(id jsonrpcid, err error) RPCResponse { 224 return NewRPCErrorResponse(id, -32600, "Invalid Request", err.Error()) 225 } 226 227 func RPCMethodNotFoundError(id jsonrpcid) RPCResponse { 228 return NewRPCErrorResponse(id, -32601, "Method not found", "") 229 } 230 231 func RPCInvalidParamsError(id jsonrpcid, err error) RPCResponse { 232 return NewRPCErrorResponse(id, -32602, "Invalid params", err.Error()) 233 } 234 235 func RPCInternalError(id jsonrpcid, err error) RPCResponse { 236 return NewRPCErrorResponse(id, -32603, "Internal error", err.Error()) 237 } 238 239 func RPCServerError(id jsonrpcid, err error) RPCResponse { 240 return NewRPCErrorResponse(id, -32000, "Server error", err.Error()) 241 } 242 243 //---------------------------------------- 244 245 // WSRPCConnection represents a websocket connection. 246 type WSRPCConnection interface { 247 // GetRemoteAddr returns a remote address of the connection. 248 GetRemoteAddr() string 249 // WriteRPCResponse writes the resp onto connection (BLOCKING). 250 WriteRPCResponse(resp RPCResponse) 251 // TryWriteRPCResponse tries to write the resp onto connection (NON-BLOCKING). 252 TryWriteRPCResponse(resp RPCResponse) bool 253 // Context returns the connection's context. 254 Context() context.Context 255 } 256 257 // Context is the first parameter for all functions. It carries a json-rpc 258 // request, http request and websocket connection. 259 // 260 // - JSONReq is non-nil when JSONRPC is called over websocket or HTTP. 261 // - WSConn is non-nil when we're connected via a websocket. 262 // - HTTPReq is non-nil when URI or JSONRPC is called over HTTP. 263 type Context struct { 264 // json-rpc request 265 JSONReq *RPCRequest 266 // websocket connection 267 WSConn WSRPCConnection 268 // http request 269 HTTPReq *http.Request 270 } 271 272 // RemoteAddr returns the remote address (usually a string "IP:port"). 273 // If neither HTTPReq nor WSConn is set, an empty string is returned. 274 // HTTP: 275 // http.Request#RemoteAddr 276 // WS: 277 // result of GetRemoteAddr 278 func (ctx *Context) RemoteAddr() string { 279 if ctx.HTTPReq != nil { 280 return ctx.HTTPReq.RemoteAddr 281 } else if ctx.WSConn != nil { 282 return ctx.WSConn.GetRemoteAddr() 283 } 284 return "" 285 } 286 287 // Context returns the request's context. 288 // The returned context is always non-nil; it defaults to the background context. 289 // HTTP: 290 // The context is canceled when the client's connection closes, the request 291 // is canceled (with HTTP/2), or when the ServeHTTP method returns. 292 // WS: 293 // The context is canceled when the client's connections closes. 294 func (ctx *Context) Context() context.Context { 295 if ctx.HTTPReq != nil { 296 return ctx.HTTPReq.Context() 297 } else if ctx.WSConn != nil { 298 return ctx.WSConn.Context() 299 } 300 return context.Background() 301 } 302 303 //---------------------------------------- 304 // SOCKETS 305 306 // 307 // Determine if its a unix or tcp socket. 308 // If tcp, must specify the port; `0.0.0.0` will return incorrectly as "unix" since there's no port 309 // TODO: deprecate 310 func SocketType(listenAddr string) string { 311 socketType := "unix" 312 if len(strings.Split(listenAddr, ":")) >= 2 { 313 socketType = "tcp" 314 } 315 return socketType 316 }