mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-22 01:11:32 +00:00
time encoding in binary/reflect
This commit is contained in:
@ -7,6 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
. "github.com/tendermint/tendermint/common"
|
. "github.com/tendermint/tendermint/common"
|
||||||
)
|
)
|
||||||
@ -15,8 +16,9 @@ type TypeInfo struct {
|
|||||||
Type reflect.Type // The type
|
Type reflect.Type // The type
|
||||||
|
|
||||||
// Custom encoder/decoder
|
// Custom encoder/decoder
|
||||||
Encoder Encoder
|
// NOTE: Not used.
|
||||||
Decoder Decoder
|
BinaryEncoder Encoder
|
||||||
|
BinaryDecoder Decoder
|
||||||
|
|
||||||
// If Type is kind reflect.Interface
|
// If Type is kind reflect.Interface
|
||||||
ConcreteTypes map[byte]reflect.Type
|
ConcreteTypes map[byte]reflect.Type
|
||||||
@ -44,6 +46,15 @@ func GetTypeByteFromStruct(o interface{}) (hasTypeByte bool, typeByte byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Predeclaration of common types
|
||||||
|
var (
|
||||||
|
timeType = GetTypeFromStructDeclaration(struct{ time.Time }{})
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
rfc2822 = "Mon Jan 02 15:04:05 -0700 2006"
|
||||||
|
)
|
||||||
|
|
||||||
// If a type implements TypeByte, the byte is included
|
// If a type implements TypeByte, the byte is included
|
||||||
// as the first byte for encoding and decoding.
|
// as the first byte for encoding and decoding.
|
||||||
// This is primarily used to encode interfaces types.
|
// This is primarily used to encode interfaces types.
|
||||||
@ -155,8 +166,8 @@ func readReflect(rv reflect.Value, rt reflect.Type, r Unreader, n *int64, err *e
|
|||||||
typeInfo := GetTypeInfo(rt)
|
typeInfo := GetTypeInfo(rt)
|
||||||
|
|
||||||
// Custom decoder
|
// Custom decoder
|
||||||
if typeInfo.Decoder != nil {
|
if typeInfo.BinaryDecoder != nil {
|
||||||
decoded := typeInfo.Decoder(r, n, err)
|
decoded := typeInfo.BinaryDecoder(r, n, err)
|
||||||
rv.Set(reflect.ValueOf(decoded))
|
rv.Set(reflect.ValueOf(decoded))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -219,14 +230,21 @@ func readReflect(rv reflect.Value, rt reflect.Type, r Unreader, n *int64, err *e
|
|||||||
}
|
}
|
||||||
|
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
numFields := rt.NumField()
|
if rt == timeType {
|
||||||
for i := 0; i < numFields; i++ {
|
// Special case: time.Time
|
||||||
field := rt.Field(i)
|
num := ReadInt64(r, n, err)
|
||||||
if field.PkgPath != "" {
|
log.Debug(Fmt("Read time: %v", num))
|
||||||
continue
|
rv.Set(reflect.ValueOf(time.Unix(num, 0)))
|
||||||
|
} else {
|
||||||
|
numFields := rt.NumField()
|
||||||
|
for i := 0; i < numFields; i++ {
|
||||||
|
field := rt.Field(i)
|
||||||
|
if field.PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fieldRv := rv.Field(i)
|
||||||
|
readReflect(fieldRv, field.Type, r, n, err)
|
||||||
}
|
}
|
||||||
fieldRv := rv.Field(i)
|
|
||||||
readReflect(fieldRv, field.Type, r, n, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
@ -295,8 +313,8 @@ func writeReflect(rv reflect.Value, rt reflect.Type, w io.Writer, n *int64, err
|
|||||||
typeInfo := GetTypeInfo(rt)
|
typeInfo := GetTypeInfo(rt)
|
||||||
|
|
||||||
// Custom encoder, say for an interface type rt.
|
// Custom encoder, say for an interface type rt.
|
||||||
if typeInfo.Encoder != nil {
|
if typeInfo.BinaryEncoder != nil {
|
||||||
typeInfo.Encoder(rv.Interface(), w, n, err)
|
typeInfo.BinaryEncoder(rv.Interface(), w, n, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,14 +356,19 @@ func writeReflect(rv reflect.Value, rt reflect.Type, w io.Writer, n *int64, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
numFields := rt.NumField()
|
if rt == timeType {
|
||||||
for i := 0; i < numFields; i++ {
|
// Special case: time.Time
|
||||||
field := rt.Field(i)
|
WriteInt64(rv.Interface().(time.Time).Unix(), w, n, err)
|
||||||
if field.PkgPath != "" {
|
} else {
|
||||||
continue
|
numFields := rt.NumField()
|
||||||
|
for i := 0; i < numFields; i++ {
|
||||||
|
field := rt.Field(i)
|
||||||
|
if field.PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fieldRv := rv.Field(i)
|
||||||
|
writeReflect(fieldRv, field.Type, w, n, err)
|
||||||
}
|
}
|
||||||
fieldRv := rv.Field(i)
|
|
||||||
writeReflect(fieldRv, field.Type, w, n, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
@ -488,25 +511,41 @@ func readReflectJSON(rv reflect.Value, rt reflect.Type, o interface{}, err *erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
oMap, ok := o.(map[string]interface{})
|
if rt == timeType {
|
||||||
if !ok {
|
// Special case: time.Time
|
||||||
*err = errors.New(Fmt("Expected map but got type %v", reflect.TypeOf(o)))
|
str, ok := o.(string)
|
||||||
return
|
|
||||||
}
|
|
||||||
// TODO: ensure that all fields are set?
|
|
||||||
for name, value := range oMap {
|
|
||||||
field, ok := rt.FieldByName(name)
|
|
||||||
if !ok {
|
if !ok {
|
||||||
*err = errors.New(Fmt("Attempt to set unknown field %v", field.Name))
|
*err = errors.New(Fmt("Expected string but got type %v", reflect.TypeOf(o)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// JAE: I don't think golang reflect lets us set unexported fields, but just in case:
|
log.Debug(Fmt("Read time: %v", str))
|
||||||
if field.PkgPath != "" {
|
t, err_ := time.Parse(rfc2822, str)
|
||||||
*err = errors.New(Fmt("Attempt to set unexported field %v", field.Name))
|
if err_ != nil {
|
||||||
|
*err = err_
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fieldRv := rv.FieldByName(name)
|
rv.Set(reflect.ValueOf(t))
|
||||||
readReflectJSON(fieldRv, field.Type, value, err)
|
} else {
|
||||||
|
oMap, ok := o.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
*err = errors.New(Fmt("Expected map but got type %v", reflect.TypeOf(o)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO: ensure that all fields are set?
|
||||||
|
for name, value := range oMap {
|
||||||
|
field, ok := rt.FieldByName(name)
|
||||||
|
if !ok {
|
||||||
|
*err = errors.New(Fmt("Attempt to set unknown field %v", field.Name))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// JAE: I don't think golang reflect lets us set unexported fields, but just in case:
|
||||||
|
if field.PkgPath != "" {
|
||||||
|
*err = errors.New(Fmt("Attempt to set unexported field %v", field.Name))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fieldRv := rv.FieldByName(name)
|
||||||
|
readReflectJSON(fieldRv, field.Type, value, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
@ -550,12 +589,6 @@ func writeReflectJSON(rv reflect.Value, rt reflect.Type, w io.Writer, n *int64,
|
|||||||
// Get typeInfo
|
// Get typeInfo
|
||||||
typeInfo := GetTypeInfo(rt)
|
typeInfo := GetTypeInfo(rt)
|
||||||
|
|
||||||
// Custom encoder, say for an interface type rt.
|
|
||||||
if typeInfo.Encoder != nil {
|
|
||||||
typeInfo.Encoder(rv.Interface(), w, n, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dereference interface
|
// Dereference interface
|
||||||
if rt.Kind() == reflect.Interface {
|
if rt.Kind() == reflect.Interface {
|
||||||
rv = rv.Elem()
|
rv = rv.Elem()
|
||||||
@ -598,21 +631,36 @@ func writeReflectJSON(rv reflect.Value, rt reflect.Type, w io.Writer, n *int64,
|
|||||||
}
|
}
|
||||||
|
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
WriteTo([]byte("{"), w, n, err)
|
if rt == timeType {
|
||||||
numFields := rt.NumField()
|
// Special case: time.Time
|
||||||
for i := 0; i < numFields; i++ {
|
t := rv.Interface().(time.Time)
|
||||||
field := rt.Field(i)
|
str := t.Format(rfc2822)
|
||||||
if field.PkgPath != "" {
|
jsonBytes, err_ := json.Marshal(str)
|
||||||
continue
|
if err_ != nil {
|
||||||
|
*err = err_
|
||||||
|
return
|
||||||
}
|
}
|
||||||
fieldRv := rv.Field(i)
|
WriteTo(jsonBytes, w, n, err)
|
||||||
WriteTo([]byte(Fmt("\"%v\":", field.Name)), w, n, err)
|
} else {
|
||||||
writeReflectJSON(fieldRv, field.Type, w, n, err)
|
WriteTo([]byte("{"), w, n, err)
|
||||||
if i < numFields-1 {
|
numFields := rt.NumField()
|
||||||
WriteTo([]byte(","), w, n, err)
|
wroteField := false
|
||||||
|
for i := 0; i < numFields; i++ {
|
||||||
|
field := rt.Field(i)
|
||||||
|
if field.PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fieldRv := rv.Field(i)
|
||||||
|
if wroteField {
|
||||||
|
WriteTo([]byte(","), w, n, err)
|
||||||
|
} else {
|
||||||
|
wroteField = true
|
||||||
|
}
|
||||||
|
WriteTo([]byte(Fmt("\"%v\":", field.Name)), w, n, err)
|
||||||
|
writeReflectJSON(fieldRv, field.Type, w, n, err)
|
||||||
}
|
}
|
||||||
|
WriteTo([]byte("}"), w, n, err)
|
||||||
}
|
}
|
||||||
WriteTo([]byte("}"), w, n, err)
|
|
||||||
|
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
fallthrough
|
fallthrough
|
||||||
@ -635,3 +683,5 @@ func writeReflectJSON(rv reflect.Value, rt reflect.Type, w io.Writer, n *int64,
|
|||||||
WriteTo([]byte("]"), w, n, err)
|
WriteTo([]byte("]"), w, n, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -4,11 +4,13 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SimpleStruct struct {
|
type SimpleStruct struct {
|
||||||
String string
|
String string
|
||||||
Bytes []byte
|
Bytes []byte
|
||||||
|
Time time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
@ -75,6 +77,7 @@ func constructBasic() interface{} {
|
|||||||
SimpleStruct{
|
SimpleStruct{
|
||||||
String: "String",
|
String: "String",
|
||||||
Bytes: []byte("Bytes"),
|
Bytes: []byte("Bytes"),
|
||||||
|
Time: time.Unix(123, 0),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return cat
|
return cat
|
||||||
@ -92,6 +95,9 @@ func validateBasic(o interface{}, t *testing.T) {
|
|||||||
if string(cat.Bytes) != "Bytes" {
|
if string(cat.Bytes) != "Bytes" {
|
||||||
t.Errorf("Expected cat2.Bytes == 'Bytes', got %X", cat.Bytes)
|
t.Errorf("Expected cat2.Bytes == 'Bytes', got %X", cat.Bytes)
|
||||||
}
|
}
|
||||||
|
if cat.Time.Unix() != 123 {
|
||||||
|
t.Errorf("Expected cat2.Time == 'Unix(123)', got %v", cat.Time)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
@ -20,6 +20,7 @@ type Node struct {
|
|||||||
sw *p2p.Switch
|
sw *p2p.Switch
|
||||||
book *p2p.AddrBook
|
book *p2p.AddrBook
|
||||||
pexReactor *p2p.PEXReactor
|
pexReactor *p2p.PEXReactor
|
||||||
|
blockStore *block.BlockStore
|
||||||
mempoolReactor *mempool_.MempoolReactor
|
mempoolReactor *mempool_.MempoolReactor
|
||||||
consensusReactor *consensus.ConsensusReactor
|
consensusReactor *consensus.ConsensusReactor
|
||||||
state *state_.State
|
state *state_.State
|
||||||
@ -65,6 +66,7 @@ func NewNode() *Node {
|
|||||||
sw: sw,
|
sw: sw,
|
||||||
book: book,
|
book: book,
|
||||||
pexReactor: pexReactor,
|
pexReactor: pexReactor,
|
||||||
|
blockStore: blockStore,
|
||||||
mempoolReactor: mempoolReactor,
|
mempoolReactor: mempoolReactor,
|
||||||
consensusReactor: consensusReactor,
|
consensusReactor: consensusReactor,
|
||||||
state: state,
|
state: state,
|
||||||
@ -137,7 +139,8 @@ func daemon() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run the RPC server.
|
// Run the RPC server.
|
||||||
if config.Config.RPC.HTTPPort != 0 {
|
if config.Config.RPC.HTTPLAddr != "" {
|
||||||
|
rpc.SetRPCBlockStore(n.blockStore)
|
||||||
rpc.SetRPCState(n.state)
|
rpc.SetRPCState(n.state)
|
||||||
rpc.SetRPCMempoolReactor(n.mempoolReactor)
|
rpc.SetRPCMempoolReactor(n.mempoolReactor)
|
||||||
rpc.StartHTTPServer()
|
rpc.StartHTTPServer()
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@ -50,7 +51,7 @@ type SMTPConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RPCConfig struct {
|
type RPCConfig struct {
|
||||||
HTTPPort uint
|
HTTPLAddr string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *ConfigType) validate() error {
|
func (cfg *ConfigType) validate() error {
|
||||||
@ -66,6 +67,17 @@ func (cfg *ConfigType) validate() error {
|
|||||||
if cfg.DB.Backend == "" {
|
if cfg.DB.Backend == "" {
|
||||||
return errors.New("DB.Backend must be set")
|
return errors.New("DB.Backend must be set")
|
||||||
}
|
}
|
||||||
|
if cfg.RPC.HTTPLAddr == "" {
|
||||||
|
fmt.Println("Set RPC.HTTPLAddr to \"0.0.0.0:8888\" in your config.json to enable the RPC API server.")
|
||||||
|
} else {
|
||||||
|
_, port, err := net.SplitHostPort(cfg.RPC.HTTPLAddr)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(Fmt("RPC.HTTPLAddr is invalid. %v", err))
|
||||||
|
}
|
||||||
|
if port == "" || port == "0" {
|
||||||
|
return errors.New("RPC.HTTPLAddr is invalid. Port number must be defined")
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +126,7 @@ func init() {
|
|||||||
Alert: AlertConfig{},
|
Alert: AlertConfig{},
|
||||||
SMTP: SMTPConfig{},
|
SMTP: SMTPConfig{},
|
||||||
RPC: RPCConfig{
|
RPC: RPCConfig{
|
||||||
HTTPPort: 8888,
|
HTTPLAddr: "0.0.0.0:0",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,6 +144,7 @@ func parseFlags(flags *flag.FlagSet, args []string) (printHelp bool) {
|
|||||||
flags.BoolVar(&printHelp, "help", false, "Print this help message.")
|
flags.BoolVar(&printHelp, "help", false, "Print this help message.")
|
||||||
flags.StringVar(&Config.LAddr, "laddr", Config.LAddr, "Listen address. (0.0.0.0:0 means any interface, any port)")
|
flags.StringVar(&Config.LAddr, "laddr", Config.LAddr, "Listen address. (0.0.0.0:0 means any interface, any port)")
|
||||||
flags.StringVar(&Config.SeedNode, "seed", Config.SeedNode, "Address of seed node")
|
flags.StringVar(&Config.SeedNode, "seed", Config.SeedNode, "Address of seed node")
|
||||||
|
flags.StringVar(&Config.RPC.HTTPLAddr, "rpc_http_laddr", Config.RPC.HTTPLAddr, "RPC listen address. (0.0.0.0:0 means any interface, any port)")
|
||||||
flags.Parse(args)
|
flags.Parse(args)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -152,11 +165,11 @@ func ParseFlags(args []string) {
|
|||||||
Config = ConfigType{}
|
Config = ConfigType{}
|
||||||
err = json.Unmarshal(configBytes, &Config)
|
err = json.Unmarshal(configBytes, &Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Exit(Fmt("Invalid configuration file %s: %v", configFile, err))
|
Exit(Fmt("Invalid configuration file %s:\n%v\n", configFile, err))
|
||||||
}
|
}
|
||||||
err = Config.validate()
|
err = Config.validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Exit(Fmt("Invalid configuration file %s: %v", configFile, err))
|
Exit(Fmt("Invalid configuration file %s:\n%v\n", configFile, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to parse arg flags, which can override file configuration.
|
// try to parse arg flags, which can override file configuration.
|
||||||
|
@ -820,7 +820,7 @@ func (cs *ConsensusState) TryFinalizeCommit(height uint) bool {
|
|||||||
}
|
}
|
||||||
hash, header, _ := cs.Commits.TwoThirdsMajority()
|
hash, header, _ := cs.Commits.TwoThirdsMajority()
|
||||||
if !cs.ProposalBlock.HashesTo(hash) {
|
if !cs.ProposalBlock.HashesTo(hash) {
|
||||||
panic(Fmt("Expected ProposalBlock to hash to commit hash"))
|
panic(Fmt("Expected ProposalBlock to hash to commit hash. Expected %X, got %X", hash, cs.ProposalBlock.Hash()))
|
||||||
}
|
}
|
||||||
if !cs.ProposalBlockParts.HasHeader(header) {
|
if !cs.ProposalBlockParts.HasHeader(header) {
|
||||||
panic(Fmt("Expected ProposalBlockParts header to be commit header"))
|
panic(Fmt("Expected ProposalBlockParts header to be commit header"))
|
||||||
|
@ -2,12 +2,36 @@ package rpc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
//. "github.com/tendermint/tendermint/block"
|
|
||||||
|
. "github.com/tendermint/tendermint/block"
|
||||||
|
. "github.com/tendermint/tendermint/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BlockHandler(w http.ResponseWriter, r *http.Request) {
|
type BlockchainInfoResponse struct {
|
||||||
//height, _ := GetParamUint64Safe(r, "height")
|
LastHeight uint
|
||||||
//count, _ := GetParamUint64Safe(r, "count")
|
BlockMetas []*BlockMeta
|
||||||
|
}
|
||||||
ReturnJSON(API_OK, "hello")
|
|
||||||
|
func BlockchainInfoHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
minHeight, _ := GetParamUint(r, "min_height")
|
||||||
|
maxHeight, _ := GetParamUint(r, "max_height")
|
||||||
|
if maxHeight == 0 {
|
||||||
|
maxHeight = blockStore.Height()
|
||||||
|
}
|
||||||
|
if minHeight == 0 {
|
||||||
|
minHeight = MaxUint(0, maxHeight-20)
|
||||||
|
}
|
||||||
|
|
||||||
|
blockMetas := []*BlockMeta{}
|
||||||
|
for height := minHeight; height <= maxHeight; height++ {
|
||||||
|
blockMetas = append(blockMetas, blockStore.LoadBlockMeta(height))
|
||||||
|
}
|
||||||
|
|
||||||
|
res := BlockchainInfoResponse{
|
||||||
|
LastHeight: blockStore.Height(),
|
||||||
|
BlockMetas: blockMetas,
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteAPIResponse(w, API_OK, res)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,14 @@
|
|||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/alert"
|
"github.com/tendermint/tendermint/alert"
|
||||||
. "github.com/tendermint/tendermint/common"
|
"github.com/tendermint/tendermint/binary"
|
||||||
)
|
)
|
||||||
|
|
||||||
type APIStatus string
|
type APIStatus string
|
||||||
@ -33,12 +31,33 @@ func (res APIResponse) Error() string {
|
|||||||
return fmt.Sprintf("Status(%v) %v", res.Status, res.Data)
|
return fmt.Sprintf("Status(%v) %v", res.Status, res.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throws a panic which the RecoverAndLogHandler catches.
|
func WriteAPIResponse(w http.ResponseWriter, status APIStatus, data interface{}) {
|
||||||
func ReturnJSON(status APIStatus, data interface{}) {
|
|
||||||
res := APIResponse{}
|
res := APIResponse{}
|
||||||
res.Status = status
|
res.Status = status
|
||||||
res.Data = data
|
res.Data = data
|
||||||
panic(res)
|
|
||||||
|
buf, n, err := new(bytes.Buffer), new(int64), new(error)
|
||||||
|
binary.WriteJSON(res, w, n, err)
|
||||||
|
if *err != nil {
|
||||||
|
log.Warn("Failed to write JSON APIResponse", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
switch res.Status {
|
||||||
|
case API_OK:
|
||||||
|
w.WriteHeader(200)
|
||||||
|
case API_ERROR:
|
||||||
|
w.WriteHeader(400)
|
||||||
|
case API_UNAUTHORIZED:
|
||||||
|
w.WriteHeader(401)
|
||||||
|
case API_INVALID_PARAM:
|
||||||
|
w.WriteHeader(420)
|
||||||
|
case API_REDIRECT:
|
||||||
|
w.WriteHeader(430)
|
||||||
|
default:
|
||||||
|
w.WriteHeader(440)
|
||||||
|
}
|
||||||
|
w.Write(buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wraps an HTTP handler, adding error logging.
|
// Wraps an HTTP handler, adding error logging.
|
||||||
@ -52,16 +71,18 @@ func RecoverAndLogHandler(handler http.Handler) http.Handler {
|
|||||||
begin := time.Now()
|
begin := time.Now()
|
||||||
|
|
||||||
// Common headers
|
// Common headers
|
||||||
origin := r.Header.Get("Origin")
|
/*
|
||||||
originUrl, err := url.Parse(origin)
|
origin := r.Header.Get("Origin")
|
||||||
if err == nil {
|
originUrl, err := url.Parse(origin)
|
||||||
originHost := strings.Split(originUrl.Host, ":")[0]
|
if err == nil {
|
||||||
if strings.HasSuffix(originHost, ".ftnox.com") {
|
originHost := strings.Split(originUrl.Host, ":")[0]
|
||||||
rww.Header().Set("Access-Control-Allow-Origin", origin)
|
if strings.HasSuffix(originHost, ".tendermint.com") {
|
||||||
rww.Header().Set("Access-Control-Allow-Credentials", "true")
|
rww.Header().Set("Access-Control-Allow-Origin", origin)
|
||||||
rww.Header().Set("Access-Control-Expose-Headers", "X-Server-Time")
|
rww.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||||
|
rww.Header().Set("Access-Control-Expose-Headers", "X-Server-Time")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
*/
|
||||||
rww.Header().Set("X-Server-Time", fmt.Sprintf("%v", begin.Unix()))
|
rww.Header().Set("X-Server-Time", fmt.Sprintf("%v", begin.Unix()))
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
@ -72,26 +93,7 @@ func RecoverAndLogHandler(handler http.Handler) http.Handler {
|
|||||||
|
|
||||||
// If APIResponse,
|
// If APIResponse,
|
||||||
if res, ok := e.(APIResponse); ok {
|
if res, ok := e.(APIResponse); ok {
|
||||||
resJSON, err := json.Marshal(res)
|
WriteAPIResponse(rww, res.Status, res.Data)
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
rww.Header().Set("Content-Type", "application/json")
|
|
||||||
switch res.Status {
|
|
||||||
case API_OK:
|
|
||||||
rww.WriteHeader(200)
|
|
||||||
case API_ERROR:
|
|
||||||
rww.WriteHeader(400)
|
|
||||||
case API_UNAUTHORIZED:
|
|
||||||
rww.WriteHeader(401)
|
|
||||||
case API_INVALID_PARAM:
|
|
||||||
rww.WriteHeader(420)
|
|
||||||
case API_REDIRECT:
|
|
||||||
rww.WriteHeader(430)
|
|
||||||
default:
|
|
||||||
rww.WriteHeader(440)
|
|
||||||
}
|
|
||||||
rww.Write(resJSON)
|
|
||||||
} else {
|
} else {
|
||||||
// For the rest,
|
// For the rest,
|
||||||
rww.WriteHeader(http.StatusInternalServerError)
|
rww.WriteHeader(http.StatusInternalServerError)
|
||||||
@ -105,7 +107,11 @@ func RecoverAndLogHandler(handler http.Handler) http.Handler {
|
|||||||
if rww.Status == -1 {
|
if rww.Status == -1 {
|
||||||
rww.Status = 200
|
rww.Status = 200
|
||||||
}
|
}
|
||||||
log.Debug(Fmt("%s %s %v %v %s", r.RemoteAddr, r.Method, rww.Status, durationMS, r.URL))
|
log.Debug("Served HTTP response",
|
||||||
|
"method", r.Method, "url", r.URL,
|
||||||
|
"status", rww.Status, "duration", durationMS,
|
||||||
|
"remoteAddr", r.RemoteAddr,
|
||||||
|
)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
handler.ServeHTTP(rww, r)
|
handler.ServeHTTP(rww, r)
|
||||||
|
@ -67,6 +67,15 @@ func GetParamUint64(r *http.Request, param string) (uint64, error) {
|
|||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetParamUint(r *http.Request, param string) (uint, error) {
|
||||||
|
s := GetParam(r, param)
|
||||||
|
i, err := strconv.ParseUint(s, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, Errorf(param, err.Error())
|
||||||
|
}
|
||||||
|
return uint(i), nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetParamRegexp(r *http.Request, param string, re *regexp.Regexp) (string, error) {
|
func GetParamRegexp(r *http.Request, param string, re *regexp.Regexp) (string, error) {
|
||||||
s := GetParam(r, param)
|
s := GetParam(r, param)
|
||||||
if !re.MatchString(s) {
|
if !re.MatchString(s) {
|
||||||
|
@ -1,24 +1,20 @@
|
|||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
. "github.com/tendermint/tendermint/config"
|
|
||||||
. "github.com/tendermint/tendermint/common"
|
. "github.com/tendermint/tendermint/common"
|
||||||
|
. "github.com/tendermint/tendermint/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StartHTTPServer() {
|
func StartHTTPServer() {
|
||||||
|
|
||||||
http.HandleFunc("/block", BlockHandler)
|
http.HandleFunc("/block", BlockchainInfoHandler)
|
||||||
http.HandleFunc("/mempool", MempoolHandler)
|
http.HandleFunc("/mempool", MempoolHandler)
|
||||||
|
|
||||||
// Serve HTTP on localhost only.
|
log.Info(Fmt("Starting RPC HTTP server on %s", Config.RPC.HTTPLAddr))
|
||||||
// Let something like Nginx handle HTTPS connections.
|
|
||||||
address := fmt.Sprintf("127.0.0.1:%v", Config.RPC.HTTPPort)
|
|
||||||
log.Info(Fmt("Starting RPC HTTP server on http://%s", address))
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
log.Crit("%v", http.ListenAndServe(address, RecoverAndLogHandler(http.DefaultServeMux)))
|
log.Crit("RPC HTTPServer stopped", "result", http.ListenAndServe(Config.RPC.HTTPLAddr, RecoverAndLogHandler(http.DefaultServeMux)))
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -15,25 +15,29 @@ func MempoolHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
//count, _ := GetParamUint64Safe(r, "count")
|
//count, _ := GetParamUint64Safe(r, "count")
|
||||||
txBytes, err := GetParamByteSlice(r, "tx_bytes")
|
txBytes, err := GetParamByteSlice(r, "tx_bytes")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ReturnJSON(API_INVALID_PARAM, Fmt("Invalid tx_bytes: %v", err))
|
WriteAPIResponse(w, API_INVALID_PARAM, Fmt("Invalid tx_bytes: %v", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
reader, n := bytes.NewReader(txBytes), new(int64)
|
reader, n := bytes.NewReader(txBytes), new(int64)
|
||||||
tx_ := ReadBinary(struct{ Tx }{}, reader, n, &err).(struct{ Tx })
|
tx_ := ReadBinary(struct{ Tx }{}, reader, n, &err).(struct{ Tx })
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ReturnJSON(API_INVALID_PARAM, Fmt("Invalid tx_bytes: %v", err))
|
WriteAPIResponse(w, API_INVALID_PARAM, Fmt("Invalid tx_bytes: %v", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
tx := tx_.Tx
|
tx := tx_.Tx
|
||||||
|
|
||||||
err = mempoolReactor.BroadcastTx(tx)
|
err = mempoolReactor.BroadcastTx(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ReturnJSON(API_ERROR, Fmt("Error broadcasting transaction: %v", err))
|
WriteAPIResponse(w, API_ERROR, Fmt("Error broadcasting transaction: %v", err))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonBytes := JSONBytes(tx)
|
jsonBytes := JSONBytes(tx)
|
||||||
fmt.Println(">>", string(jsonBytes))
|
fmt.Println(">>", string(jsonBytes))
|
||||||
|
|
||||||
ReturnJSON(API_OK, Fmt("Broadcasted tx: %X", tx))
|
WriteAPIResponse(w, API_OK, Fmt("Broadcasted tx: %X", tx))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
14
rpc/rpc.go
14
rpc/rpc.go
@ -1,17 +1,23 @@
|
|||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
block_ "github.com/tendermint/tendermint/block"
|
||||||
mempool_ "github.com/tendermint/tendermint/mempool"
|
mempool_ "github.com/tendermint/tendermint/mempool"
|
||||||
state_ "github.com/tendermint/tendermint/state"
|
state_ "github.com/tendermint/tendermint/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var blockStore *block_.BlockStore
|
||||||
var state *state_.State
|
var state *state_.State
|
||||||
var mempoolReactor *mempool_.MempoolReactor
|
var mempoolReactor *mempool_.MempoolReactor
|
||||||
|
|
||||||
func SetRPCState(state__ *state_.State) {
|
func SetRPCBlockStore(bs *block_.BlockStore) {
|
||||||
state = state__
|
blockStore = bs
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetRPCMempoolReactor(mempoolReactor_ *mempool_.MempoolReactor) {
|
func SetRPCState(s *state_.State) {
|
||||||
mempoolReactor = mempoolReactor_
|
state = s
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetRPCMempoolReactor(mr *mempool_.MempoolReactor) {
|
||||||
|
mempoolReactor = mr
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user