@ -4,7 +4,6 @@ import (
"bytes"
"bytes"
"encoding/hex"
"encoding/hex"
"encoding/json"
"encoding/json"
"errors"
"fmt"
"fmt"
"io/ioutil"
"io/ioutil"
"net/http"
"net/http"
@ -14,10 +13,10 @@ import (
"time"
"time"
"github.com/gorilla/websocket"
"github.com/gorilla/websocket"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-events"
. "github.com/tendermint/go-rpc/types"
"github.com/tendermint/go-wire"
cmn "github.com/tendermint/go-common"
events "github.com/tendermint/go-events"
types "github.com/tendermint/go-rpc/types"
wire "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.
// Adds a route for each function in the funcMap, as well as general jsonrpc and websocket handlers for all functions.
@ -105,75 +104,99 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc) http.HandlerFunc {
return
return
}
}
var request RPCRequest
var request types . RPCRequest
err := json . Unmarshal ( b , & request )
err := json . Unmarshal ( b , & request )
if err != nil {
if err != nil {
WriteRPCResponseHTTP ( w , NewRPCResponse ( "" , nil , fmt . Sprintf ( "Error unmarshalling request: %v" , err . Error ( ) ) ) )
WriteRPCResponseHTTP ( w , types . NewRPCResponse ( "" , nil , fmt . Sprintf ( "Error unmarshalling request: %v" , err . Error ( ) ) ) )
return
return
}
}
if len ( r . URL . Path ) > 1 {
if len ( r . URL . Path ) > 1 {
WriteRPCResponseHTTP ( w , NewRPCResponse ( request . ID , nil , fmt . Sprintf ( "Invalid JSONRPC endpoint %s" , r . URL . Path ) ) )
WriteRPCResponseHTTP ( w , types . NewRPCResponse ( request . ID , nil , fmt . Sprintf ( "Invalid JSONRPC endpoint %s" , r . URL . Path ) ) )
return
return
}
}
rpcFunc := funcMap [ request . Method ]
rpcFunc := funcMap [ request . Method ]
if rpcFunc == nil {
if rpcFunc == nil {
WriteRPCResponseHTTP ( w , NewRPCResponse ( request . ID , nil , "RPC method unknown: " + request . Method ) )
WriteRPCResponseHTTP ( w , types . NewRPCResponse ( request . ID , nil , "RPC method unknown: " + request . Method ) )
return
return
}
}
if rpcFunc . ws {
if rpcFunc . ws {
WriteRPCResponseHTTP ( w , NewRPCResponse ( request . ID , nil , "RPC method is only for websockets: " + request . Method ) )
WriteRPCResponseHTTP ( w , types . NewRPCResponse ( request . ID , nil , "RPC method is only for websockets: " + request . Method ) )
return
return
}
}
args , err := jsonParamsToArgs ( rpcFunc , request . Params )
args , err := jsonParamsToArgs ( rpcFunc , request . Params )
if err != nil {
if err != nil {
WriteRPCResponseHTTP ( w , NewRPCResponse ( request . ID , nil , fmt . Sprintf ( "Error converting json params to arguments: %v" , err . Error ( ) ) ) )
WriteRPCResponseHTTP ( w , types . NewRPCResponse ( request . ID , nil , fmt . Sprintf ( "Error converting json params to arguments: %v" , err . Error ( ) ) ) )
return
return
}
}
returns := rpcFunc . f . Call ( args )
returns := rpcFunc . f . Call ( args )
log . Info ( "HTTPJSONRPC" , "method" , request . Method , "args" , args , "returns" , returns )
log . Info ( "HTTPJSONRPC" , "method" , request . Method , "args" , args , "returns" , returns )
result , err := unreflectResult ( returns )
result , err := unreflectResult ( returns )
if err != nil {
if err != nil {
WriteRPCResponseHTTP ( w , NewRPCResponse ( request . ID , result , fmt . Sprintf ( "Error unreflecting result: %v" , err . Error ( ) ) ) )
WriteRPCResponseHTTP ( w , types . NewRPCResponse ( request . ID , result , fmt . Sprintf ( "Error unreflecting result: %v" , err . Error ( ) ) ) )
return
return
}
}
WriteRPCResponseHTTP ( w , NewRPCResponse ( request . ID , result , "" ) )
WriteRPCResponseHTTP ( w , types . NewRPCResponse ( request . ID , result , "" ) )
}
}
}
}
// Convert a list of interfaces to properly typed values
// Convert a list of interfaces to properly typed values
func jsonParamsToArgs ( rpcFunc * RPCFunc , params [ ] interface { } ) ( [ ] reflect . Value , error ) {
func jsonParamsToArgs ( rpcFunc * RPCFunc , params map [ string ] interface { } ) ( [ ] reflect . Value , error ) {
if len ( rpcFunc . argNames ) != len ( params ) {
if len ( rpcFunc . argNames ) != len ( params ) {
return nil , errors . New ( fmt . Sprint f( "Expected %v parameters (%v), got %v (%v)" ,
len ( rpcFunc . argNames ) , rpcFunc . argNames , len ( params ) , params ) )
return nil , fmt . Error f( "Expected %v parameters (%v), got %v (%v)" ,
len ( rpcFunc . argNames ) , rpcFunc . argNames , len ( params ) , params )
}
}
values := make ( [ ] reflect . Value , len ( params ) )
values := make ( [ ] reflect . Value , len ( params ) )
for i , p := range params {
for name , param := range params {
i := indexOf ( name , rpcFunc . argNames )
if - 1 == i {
return nil , fmt . Errorf ( "%s is not an argument (args: %v)" , name , rpcFunc . argNames )
}
ty := rpcFunc . args [ i ]
ty := rpcFunc . args [ i ]
v , err := _jsonObjectToArg ( ty , p )
v , err := _jsonObjectToArg ( ty , param )
if err != nil {
if err != nil {
return nil , err
return nil , err
}
}
values [ i ] = v
values [ i ] = v
}
}
return values , nil
return values , nil
}
}
// indexOf returns index of a string in a slice of strings, -1 if not found.
func indexOf ( value string , values [ ] string ) int {
for i , v := range values {
if v == value {
return i
}
}
return - 1
}
// Same as above, but with the first param the websocket connection
// Same as above, but with the first param the websocket connection
func jsonParamsToArgsWS ( rpcFunc * RPCFunc , params [ ] interface { } , wsCtx WSRPCContext ) ( [ ] reflect . Value , error ) {
func jsonParamsToArgsWS ( rpcFunc * RPCFunc , params map [ string ] interface { } , wsCtx types . WSRPCContext ) ( [ ] reflect . Value , error ) {
if len ( rpcFunc . argNames ) != len ( params ) {
if len ( rpcFunc . argNames ) != len ( params ) {
return nil , errors . New ( fmt . Sprintf ( "Expected %v parameters (%v), got %v (%v)" ,
len ( rpcFunc . argNames ) - 1 , rpcFunc . argNames [ 1 : ] , len ( params ) , params ) )
return nil , fmt . Error f( "Expected %v parameters (%v), got %v (%v)" ,
len ( rpcFunc . argNames ) - 1 , rpcFunc . argNames [ 1 : ] , len ( params ) , params )
}
}
values := make ( [ ] reflect . Value , len ( params ) + 1 )
values := make ( [ ] reflect . Value , len ( params ) + 1 )
values [ 0 ] = reflect . ValueOf ( wsCtx )
values [ 0 ] = reflect . ValueOf ( wsCtx )
for i , p := range params {
for name , param := range params {
i := indexOf ( name , rpcFunc . argNames )
if - 1 == i {
return nil , fmt . Errorf ( "%s is not an argument (args: %v)" , name , rpcFunc . argNames )
}
ty := rpcFunc . args [ i + 1 ]
ty := rpcFunc . args [ i + 1 ]
v , err := _jsonObjectToArg ( ty , p )
v , err := _jsonObjectToArg ( ty , param )
if err != nil {
if err != nil {
return nil , err
return nil , err
}
}
values [ i + 1 ] = v
values [ i + 1 ] = v
}
}
return values , nil
return values , nil
}
}
@ -197,7 +220,7 @@ func makeHTTPHandler(rpcFunc *RPCFunc) func(http.ResponseWriter, *http.Request)
// Exception for websocket endpoints
// Exception for websocket endpoints
if rpcFunc . ws {
if rpcFunc . ws {
return func ( w http . ResponseWriter , r * http . Request ) {
return func ( w http . ResponseWriter , r * http . Request ) {
WriteRPCResponseHTTP ( w , NewRPCResponse ( "" , nil , "This RPC method is only for websockets" ) )
WriteRPCResponseHTTP ( w , types . NewRPCResponse ( "" , nil , "This RPC method is only for websockets" ) )
}
}
}
}
// All other endpoints
// All other endpoints
@ -205,17 +228,17 @@ func makeHTTPHandler(rpcFunc *RPCFunc) func(http.ResponseWriter, *http.Request)
log . Debug ( "HTTP HANDLER" , "req" , r )
log . Debug ( "HTTP HANDLER" , "req" , r )
args , err := httpParamsToArgs ( rpcFunc , r )
args , err := httpParamsToArgs ( rpcFunc , r )
if err != nil {
if err != nil {
WriteRPCResponseHTTP ( w , NewRPCResponse ( "" , nil , fmt . Sprintf ( "Error converting http params to args: %v" , err . Error ( ) ) ) )
WriteRPCResponseHTTP ( w , types . NewRPCResponse ( "" , nil , fmt . Sprintf ( "Error converting http params to args: %v" , err . Error ( ) ) ) )
return
return
}
}
returns := rpcFunc . f . Call ( args )
returns := rpcFunc . f . Call ( args )
log . Info ( "HTTPRestRPC" , "method" , r . URL . Path , "args" , args , "returns" , returns )
log . Info ( "HTTPRestRPC" , "method" , r . URL . Path , "args" , args , "returns" , returns )
result , err := unreflectResult ( returns )
result , err := unreflectResult ( returns )
if err != nil {
if err != nil {
WriteRPCResponseHTTP ( w , NewRPCResponse ( "" , nil , fmt . Sprintf ( "Error unreflecting result: %v" , err . Error ( ) ) ) )
WriteRPCResponseHTTP ( w , types . NewRPCResponse ( "" , nil , fmt . Sprintf ( "Error unreflecting result: %v" , err . Error ( ) ) ) )
return
return
}
}
WriteRPCResponseHTTP ( w , NewRPCResponse ( "" , result , "" ) )
WriteRPCResponseHTTP ( w , types . NewRPCResponse ( "" , result , "" ) )
}
}
}
}
@ -313,11 +336,11 @@ const (
// contains listener id, underlying ws connection,
// contains listener id, underlying ws connection,
// and the event switch for subscribing to events
// and the event switch for subscribing to events
type wsConnection struct {
type wsConnection struct {
BaseService
cmn . BaseService
remoteAddr string
remoteAddr string
baseConn * websocket . Conn
baseConn * websocket . Conn
writeChan chan RPCResponse
writeChan chan types . RPCResponse
readTimeout * time . Timer
readTimeout * time . Timer
pingTicker * time . Ticker
pingTicker * time . Ticker
@ -330,11 +353,11 @@ func NewWSConnection(baseConn *websocket.Conn, funcMap map[string]*RPCFunc, evsw
wsc := & wsConnection {
wsc := & wsConnection {
remoteAddr : baseConn . RemoteAddr ( ) . String ( ) ,
remoteAddr : baseConn . RemoteAddr ( ) . String ( ) ,
baseConn : baseConn ,
baseConn : baseConn ,
writeChan : make ( chan RPCResponse , writeChanCapacity ) , // error when full.
writeChan : make ( chan types . RPCResponse , writeChanCapacity ) , // error when full.
funcMap : funcMap ,
funcMap : funcMap ,
evsw : evsw ,
evsw : evsw ,
}
}
wsc . BaseService = * NewBaseService ( log , "wsConnection" , wsc )
wsc . BaseService = * cmn . NewBaseService ( log , "wsConnection" , wsc )
return wsc
return wsc
}
}
@ -399,7 +422,7 @@ func (wsc *wsConnection) GetEventSwitch() events.EventSwitch {
// Implements WSRPCConnection
// Implements WSRPCConnection
// Blocking write to writeChan until service stops.
// Blocking write to writeChan until service stops.
// Goroutine-safe
// Goroutine-safe
func ( wsc * wsConnection ) WriteRPCResponse ( resp RPCResponse ) {
func ( wsc * wsConnection ) WriteRPCResponse ( resp types . RPCResponse ) {
select {
select {
case <- wsc . Quit :
case <- wsc . Quit :
return
return
@ -410,7 +433,7 @@ func (wsc *wsConnection) WriteRPCResponse(resp RPCResponse) {
// Implements WSRPCConnection
// Implements WSRPCConnection
// Nonblocking write.
// Nonblocking write.
// Goroutine-safe
// Goroutine-safe
func ( wsc * wsConnection ) TryWriteRPCResponse ( resp RPCResponse ) bool {
func ( wsc * wsConnection ) TryWriteRPCResponse ( resp types . RPCResponse ) bool {
select {
select {
case <- wsc . Quit :
case <- wsc . Quit :
return false
return false
@ -444,11 +467,11 @@ func (wsc *wsConnection) readRoutine() {
wsc . Stop ( )
wsc . Stop ( )
return
return
}
}
var request RPCRequest
var request types . RPCRequest
err = json . Unmarshal ( in , & request )
err = json . Unmarshal ( in , & request )
if err != nil {
if err != nil {
errStr := fmt . Sprintf ( "Error unmarshaling data: %s" , err . Error ( ) )
errStr := fmt . Sprintf ( "Error unmarshaling data: %s" , err . Error ( ) )
wsc . WriteRPCResponse ( NewRPCResponse ( request . ID , nil , errStr ) )
wsc . WriteRPCResponse ( types . NewRPCResponse ( request . ID , nil , errStr ) )
continue
continue
}
}
@ -456,28 +479,28 @@ func (wsc *wsConnection) readRoutine() {
rpcFunc := wsc . funcMap [ request . Method ]
rpcFunc := wsc . funcMap [ request . Method ]
if rpcFunc == nil {
if rpcFunc == nil {
wsc . WriteRPCResponse ( NewRPCResponse ( request . ID , nil , "RPC method unknown: " + request . Method ) )
wsc . WriteRPCResponse ( types . NewRPCResponse ( request . ID , nil , "RPC method unknown: " + request . Method ) )
continue
continue
}
}
var args [ ] reflect . Value
var args [ ] reflect . Value
if rpcFunc . ws {
if rpcFunc . ws {
wsCtx := WSRPCContext { Request : request , WSRPCConnection : wsc }
wsCtx := types . WSRPCContext { Request : request , WSRPCConnection : wsc }
args , err = jsonParamsToArgsWS ( rpcFunc , request . Params , wsCtx )
args , err = jsonParamsToArgsWS ( rpcFunc , request . Params , wsCtx )
} else {
} else {
args , err = jsonParamsToArgs ( rpcFunc , request . Params )
args , err = jsonParamsToArgs ( rpcFunc , request . Params )
}
}
if err != nil {
if err != nil {
wsc . WriteRPCResponse ( NewRPCResponse ( request . ID , nil , err . Error ( ) ) )
wsc . WriteRPCResponse ( types . NewRPCResponse ( request . ID , nil , err . Error ( ) ) )
continue
continue
}
}
returns := rpcFunc . f . Call ( args )
returns := rpcFunc . f . Call ( args )
log . Info ( "WSJSONRPC" , "method" , request . Method , "args" , args , "returns" , returns )
log . Info ( "WSJSONRPC" , "method" , request . Method , "args" , args , "returns" , returns )
result , err := unreflectResult ( returns )
result , err := unreflectResult ( returns )
if err != nil {
if err != nil {
wsc . WriteRPCResponse ( NewRPCResponse ( request . ID , nil , err . Error ( ) ) )
wsc . WriteRPCResponse ( types . NewRPCResponse ( request . ID , nil , err . Error ( ) ) )
continue
continue
} else {
} else {
wsc . WriteRPCResponse ( NewRPCResponse ( request . ID , result , "" ) )
wsc . WriteRPCResponse ( types . NewRPCResponse ( request . ID , result , "" ) )
continue
continue
}
}