Browse Source

add readReflectJSON from wire

pull/467/head
Ethan Buchman 7 years ago
parent
commit
ac28b12fa8
2 changed files with 291 additions and 28 deletions
  1. +1
    -28
      rpc/lib/server/handlers.go
  2. +290
    -0
      rpc/lib/server/wire.go

+ 1
- 28
rpc/lib/server/handlers.go View File

@ -2,7 +2,6 @@ package rpcserver
import (
"bytes"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
@ -16,7 +15,6 @@ import (
"github.com/gorilla/websocket"
"github.com/pkg/errors"
//wire "github.com/tendermint/go-wire"
"github.com/tendermint/go-wire/data"
types "github.com/tendermint/tendermint/rpc/lib/types"
cmn "github.com/tendermint/tmlibs/common"
events "github.com/tendermint/tmlibs/events"
@ -206,32 +204,7 @@ func jsonParamsToArgsWS(rpcFunc *RPCFunc, paramsI interface{}, wsCtx types.WSRPC
func _jsonObjectToArg(ty reflect.Type, object interface{}) (reflect.Value, error) {
var err error
v := reflect.New(ty)
// if the object is a byte array, we need to decode it
if ty.Kind() == reflect.Slice && ty.Elem().Kind() == reflect.Uint8 {
s, ok := object.(string)
if !ok {
return v, fmt.Errorf("cmah")
}
// if its data.Bytes, use hex
// else use base64
dbty := reflect.TypeOf(data.Bytes{})
if ty == dbty {
decoded, err := hex.DecodeString(s)
if err != nil {
return v, err
}
object = decoded
} else {
decoded, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return v, err
}
object = decoded
}
}
v.Elem().Set(reflect.ValueOf(object))
readJSONObjectPtr(v.Interface(), object, &err)
if err != nil {
return v, err
}


+ 290
- 0
rpc/lib/server/wire.go View File

@ -0,0 +1,290 @@
package rpcserver
import (
"encoding/base64"
"encoding/hex"
"reflect"
"time"
"github.com/pkg/errors"
"github.com/tendermint/go-wire"
"github.com/tendermint/go-wire/data"
cmn "github.com/tendermint/tmlibs/common"
)
var (
timeType = wire.GetTypeFromStructDeclaration(struct{ time.Time }{})
)
func readJSONObjectPtr(o interface{}, object interface{}, err *error) interface{} {
rv, rt := reflect.ValueOf(o), reflect.TypeOf(o)
if rv.Kind() == reflect.Ptr {
readReflectJSON(rv.Elem(), rt.Elem(), wire.Options{}, object, err)
} else {
cmn.PanicSanity("ReadJSON(Object)Ptr expects o to be a pointer")
}
return o
}
func readByteJSON(o interface{}) (typeByte byte, rest interface{}, err error) {
oSlice, ok := o.([]interface{})
if !ok {
err = errors.New(cmn.Fmt("Expected type [Byte,?] but got type %v", reflect.TypeOf(o)))
return
}
if len(oSlice) != 2 {
err = errors.New(cmn.Fmt("Expected [Byte,?] len 2 but got len %v", len(oSlice)))
return
}
typeByte_, ok := oSlice[0].(float64)
typeByte = byte(typeByte_)
rest = oSlice[1]
return
}
// Contract: Caller must ensure that rt is supported
// (e.g. is recursively composed of supported native types, and structs and slices.)
// rv and rt refer to the object we're unmarhsaling into, whereas o is the result of naiive json unmarshal (map[string]interface{})
func readReflectJSON(rv reflect.Value, rt reflect.Type, opts wire.Options, o interface{}, err *error) {
// Get typeInfo
typeInfo := wire.GetTypeInfo(rt)
if rt.Kind() == reflect.Interface {
if !typeInfo.IsRegisteredInterface {
// There's no way we can read such a thing.
*err = errors.New(cmn.Fmt("Cannot read unregistered interface type %v", rt))
return
}
if o == nil {
return // nil
}
typeByte, rest, err_ := readByteJSON(o)
if err_ != nil {
*err = err_
return
}
crt, ok := typeInfo.ByteToType[typeByte]
if !ok {
*err = errors.New(cmn.Fmt("Byte %X not registered for interface %v", typeByte, rt))
return
}
if crt.Kind() == reflect.Ptr {
crt = crt.Elem()
crv := reflect.New(crt)
readReflectJSON(crv.Elem(), crt, opts, rest, err)
rv.Set(crv) // NOTE: orig rv is ignored.
} else {
crv := reflect.New(crt).Elem()
readReflectJSON(crv, crt, opts, rest, err)
rv.Set(crv) // NOTE: orig rv is ignored.
}
return
}
if rt.Kind() == reflect.Ptr {
if o == nil {
return // nil
}
// Create new struct if rv is nil.
if rv.IsNil() {
newRv := reflect.New(rt.Elem())
rv.Set(newRv)
rv = newRv
}
// Dereference pointer
rv, rt = rv.Elem(), rt.Elem()
typeInfo = wire.GetTypeInfo(rt)
// continue...
}
switch rt.Kind() {
case reflect.Array:
elemRt := rt.Elem()
length := rt.Len()
if elemRt.Kind() == reflect.Uint8 {
// Special case: Bytearrays
oString, ok := o.(string)
if !ok {
*err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o)))
return
}
// if its data.Bytes, use hex; else use base64
dbty := reflect.TypeOf(data.Bytes{})
var buf []byte
var err_ error
if rt == dbty {
buf, err_ = hex.DecodeString(oString)
} else {
buf, err_ = base64.StdEncoding.DecodeString(oString)
}
if err_ != nil {
*err = err_
return
}
if len(buf) != length {
*err = errors.New(cmn.Fmt("Expected bytearray of length %v but got %v", length, len(buf)))
return
}
//log.Info("Read bytearray", "bytes", buf)
reflect.Copy(rv, reflect.ValueOf(buf))
} else {
oSlice, ok := o.([]interface{})
if !ok {
*err = errors.New(cmn.Fmt("Expected array of %v but got type %v", rt, reflect.TypeOf(o)))
return
}
if len(oSlice) != length {
*err = errors.New(cmn.Fmt("Expected array of length %v but got %v", length, len(oSlice)))
return
}
for i := 0; i < length; i++ {
elemRv := rv.Index(i)
readReflectJSON(elemRv, elemRt, opts, oSlice[i], err)
}
//log.Info("Read x-array", "x", elemRt, "length", length)
}
case reflect.Slice:
elemRt := rt.Elem()
if elemRt.Kind() == reflect.Uint8 {
// Special case: Byteslices
oString, ok := o.(string)
if !ok {
*err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o)))
return
}
// if its data.Bytes, use hex; else use base64
dbty := reflect.TypeOf(data.Bytes{})
var buf []byte
var err_ error
if rt == dbty {
buf, err_ = hex.DecodeString(oString)
} else {
buf, err_ = base64.StdEncoding.DecodeString(oString)
}
if err_ != nil {
*err = err_
return
}
//log.Info("Read byteslice", "bytes", byteslice)
rv.Set(reflect.ValueOf(buf))
} else {
// Read length
oSlice, ok := o.([]interface{})
if !ok {
*err = errors.New(cmn.Fmt("Expected array of %v but got type %v", rt, reflect.TypeOf(o)))
return
}
length := len(oSlice)
//log.Info("Read slice", "length", length)
sliceRv := reflect.MakeSlice(rt, length, length)
// Read elems
for i := 0; i < length; i++ {
elemRv := sliceRv.Index(i)
readReflectJSON(elemRv, elemRt, opts, oSlice[i], err)
}
rv.Set(sliceRv)
}
case reflect.Struct:
if rt == timeType {
// Special case: time.Time
str, ok := o.(string)
if !ok {
*err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o)))
return
}
// try three ways, seconds, milliseconds, or microseconds...
t, err_ := time.Parse(time.RFC3339Nano, str)
if err_ != nil {
*err = err_
return
}
rv.Set(reflect.ValueOf(t))
} else {
if typeInfo.Unwrap {
f := typeInfo.Fields[0]
fieldIdx, fieldType, opts := f.Index, f.Type, f.Options
fieldRv := rv.Field(fieldIdx)
readReflectJSON(fieldRv, fieldType, opts, o, err)
} else {
oMap, ok := o.(map[string]interface{})
if !ok {
*err = errors.New(cmn.Fmt("Expected map but got type %v", reflect.TypeOf(o)))
return
}
// TODO: ensure that all fields are set?
// TODO: disallow unknown oMap fields?
for _, fieldInfo := range typeInfo.Fields {
f := fieldInfo
fieldIdx, fieldType, opts := f.Index, f.Type, f.Options
value, ok := oMap[opts.JSONName]
if !ok {
continue // Skip missing fields.
}
fieldRv := rv.Field(fieldIdx)
readReflectJSON(fieldRv, fieldType, opts, value, err)
}
}
}
case reflect.String:
str, ok := o.(string)
if !ok {
*err = errors.New(cmn.Fmt("Expected string but got type %v", reflect.TypeOf(o)))
return
}
//log.Info("Read string", "str", str)
rv.SetString(str)
case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int:
num, ok := o.(float64)
if !ok {
*err = errors.New(cmn.Fmt("Expected numeric but got type %v", reflect.TypeOf(o)))
return
}
//log.Info("Read num", "num", num)
rv.SetInt(int64(num))
case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint:
num, ok := o.(float64)
if !ok {
*err = errors.New(cmn.Fmt("Expected numeric but got type %v", reflect.TypeOf(o)))
return
}
if num < 0 {
*err = errors.New(cmn.Fmt("Expected unsigned numeric but got %v", num))
return
}
//log.Info("Read num", "num", num)
rv.SetUint(uint64(num))
case reflect.Float64, reflect.Float32:
if !opts.Unsafe {
*err = errors.New("Wire float* support requires `wire:\"unsafe\"`")
return
}
num, ok := o.(float64)
if !ok {
*err = errors.New(cmn.Fmt("Expected numeric but got type %v", reflect.TypeOf(o)))
return
}
//log.Info("Read num", "num", num)
rv.SetFloat(num)
case reflect.Bool:
bl, ok := o.(bool)
if !ok {
*err = errors.New(cmn.Fmt("Expected boolean but got type %v", reflect.TypeOf(o)))
return
}
//log.Info("Read boolean", "boolean", bl)
rv.SetBool(bl)
default:
cmn.PanicSanity(cmn.Fmt("Unknown field type %v", rt.Kind()))
}
}

Loading…
Cancel
Save