From aff561d8c3a904d87ff9eb859b467772d8506e08 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 13 Jan 2016 18:37:35 -0500 Subject: [PATCH] RPCResponse.Result is json.RawMessage --- client/http_client.go | 25 ++++++++++++++----------- client/ws_client.go | 4 ++-- server/handlers.go | 18 +++++++++++------- server/http_server.go | 9 +++++++-- types/types.go | 16 ++++++++++------ 5 files changed, 44 insertions(+), 28 deletions(-) diff --git a/client/http_client.go b/client/http_client.go index 133f2b72..f614a776 100644 --- a/client/http_client.go +++ b/client/http_client.go @@ -2,6 +2,7 @@ package rpcclient import ( "bytes" + "encoding/json" "errors" "io/ioutil" "net/http" @@ -22,8 +23,8 @@ func NewClientJSONRPC(remote string) *ClientJSONRPC { return &ClientJSONRPC{remote} } -func (c *ClientJSONRPC) Call(method string, params []interface{}) (interface{}, error) { - return CallHTTP_JSONRPC(c.remote, method, params) +func (c *ClientJSONRPC) Call(method string, params []interface{}, result interface{}) (interface{}, error) { + return CallHTTP_JSONRPC(c.remote, method, params, result) } // URI takes params as a map @@ -38,11 +39,11 @@ func NewClientURI(remote string) *ClientURI { return &ClientURI{remote} } -func (c *ClientURI) Call(method string, params map[string]interface{}) (interface{}, error) { - return CallHTTP_URI(c.remote, method, params) +func (c *ClientURI) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) { + return CallHTTP_URI(c.remote, method, params, result) } -func CallHTTP_JSONRPC(remote string, method string, params []interface{}) (interface{}, error) { +func CallHTTP_JSONRPC(remote string, method string, params []interface{}, result interface{}) (interface{}, error) { // Make request and get responseBytes request := rpctypes.RPCRequest{ JSONRPC: "2.0", @@ -63,10 +64,10 @@ func CallHTTP_JSONRPC(remote string, method string, params []interface{}) (inter return nil, err } log.Info(Fmt("RPC response: %v", string(responseBytes))) - return unmarshalResponseBytes(responseBytes) + return unmarshalResponseBytes(responseBytes, result) } -func CallHTTP_URI(remote string, method string, params map[string]interface{}) (interface{}, error) { +func CallHTTP_URI(remote string, method string, params map[string]interface{}, result interface{}) (interface{}, error) { values, err := argsToURLValues(params) if err != nil { return nil, err @@ -81,18 +82,18 @@ func CallHTTP_URI(remote string, method string, params map[string]interface{}) ( if err != nil { return nil, err } - return unmarshalResponseBytes(responseBytes) + return unmarshalResponseBytes(responseBytes, result) } //------------------------------------------------ -func unmarshalResponseBytes(responseBytes []byte) (interface{}, error) { +func unmarshalResponseBytes(responseBytes []byte, result interface{}) (interface{}, error) { // read response // if rpc/core/types is imported, the result will unmarshal // into the correct type var err error response := &rpctypes.RPCResponse{} - wire.ReadJSON(response, responseBytes, &err) + err = json.Unmarshal(responseBytes, response) if err != nil { return nil, err } @@ -100,7 +101,9 @@ func unmarshalResponseBytes(responseBytes []byte) (interface{}, error) { if errorStr != "" { return nil, errors.New(errorStr) } - return response.Result, err + // unmarshal the RawMessage into the result + result = wire.ReadJSONPtr(result, *response.Result, &err) + return result, err } func argsToURLValues(args map[string]interface{}) (url.Values, error) { diff --git a/client/ws_client.go b/client/ws_client.go index 4ce1ca07..e0dc349c 100644 --- a/client/ws_client.go +++ b/client/ws_client.go @@ -1,13 +1,13 @@ package rpcclient import ( + "encoding/json" "net/http" "time" "github.com/gorilla/websocket" . "github.com/tendermint/go-common" "github.com/tendermint/go-rpc/types" - "github.com/tendermint/go-wire" ) const ( @@ -79,7 +79,7 @@ func (wsc *WSClient) receiveEventsRoutine() { break } else { var response rpctypes.RPCResponse - wire.ReadJSON(&response, data, &err) + err := json.Unmarshal(data, &response) if err != nil { log.Info("WSClient failed to parse message", "error", err, "data", string(data)) wsc.Stop() diff --git a/server/handlers.go b/server/handlers.go index 843a7ada..a463b21e 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -19,6 +19,8 @@ import ( "github.com/tendermint/go-wire" ) +// Adds a route for each function in the funcMap, as well as general jsonrpc and websocket handlers for all functions. +// "result" is the interface on which the result objects are registered, and is popualted with every RPCResponse func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc) { // HTTP endpoints for funcName, rpcFunc := range funcMap { @@ -433,7 +435,6 @@ func (wsc *wsConnection) readRoutine() { // receives on a write channel and writes out on the socket func (wsc *wsConnection) writeRoutine() { defer wsc.baseConn.Close() - var n, err = int(0), error(nil) for { select { case <-wsc.Quit: @@ -446,14 +447,12 @@ func (wsc *wsConnection) writeRoutine() { return } case msg := <-wsc.writeChan: - buf := new(bytes.Buffer) - wire.WriteJSON(msg, buf, &n, &err) + jsonBytes, err := json.Marshal(msg) if err != nil { log.Error("Failed to marshal RPCResponse to JSON", "error", err) } else { wsc.baseConn.SetWriteDeadline(time.Now().Add(time.Second * wsWriteTimeoutSeconds)) - bufBytes := buf.Bytes() - if err = wsc.baseConn.WriteMessage(websocket.TextMessage, bufBytes); err != nil { + if err = wsc.baseConn.WriteMessage(websocket.TextMessage, jsonBytes); err != nil { log.Warn("Failed to write response on websocket", "error", err) wsc.Stop() return @@ -507,13 +506,18 @@ func (wm *WebsocketManager) WebsocketHandler(w http.ResponseWriter, r *http.Requ // rpc.websocket //----------------------------------------------------------------------------- -// returns is result struct and error. If error is not nil, return it +// NOTE: assume returns is result struct and error. If error is not nil, return it func unreflectResult(returns []reflect.Value) (interface{}, error) { errV := returns[1] if errV.Interface() != nil { return nil, fmt.Errorf("%v", errV.Interface()) } - return returns[0].Interface(), nil + rv := returns[0] + // the result is a registered interface, + // we need a pointer to it so we can marshal with type byte + rvp := reflect.New(rv.Type()) + rvp.Elem().Set(rv) + return rvp.Interface(), nil } // writes a list of available rpc endpoints as an html page diff --git a/server/http_server.go b/server/http_server.go index 37b01cee..1271d073 100644 --- a/server/http_server.go +++ b/server/http_server.go @@ -3,6 +3,7 @@ package rpcserver import ( "bufio" + "encoding/json" "fmt" "net" "net/http" @@ -12,7 +13,7 @@ import ( "github.com/tendermint/go-alert" . "github.com/tendermint/go-common" . "github.com/tendermint/go-rpc/types" - "github.com/tendermint/go-wire" + //"github.com/tendermint/go-wire" ) func StartHTTPServer(listenAddr string, handler http.Handler) (net.Listener, error) { @@ -32,7 +33,11 @@ func StartHTTPServer(listenAddr string, handler http.Handler) (net.Listener, err } func WriteRPCResponseHTTP(w http.ResponseWriter, res RPCResponse) { - jsonBytes := wire.JSONBytesPretty(res) + // jsonBytes := wire.JSONBytesPretty(res) + jsonBytes, err := json.Marshal(res) + if err != nil { + panic(err) + } w.Header().Set("Content-Type", "application/json") w.WriteHeader(200) w.Write(jsonBytes) diff --git a/types/types.go b/types/types.go index 4905d000..f9d1961c 100644 --- a/types/types.go +++ b/types/types.go @@ -1,7 +1,10 @@ package rpctypes import ( + "encoding/json" + "github.com/tendermint/go-events" + "github.com/tendermint/go-wire" ) type RPCRequest struct { @@ -39,17 +42,18 @@ type Result interface { //---------------------------------------- type RPCResponse struct { - JSONRPC string `json:"jsonrpc"` - ID string `json:"id"` - Result Result `json:"result"` - Error string `json:"error"` + JSONRPC string `json:"jsonrpc"` + ID string `json:"id"` + Result *json.RawMessage `json:"result"` + Error string `json:"error"` } -func NewRPCResponse(id string, res Result, err string) RPCResponse { +func NewRPCResponse(id string, res interface{}, err string) RPCResponse { + raw := json.RawMessage(wire.JSONBytes(res)) return RPCResponse{ JSONRPC: "2.0", ID: id, - Result: res, + Result: &raw, Error: err, } }