Everything is an RPCResponse

This commit is contained in:
Jae Kwon 2015-04-01 04:58:33 -07:00
parent 7b049e93fb
commit cc715e0ee3
8 changed files with 121 additions and 152 deletions

View File

@ -572,8 +572,7 @@ func readReflectJSON(rv reflect.Value, rt reflect.Type, o interface{}, err *erro
i, jsonName, fieldType := fieldInfo.unpack() i, jsonName, fieldType := fieldInfo.unpack()
value, ok := oMap[jsonName] value, ok := oMap[jsonName]
if !ok { if !ok {
*err = errors.New(Fmt("Missing field: %v", jsonName)) continue // Skip missing fields.
return
} }
fieldRv := rv.Field(i) fieldRv := rv.Field(i)
readReflectJSON(fieldRv, fieldType, value, err) readReflectJSON(fieldRv, fieldType, value, err)

View File

@ -199,6 +199,7 @@ func deboraBroadcast(n *Node) func([]byte) {
func Daemon(deborable DeboraMode) { func Daemon(deborable DeboraMode) {
// Add to debora // Add to debora
if deborable == DeboraPeerMode { if deborable == DeboraPeerMode {
// TODO: support debora.logfile
if err := debora.Add(PublicKey, SrcPath, AppName, config.App().GetString("Debora.LogFile")); err != nil { if err := debora.Add(PublicKey, SrcPath, AppName, config.App().GetString("Debora.LogFile")); err != nil {
log.Info("Failed to add program to debora", "error", err) log.Info("Failed to add program to debora", "error", err)
} }

View File

@ -97,22 +97,23 @@ func JSONRPCHandler(w http.ResponseWriter, r *http.Request) {
var jrpc JSONRPC var jrpc JSONRPC
err := json.Unmarshal(b, &jrpc) err := json.Unmarshal(b, &jrpc)
if err != nil { if err != nil {
// TODO WriteRPCResponse(w, NewRPCResponse(nil, err.Error()))
return
} }
funcInfo := funcMap[jrpc.Method] funcInfo := funcMap[jrpc.Method]
args, err := jsonParamsToArgs(funcInfo, jrpc.Params) args, err := jsonParamsToArgs(funcInfo, jrpc.Params)
if err != nil { if err != nil {
WriteAPIResponse(w, API_INVALID_PARAM, nil, err.Error()) WriteRPCResponse(w, NewRPCResponse(nil, err.Error()))
return return
} }
returns := funcInfo.f.Call(args) returns := funcInfo.f.Call(args)
response, err := returnsToResponse(returns) response, err := returnsToResponse(returns)
if err != nil { if err != nil {
WriteAPIResponse(w, API_ERROR, nil, err.Error()) WriteRPCResponse(w, NewRPCResponse(nil, err.Error()))
return return
} }
WriteAPIResponse(w, API_OK, response, "") WriteRPCResponse(w, NewRPCResponse(response, ""))
} }
// covert a list of interfaces to properly typed values // covert a list of interfaces to properly typed values
@ -149,16 +150,16 @@ func toHttpHandler(funcInfo *FuncWrapper) func(http.ResponseWriter, *http.Reques
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
args, err := httpParamsToArgs(funcInfo, r) args, err := httpParamsToArgs(funcInfo, r)
if err != nil { if err != nil {
WriteAPIResponse(w, API_INVALID_PARAM, nil, err.Error()) WriteRPCResponse(w, NewRPCResponse(nil, err.Error()))
return return
} }
returns := funcInfo.f.Call(args) returns := funcInfo.f.Call(args)
response, err := returnsToResponse(returns) response, err := returnsToResponse(returns)
if err != nil { if err != nil {
WriteAPIResponse(w, API_ERROR, nil, err.Error()) WriteRPCResponse(w, NewRPCResponse(nil, err.Error()))
return return
} }
WriteAPIResponse(w, API_OK, response, "") WriteRPCResponse(w, NewRPCResponse(response, ""))
} }
} }

View File

@ -23,8 +23,8 @@ var (
//RE_ID12 = regexp.MustCompile(`^[a-zA-Z0-9]{12}$`) //RE_ID12 = regexp.MustCompile(`^[a-zA-Z0-9]{12}$`)
) )
func panicAPI(err error) { func panicRPC(err error) {
panic(APIResponse{API_INVALID_PARAM, nil, err.Error()}) panic(NewRPCResponse(nil, err.Error()))
} }
func GetParam(r *http.Request, param string) string { func GetParam(r *http.Request, param string) string {

View File

@ -23,72 +23,34 @@ func StartHTTPServer() {
}() }()
} }
//----------------------------------------------------------------------------- type RPCResponse struct {
Result interface{} `json:"result"`
type RPCStatus string Error string `json:"error"`
Id string `json:"id"`
const ( JSONRPC int `json:"jsonrpc"`
RPC_OK RPCStatus = "OK"
RPC_ERROR RPCStatus = "ERROR"
RPC_INVALID_PARAM RPCStatus = "INVALID_PARAM"
RPC_UNAUTHORIZED RPCStatus = "UNAUTHORIZED"
RPC_REDIRECT RPCStatus = "REDIRECT"
)
//-----------------------------------------------------------------------------
type JSONRPCResponse struct {
JSONRPC int `json:"jsonrpc"`
Result interface{}
Error string
Id string
} }
//----------------------------------------------------------------------------- func NewRPCResponse(res interface{}, err string) RPCResponse {
if res == nil {
type RestResponse struct { res = struct{}{}
Status RPCStatus `json:"status"`
Data interface{} `json:"data"`
Error string `json:"error"`
}
func (res RestResponse) StatusError() string {
return fmt.Sprintf("Status(%v) %v", res.Status, res.Error)
}
func WriteRestResponse(w http.ResponseWriter, status RPCStatus, data interface{}, responseErr string) {
res := RestResponse{}
res.Status = status
if data == nil {
// so json doesn't vommit
data = struct{}{}
} }
res.Data = data return RPCResponse{
res.Error = responseErr Result: res,
Error: err,
Id: "",
JSONRPC: 2,
}
}
func WriteRPCResponse(w http.ResponseWriter, res RPCResponse) {
buf, n, err := new(bytes.Buffer), new(int64), new(error) buf, n, err := new(bytes.Buffer), new(int64), new(error)
binary.WriteJSON(res, buf, n, err) binary.WriteJSON(res, buf, n, err)
if *err != nil { if *err != nil {
log.Warn("Failed to write JSON RestResponse", "error", err) log.Warn("Failed to write JSON RPCResponse", "error", err)
} }
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200) w.WriteHeader(200)
/* Bad idea: (e.g. hard to use with jQuery)
switch res.Status {
case RPC_OK:
w.WriteHeader(200)
case RPC_ERROR:
w.WriteHeader(400)
case RPC_UNAUTHORIZED:
w.WriteHeader(401)
case RPC_INVALID_PARAM:
w.WriteHeader(420)
case RPC_REDIRECT:
w.WriteHeader(430)
default:
w.WriteHeader(440)
}*/
w.Write(buf.Bytes()) w.Write(buf.Bytes())
} }
@ -126,9 +88,9 @@ func RecoverAndLogHandler(handler http.Handler) http.Handler {
// at least to my localhost. // at least to my localhost.
if e := recover(); e != nil { if e := recover(); e != nil {
// If RestResponse, // If RPCResponse
if res, ok := e.(RestResponse); ok { if res, ok := e.(RPCResponse); ok {
WriteRestResponse(rww, res.Status, nil, res.Error) WriteRPCResponse(rww, res)
} else { } else {
// For the rest, // For the rest,
rww.WriteHeader(http.StatusInternalServerError) rww.WriteHeader(http.StatusInternalServerError)

View File

@ -3,11 +3,9 @@ package rpc
import ( import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"encoding/json"
"fmt" "fmt"
"github.com/tendermint/tendermint2/binary" "github.com/tendermint/tendermint2/binary"
. "github.com/tendermint/tendermint2/common" . "github.com/tendermint/tendermint2/common"
"github.com/tendermint/tendermint2/config"
"github.com/tendermint/tendermint2/merkle" "github.com/tendermint/tendermint2/merkle"
"github.com/tendermint/tendermint2/rpc/core" "github.com/tendermint/tendermint2/rpc/core"
"github.com/tendermint/tendermint2/state" "github.com/tendermint/tendermint2/state"
@ -28,19 +26,19 @@ func TestHTTPStatus(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
var status struct { var response struct {
Status string Result core.ResponseStatus `json:"result"`
Data core.ResponseStatus Error string `json:"error"`
Error string Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
} }
err = json.Unmarshal(body, &status) binary.ReadJSON(&response, body, &err)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
data := status.Data result := response.Result
if data.Network != config.App().GetString("Network") { fmt.Println(">>>", result)
t.Fatal(fmt.Errorf("Network mismatch: got %s expected %s", data.Network, config.App().Get("Network"))) return
}
} }
func TestHTTPGenPriv(t *testing.T) { func TestHTTPGenPriv(t *testing.T) {
@ -56,18 +54,17 @@ func TestHTTPGenPriv(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
var status struct { var response struct {
Status string Result core.ResponseGenPrivAccount `json:"result"`
Data core.ResponseGenPrivAccount Error string `json:"error"`
Error string Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
} }
binary.ReadJSON(&status, body, &err) binary.ReadJSON(&response, body, &err)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(status.Data.PrivAccount.Address) == 0 { fmt.Println(">>>", response)
t.Fatal("Failed to generate an address")
}
} }
func TestHTTPGetAccount(t *testing.T) { func TestHTTPGetAccount(t *testing.T) {

View File

@ -38,17 +38,20 @@ func TestJSONStatus(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
status := new(struct {
Status string var response struct {
Data core.ResponseStatus Result core.ResponseStatus `json:"result"`
Error string Error string `json:"error"`
}) Id string `json:"id"`
err = json.Unmarshal(body, status) JSONRPC int `json:"jsonrpc"`
}
binary.ReadJSON(&response, body, &err)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if status.Data.Network != config.App().GetString("Network") { if response.Result.Network != config.App().GetString("Network") {
t.Fatal(fmt.Errorf("Network mismatch: got %s expected %s", status.Data.Network, config.App().Get("Network"))) t.Fatal(fmt.Errorf("Network mismatch: got %s expected %s",
response.Result.Network, config.App().Get("Network")))
} }
} }
@ -76,16 +79,17 @@ func TestJSONGenPriv(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
var status struct { var response struct {
Status string Result core.ResponseGenPrivAccount `json:"result"`
Data core.ResponseGenPrivAccount Error string `json:"error"`
Error string Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
} }
binary.ReadJSON(&status, body, &err) binary.ReadJSON(&response, body, &err)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(status.Data.PrivAccount.Address) == 0 { if len(response.Result.PrivAccount.Address) == 0 {
t.Fatal("Failed to generate an address") t.Fatal("Failed to generate an address")
} }
} }
@ -138,16 +142,17 @@ func TestJSONBroadcastTx(t *testing.T) {
} }
b := w.Bytes() b := w.Bytes()
var status struct { var response struct {
Status string Result core.ResponseBroadcastTx `json:"result"`
Data core.ResponseBroadcastTx Error string `json:"error"`
Error string Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
} }
requestResponse(t, "broadcast_tx", url.Values{"tx": {string(b)}}, &status) requestResponse(t, "broadcast_tx", url.Values{"tx": {string(b)}}, &response)
if status.Status == "ERROR" { if response.Error != "" {
t.Fatal(status.Error) t.Fatal(response.Error)
} }
receipt := status.Data.Receipt receipt := response.Result.Receipt
if receipt.CreatesContract > 0 { if receipt.CreatesContract > 0 {
t.Fatal("This tx does not create a contract") t.Fatal("This tx does not create a contract")
} }

View File

@ -96,16 +96,17 @@ func getAccount(t *testing.T, typ string, addr []byte) *account.Account {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
var status struct { var response struct {
Status string Result core.ResponseGetAccount `json:"result"`
Data core.ResponseGetAccount Error string `json:"error"`
Error string Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
} }
binary.ReadJSON(&status, body, &err) binary.ReadJSON(&response, body, &err)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
return status.Data.Account return response.Result.Account
} }
func makeSendTx(t *testing.T, typ string, from, to []byte, amt uint64) *types.SendTx { func makeSendTx(t *testing.T, typ string, from, to []byte, amt uint64) *types.SendTx {
@ -165,7 +166,7 @@ func makeCallTx(t *testing.T, typ string, from, to, data []byte, amt, gaslim, fe
return tx return tx
} }
func requestResponse(t *testing.T, method string, values url.Values, status interface{}) { func requestResponse(t *testing.T, method string, values url.Values, response interface{}) {
resp, err := http.PostForm(requestAddr+method, values) resp, err := http.PostForm(requestAddr+method, values)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -175,7 +176,7 @@ func requestResponse(t *testing.T, method string, values url.Values, status inte
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
binary.ReadJSON(status, body, &err) binary.ReadJSON(response, body, &err)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -207,18 +208,18 @@ func signTx(t *testing.T, typ string, fromAddr, toAddr, data []byte, key [64]byt
t.Fatal(err) t.Fatal(err)
} }
var status struct { var response struct {
Status string Result core.ResponseSignTx `json:"result"`
Data core.ResponseSignTx Error string `json:"error"`
Error string Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
} }
requestResponse(t, "unsafe/sign_tx", url.Values{"tx": {string(b)}, "privAccounts": {string(w.Bytes())}}, &status) requestResponse(t, "unsafe/sign_tx", url.Values{"tx": {string(b)}, "privAccounts": {string(w.Bytes())}}, &response)
if status.Status == "ERROR" { if response.Error != "" {
t.Fatal(status.Error) t.Fatal(response.Error)
} }
response := status.Data result := response.Result
//tx = response.Tx.(*types.SendTx) return result.Tx, privAcc
return response.Tx, privAcc
} }
func broadcastTx(t *testing.T, typ string, fromAddr, toAddr, data []byte, key [64]byte, amt, gaslim, fee uint64) (types.Tx, core.Receipt) { func broadcastTx(t *testing.T, typ string, fromAddr, toAddr, data []byte, key [64]byte, amt, gaslim, fee uint64) (types.Tx, core.Receipt) {
@ -232,45 +233,48 @@ func broadcastTx(t *testing.T, typ string, fromAddr, toAddr, data []byte, key [6
} }
b := w.Bytes() b := w.Bytes()
var status struct { var response struct {
Status string Result core.ResponseBroadcastTx `json:"result"`
Data core.ResponseBroadcastTx Error string `json:"error"`
Error string Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
} }
requestResponse(t, "broadcast_tx", url.Values{"tx": {string(b)}}, &status) requestResponse(t, "broadcast_tx", url.Values{"tx": {string(b)}}, &response)
if status.Status == "ERROR" { if response.Error != "" {
t.Fatal(status.Error) t.Fatal(response.Error)
} }
return tx, status.Data.Receipt return tx, response.Result.Receipt
} }
func dumpStorage(t *testing.T, addr []byte) core.ResponseDumpStorage { func dumpStorage(t *testing.T, addr []byte) core.ResponseDumpStorage {
addrString := "\"" + hex.EncodeToString(addr) + "\"" addrString := "\"" + hex.EncodeToString(addr) + "\""
var status struct { var response struct {
Status string Result core.ResponseDumpStorage `json:"result"`
Data core.ResponseDumpStorage Error string `json:"error"`
Error string Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
} }
requestResponse(t, "dump_storage", url.Values{"address": {addrString}}, &status) requestResponse(t, "dump_storage", url.Values{"address": {addrString}}, &response)
if status.Status != "OK" { if response.Error != "" {
t.Fatal(status.Error) t.Fatal(response.Error)
} }
return status.Data return response.Result
} }
func getStorage(t *testing.T, addr, slot []byte) []byte { func getStorage(t *testing.T, addr, slot []byte) []byte {
addrString := "\"" + hex.EncodeToString(addr) + "\"" addrString := "\"" + hex.EncodeToString(addr) + "\""
slotString := "\"" + hex.EncodeToString(slot) + "\"" slotString := "\"" + hex.EncodeToString(slot) + "\""
var status struct { var response struct {
Status string Result core.ResponseGetStorage `json:"result"`
Data core.ResponseGetStorage Error string `json:"error"`
Error string Id string `json:"id"`
JSONRPC int `json:"jsonrpc"`
} }
requestResponse(t, "get_storage", url.Values{"address": {addrString}, "storage": {slotString}}, &status) requestResponse(t, "get_storage", url.Values{"address": {addrString}, "storage": {slotString}}, &response)
if status.Status != "OK" { if response.Error != "" {
t.Fatal(status.Error) t.Fatal(response.Error)
} }
return status.Data.Value return response.Result.Value
} }
func checkTx(t *testing.T, fromAddr []byte, priv *account.PrivAccount, tx *types.SendTx) { func checkTx(t *testing.T, fromAddr []byte, priv *account.PrivAccount, tx *types.SendTx) {