github.com/evdatsion/aphelion-dpos-bft@v0.32.1/rpc/lib/server/handlers.go (about)

     1  package rpcserver
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/hex"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"reflect"
    12  	"runtime/debug"
    13  	"sort"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/gorilla/websocket"
    18  	"github.com/pkg/errors"
    19  
    20  	amino "github.com/evdatsion/go-amino"
    21  	cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common"
    22  	"github.com/evdatsion/aphelion-dpos-bft/libs/log"
    23  	types "github.com/evdatsion/aphelion-dpos-bft/rpc/lib/types"
    24  )
    25  
    26  // RegisterRPCFuncs adds a route for each function in the funcMap, as well as general jsonrpc and websocket handlers for all functions.
    27  // "result" is the interface on which the result objects are registered, and is popualted with every RPCResponse
    28  func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc, cdc *amino.Codec, logger log.Logger) {
    29  	// HTTP endpoints
    30  	for funcName, rpcFunc := range funcMap {
    31  		mux.HandleFunc("/"+funcName, makeHTTPHandler(rpcFunc, cdc, logger))
    32  	}
    33  
    34  	// JSONRPC endpoints
    35  	mux.HandleFunc("/", handleInvalidJSONRPCPaths(makeJSONRPCHandler(funcMap, cdc, logger)))
    36  }
    37  
    38  //-------------------------------------
    39  // function introspection
    40  
    41  // RPCFunc contains the introspected type information for a function
    42  type RPCFunc struct {
    43  	f        reflect.Value  // underlying rpc function
    44  	args     []reflect.Type // type of each function arg
    45  	returns  []reflect.Type // type of each return arg
    46  	argNames []string       // name of each argument
    47  	ws       bool           // websocket only
    48  }
    49  
    50  // NewRPCFunc wraps a function for introspection.
    51  // f is the function, args are comma separated argument names
    52  func NewRPCFunc(f interface{}, args string) *RPCFunc {
    53  	return newRPCFunc(f, args, false)
    54  }
    55  
    56  // NewWSRPCFunc wraps a function for introspection and use in the websockets.
    57  func NewWSRPCFunc(f interface{}, args string) *RPCFunc {
    58  	return newRPCFunc(f, args, true)
    59  }
    60  
    61  func newRPCFunc(f interface{}, args string, ws bool) *RPCFunc {
    62  	var argNames []string
    63  	if args != "" {
    64  		argNames = strings.Split(args, ",")
    65  	}
    66  	return &RPCFunc{
    67  		f:        reflect.ValueOf(f),
    68  		args:     funcArgTypes(f),
    69  		returns:  funcReturnTypes(f),
    70  		argNames: argNames,
    71  		ws:       ws,
    72  	}
    73  }
    74  
    75  // return a function's argument types
    76  func funcArgTypes(f interface{}) []reflect.Type {
    77  	t := reflect.TypeOf(f)
    78  	n := t.NumIn()
    79  	typez := make([]reflect.Type, n)
    80  	for i := 0; i < n; i++ {
    81  		typez[i] = t.In(i)
    82  	}
    83  	return typez
    84  }
    85  
    86  // return a function's return types
    87  func funcReturnTypes(f interface{}) []reflect.Type {
    88  	t := reflect.TypeOf(f)
    89  	n := t.NumOut()
    90  	typez := make([]reflect.Type, n)
    91  	for i := 0; i < n; i++ {
    92  		typez[i] = t.Out(i)
    93  	}
    94  	return typez
    95  }
    96  
    97  // function introspection
    98  //-----------------------------------------------------------------------------
    99  // rpc.json
   100  
   101  // jsonrpc calls grab the given method's function info and runs reflect.Call
   102  func makeJSONRPCHandler(funcMap map[string]*RPCFunc, cdc *amino.Codec, logger log.Logger) http.HandlerFunc {
   103  	return func(w http.ResponseWriter, r *http.Request) {
   104  		b, err := ioutil.ReadAll(r.Body)
   105  		if err != nil {
   106  			WriteRPCResponseHTTP(w, types.RPCInvalidRequestError(types.JSONRPCStringID(""), errors.Wrap(err, "error reading request body")))
   107  			return
   108  		}
   109  		// if its an empty request (like from a browser),
   110  		// just display a list of functions
   111  		if len(b) == 0 {
   112  			writeListOfEndpoints(w, r, funcMap)
   113  			return
   114  		}
   115  
   116  		// first try to unmarshal the incoming request as an array of RPC requests
   117  		var (
   118  			requests  []types.RPCRequest
   119  			responses []types.RPCResponse
   120  		)
   121  		if err := json.Unmarshal(b, &requests); err != nil {
   122  			// next, try to unmarshal as a single request
   123  			var request types.RPCRequest
   124  			if err := json.Unmarshal(b, &request); err != nil {
   125  				WriteRPCResponseHTTP(w, types.RPCParseError(types.JSONRPCStringID(""), errors.Wrap(err, "error unmarshalling request")))
   126  				return
   127  			}
   128  			requests = []types.RPCRequest{request}
   129  		}
   130  
   131  		for _, request := range requests {
   132  			// A Notification is a Request object without an "id" member.
   133  			// The Server MUST NOT reply to a Notification, including those that are within a batch request.
   134  			if request.ID == types.JSONRPCStringID("") {
   135  				logger.Debug("HTTPJSONRPC received a notification, skipping... (please send a non-empty ID if you want to call a method)")
   136  				continue
   137  			}
   138  			if len(r.URL.Path) > 1 {
   139  				responses = append(responses, types.RPCInvalidRequestError(request.ID, errors.Errorf("path %s is invalid", r.URL.Path)))
   140  				continue
   141  			}
   142  			rpcFunc, ok := funcMap[request.Method]
   143  			if !ok || rpcFunc.ws {
   144  				responses = append(responses, types.RPCMethodNotFoundError(request.ID))
   145  				continue
   146  			}
   147  			ctx := &types.Context{JSONReq: &request, HTTPReq: r}
   148  			args := []reflect.Value{reflect.ValueOf(ctx)}
   149  			if len(request.Params) > 0 {
   150  				fnArgs, err := jsonParamsToArgs(rpcFunc, cdc, request.Params)
   151  				if err != nil {
   152  					responses = append(responses, types.RPCInvalidParamsError(request.ID, errors.Wrap(err, "error converting json params to arguments")))
   153  					continue
   154  				}
   155  				args = append(args, fnArgs...)
   156  			}
   157  			returns := rpcFunc.f.Call(args)
   158  			logger.Info("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns)
   159  			result, err := unreflectResult(returns)
   160  			if err != nil {
   161  				responses = append(responses, types.RPCInternalError(request.ID, err))
   162  				continue
   163  			}
   164  			responses = append(responses, types.NewRPCSuccessResponse(cdc, request.ID, result))
   165  		}
   166  		if len(responses) > 0 {
   167  			WriteRPCResponseArrayHTTP(w, responses)
   168  		}
   169  	}
   170  }
   171  
   172  func handleInvalidJSONRPCPaths(next http.HandlerFunc) http.HandlerFunc {
   173  	return func(w http.ResponseWriter, r *http.Request) {
   174  		// Since the pattern "/" matches all paths not matched by other registered patterns we check whether the path is indeed
   175  		// "/", otherwise return a 404 error
   176  		if r.URL.Path != "/" {
   177  			http.NotFound(w, r)
   178  			return
   179  		}
   180  
   181  		next(w, r)
   182  	}
   183  }
   184  
   185  func mapParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, params map[string]json.RawMessage, argsOffset int) ([]reflect.Value, error) {
   186  	values := make([]reflect.Value, len(rpcFunc.argNames))
   187  	for i, argName := range rpcFunc.argNames {
   188  		argType := rpcFunc.args[i+argsOffset]
   189  
   190  		if p, ok := params[argName]; ok && p != nil && len(p) > 0 {
   191  			val := reflect.New(argType)
   192  			err := cdc.UnmarshalJSON(p, val.Interface())
   193  			if err != nil {
   194  				return nil, err
   195  			}
   196  			values[i] = val.Elem()
   197  		} else { // use default for that type
   198  			values[i] = reflect.Zero(argType)
   199  		}
   200  	}
   201  
   202  	return values, nil
   203  }
   204  
   205  func arrayParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, params []json.RawMessage, argsOffset int) ([]reflect.Value, error) {
   206  	if len(rpcFunc.argNames) != len(params) {
   207  		return nil, errors.Errorf("expected %v parameters (%v), got %v (%v)",
   208  			len(rpcFunc.argNames), rpcFunc.argNames, len(params), params)
   209  	}
   210  
   211  	values := make([]reflect.Value, len(params))
   212  	for i, p := range params {
   213  		argType := rpcFunc.args[i+argsOffset]
   214  		val := reflect.New(argType)
   215  		err := cdc.UnmarshalJSON(p, val.Interface())
   216  		if err != nil {
   217  			return nil, err
   218  		}
   219  		values[i] = val.Elem()
   220  	}
   221  	return values, nil
   222  }
   223  
   224  // raw is unparsed json (from json.RawMessage) encoding either a map or an
   225  // array.
   226  //
   227  // Example:
   228  //   rpcFunc.args = [rpctypes.Context string]
   229  //   rpcFunc.argNames = ["arg"]
   230  func jsonParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, raw []byte) ([]reflect.Value, error) {
   231  	const argsOffset = 1
   232  
   233  	// TODO: Make more efficient, perhaps by checking the first character for '{' or '['?
   234  	// First, try to get the map.
   235  	var m map[string]json.RawMessage
   236  	err := json.Unmarshal(raw, &m)
   237  	if err == nil {
   238  		return mapParamsToArgs(rpcFunc, cdc, m, argsOffset)
   239  	}
   240  
   241  	// Otherwise, try an array.
   242  	var a []json.RawMessage
   243  	err = json.Unmarshal(raw, &a)
   244  	if err == nil {
   245  		return arrayParamsToArgs(rpcFunc, cdc, a, argsOffset)
   246  	}
   247  
   248  	// Otherwise, bad format, we cannot parse
   249  	return nil, errors.Errorf("unknown type for JSON params: %v. Expected map or array", err)
   250  }
   251  
   252  // rpc.json
   253  //-----------------------------------------------------------------------------
   254  // rpc.http
   255  
   256  // convert from a function name to the http handler
   257  func makeHTTPHandler(rpcFunc *RPCFunc, cdc *amino.Codec, logger log.Logger) func(http.ResponseWriter, *http.Request) {
   258  	// Exception for websocket endpoints
   259  	if rpcFunc.ws {
   260  		return func(w http.ResponseWriter, r *http.Request) {
   261  			WriteRPCResponseHTTP(w, types.RPCMethodNotFoundError(types.JSONRPCStringID("")))
   262  		}
   263  	}
   264  
   265  	// All other endpoints
   266  	return func(w http.ResponseWriter, r *http.Request) {
   267  		logger.Debug("HTTP HANDLER", "req", r)
   268  
   269  		ctx := &types.Context{HTTPReq: r}
   270  		args := []reflect.Value{reflect.ValueOf(ctx)}
   271  
   272  		fnArgs, err := httpParamsToArgs(rpcFunc, cdc, r)
   273  		if err != nil {
   274  			WriteRPCResponseHTTP(w, types.RPCInvalidParamsError(types.JSONRPCStringID(""), errors.Wrap(err, "error converting http params to arguments")))
   275  			return
   276  		}
   277  		args = append(args, fnArgs...)
   278  
   279  		returns := rpcFunc.f.Call(args)
   280  
   281  		logger.Info("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns)
   282  		result, err := unreflectResult(returns)
   283  		if err != nil {
   284  			WriteRPCResponseHTTP(w, types.RPCInternalError(types.JSONRPCStringID(""), err))
   285  			return
   286  		}
   287  		WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(cdc, types.JSONRPCStringID(""), result))
   288  	}
   289  }
   290  
   291  // Covert an http query to a list of properly typed values.
   292  // To be properly decoded the arg must be a concrete type from tendermint (if its an interface).
   293  func httpParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, r *http.Request) ([]reflect.Value, error) {
   294  	// skip types.Context
   295  	const argsOffset = 1
   296  
   297  	values := make([]reflect.Value, len(rpcFunc.argNames))
   298  
   299  	for i, name := range rpcFunc.argNames {
   300  		argType := rpcFunc.args[i+argsOffset]
   301  
   302  		values[i] = reflect.Zero(argType) // set default for that type
   303  
   304  		arg := GetParam(r, name)
   305  		// log.Notice("param to arg", "argType", argType, "name", name, "arg", arg)
   306  
   307  		if "" == arg {
   308  			continue
   309  		}
   310  
   311  		v, err, ok := nonJSONStringToArg(cdc, argType, arg)
   312  		if err != nil {
   313  			return nil, err
   314  		}
   315  		if ok {
   316  			values[i] = v
   317  			continue
   318  		}
   319  
   320  		values[i], err = jsonStringToArg(cdc, argType, arg)
   321  		if err != nil {
   322  			return nil, err
   323  		}
   324  	}
   325  
   326  	return values, nil
   327  }
   328  
   329  func jsonStringToArg(cdc *amino.Codec, rt reflect.Type, arg string) (reflect.Value, error) {
   330  	rv := reflect.New(rt)
   331  	err := cdc.UnmarshalJSON([]byte(arg), rv.Interface())
   332  	if err != nil {
   333  		return rv, err
   334  	}
   335  	rv = rv.Elem()
   336  	return rv, nil
   337  }
   338  
   339  func nonJSONStringToArg(cdc *amino.Codec, rt reflect.Type, arg string) (reflect.Value, error, bool) {
   340  	if rt.Kind() == reflect.Ptr {
   341  		rv_, err, ok := nonJSONStringToArg(cdc, rt.Elem(), arg)
   342  		if err != nil {
   343  			return reflect.Value{}, err, false
   344  		} else if ok {
   345  			rv := reflect.New(rt.Elem())
   346  			rv.Elem().Set(rv_)
   347  			return rv, nil, true
   348  		} else {
   349  			return reflect.Value{}, nil, false
   350  		}
   351  	} else {
   352  		return _nonJSONStringToArg(cdc, rt, arg)
   353  	}
   354  }
   355  
   356  // NOTE: rt.Kind() isn't a pointer.
   357  func _nonJSONStringToArg(cdc *amino.Codec, rt reflect.Type, arg string) (reflect.Value, error, bool) {
   358  	isIntString := RE_INT.Match([]byte(arg))
   359  	isQuotedString := strings.HasPrefix(arg, `"`) && strings.HasSuffix(arg, `"`)
   360  	isHexString := strings.HasPrefix(strings.ToLower(arg), "0x")
   361  
   362  	var expectingString, expectingByteSlice, expectingInt bool
   363  	switch rt.Kind() {
   364  	case reflect.Int, reflect.Uint, reflect.Int8, reflect.Uint8, reflect.Int16, reflect.Uint16, reflect.Int32, reflect.Uint32, reflect.Int64, reflect.Uint64:
   365  		expectingInt = true
   366  	case reflect.String:
   367  		expectingString = true
   368  	case reflect.Slice:
   369  		expectingByteSlice = rt.Elem().Kind() == reflect.Uint8
   370  	}
   371  
   372  	if isIntString && expectingInt {
   373  		qarg := `"` + arg + `"`
   374  		// jsonStringToArg
   375  		rv, err := jsonStringToArg(cdc, rt, qarg)
   376  		if err != nil {
   377  			return rv, err, false
   378  		} else {
   379  			return rv, nil, true
   380  		}
   381  	}
   382  
   383  	if isHexString {
   384  		if !expectingString && !expectingByteSlice {
   385  			err := errors.Errorf("got a hex string arg, but expected '%s'",
   386  				rt.Kind().String())
   387  			return reflect.ValueOf(nil), err, false
   388  		}
   389  
   390  		var value []byte
   391  		value, err := hex.DecodeString(arg[2:])
   392  		if err != nil {
   393  			return reflect.ValueOf(nil), err, false
   394  		}
   395  		if rt.Kind() == reflect.String {
   396  			return reflect.ValueOf(string(value)), nil, true
   397  		}
   398  		return reflect.ValueOf([]byte(value)), nil, true
   399  	}
   400  
   401  	if isQuotedString && expectingByteSlice {
   402  		v := reflect.New(reflect.TypeOf(""))
   403  		err := cdc.UnmarshalJSON([]byte(arg), v.Interface())
   404  		if err != nil {
   405  			return reflect.ValueOf(nil), err, false
   406  		}
   407  		v = v.Elem()
   408  		return reflect.ValueOf([]byte(v.String())), nil, true
   409  	}
   410  
   411  	return reflect.ValueOf(nil), nil, false
   412  }
   413  
   414  // rpc.http
   415  //-----------------------------------------------------------------------------
   416  // rpc.websocket
   417  
   418  const (
   419  	defaultWSWriteChanCapacity = 1000
   420  	defaultWSWriteWait         = 10 * time.Second
   421  	defaultWSReadWait          = 30 * time.Second
   422  	defaultWSPingPeriod        = (defaultWSReadWait * 9) / 10
   423  )
   424  
   425  // A single websocket connection contains listener id, underlying ws
   426  // connection, and the event switch for subscribing to events.
   427  //
   428  // In case of an error, the connection is stopped.
   429  type wsConnection struct {
   430  	cmn.BaseService
   431  
   432  	remoteAddr string
   433  	baseConn   *websocket.Conn
   434  	writeChan  chan types.RPCResponse
   435  
   436  	funcMap map[string]*RPCFunc
   437  	cdc     *amino.Codec
   438  
   439  	// write channel capacity
   440  	writeChanCapacity int
   441  
   442  	// each write times out after this.
   443  	writeWait time.Duration
   444  
   445  	// Connection times out if we haven't received *anything* in this long, not even pings.
   446  	readWait time.Duration
   447  
   448  	// Send pings to server with this period. Must be less than readWait, but greater than zero.
   449  	pingPeriod time.Duration
   450  
   451  	// callback which is called upon disconnect
   452  	onDisconnect func(remoteAddr string)
   453  
   454  	ctx    context.Context
   455  	cancel context.CancelFunc
   456  }
   457  
   458  // NewWSConnection wraps websocket.Conn.
   459  //
   460  // See the commentary on the func(*wsConnection) functions for a detailed
   461  // description of how to configure ping period and pong wait time. NOTE: if the
   462  // write buffer is full, pongs may be dropped, which may cause clients to
   463  // disconnect. see https://github.com/gorilla/websocket/issues/97
   464  func NewWSConnection(
   465  	baseConn *websocket.Conn,
   466  	funcMap map[string]*RPCFunc,
   467  	cdc *amino.Codec,
   468  	options ...func(*wsConnection),
   469  ) *wsConnection {
   470  	baseConn.SetReadLimit(maxBodyBytes)
   471  	wsc := &wsConnection{
   472  		remoteAddr:        baseConn.RemoteAddr().String(),
   473  		baseConn:          baseConn,
   474  		funcMap:           funcMap,
   475  		cdc:               cdc,
   476  		writeWait:         defaultWSWriteWait,
   477  		writeChanCapacity: defaultWSWriteChanCapacity,
   478  		readWait:          defaultWSReadWait,
   479  		pingPeriod:        defaultWSPingPeriod,
   480  	}
   481  	for _, option := range options {
   482  		option(wsc)
   483  	}
   484  	wsc.BaseService = *cmn.NewBaseService(nil, "wsConnection", wsc)
   485  	return wsc
   486  }
   487  
   488  // OnDisconnect sets a callback which is used upon disconnect - not
   489  // Goroutine-safe. Nop by default.
   490  func OnDisconnect(onDisconnect func(remoteAddr string)) func(*wsConnection) {
   491  	return func(wsc *wsConnection) {
   492  		wsc.onDisconnect = onDisconnect
   493  	}
   494  }
   495  
   496  // WriteWait sets the amount of time to wait before a websocket write times out.
   497  // It should only be used in the constructor - not Goroutine-safe.
   498  func WriteWait(writeWait time.Duration) func(*wsConnection) {
   499  	return func(wsc *wsConnection) {
   500  		wsc.writeWait = writeWait
   501  	}
   502  }
   503  
   504  // WriteChanCapacity sets the capacity of the websocket write channel.
   505  // It should only be used in the constructor - not Goroutine-safe.
   506  func WriteChanCapacity(cap int) func(*wsConnection) {
   507  	return func(wsc *wsConnection) {
   508  		wsc.writeChanCapacity = cap
   509  	}
   510  }
   511  
   512  // ReadWait sets the amount of time to wait before a websocket read times out.
   513  // It should only be used in the constructor - not Goroutine-safe.
   514  func ReadWait(readWait time.Duration) func(*wsConnection) {
   515  	return func(wsc *wsConnection) {
   516  		wsc.readWait = readWait
   517  	}
   518  }
   519  
   520  // PingPeriod sets the duration for sending websocket pings.
   521  // It should only be used in the constructor - not Goroutine-safe.
   522  func PingPeriod(pingPeriod time.Duration) func(*wsConnection) {
   523  	return func(wsc *wsConnection) {
   524  		wsc.pingPeriod = pingPeriod
   525  	}
   526  }
   527  
   528  // OnStart implements cmn.Service by starting the read and write routines. It
   529  // blocks until the connection closes.
   530  func (wsc *wsConnection) OnStart() error {
   531  	wsc.writeChan = make(chan types.RPCResponse, wsc.writeChanCapacity)
   532  
   533  	// Read subscriptions/unsubscriptions to events
   534  	go wsc.readRoutine()
   535  	// Write responses, BLOCKING.
   536  	wsc.writeRoutine()
   537  
   538  	return nil
   539  }
   540  
   541  // OnStop implements cmn.Service by unsubscribing remoteAddr from all subscriptions.
   542  func (wsc *wsConnection) OnStop() {
   543  	// Both read and write loops close the websocket connection when they exit their loops.
   544  	// The writeChan is never closed, to allow WriteRPCResponse() to fail.
   545  
   546  	if wsc.onDisconnect != nil {
   547  		wsc.onDisconnect(wsc.remoteAddr)
   548  	}
   549  
   550  	if wsc.ctx != nil {
   551  		wsc.cancel()
   552  	}
   553  }
   554  
   555  // GetRemoteAddr returns the remote address of the underlying connection.
   556  // It implements WSRPCConnection
   557  func (wsc *wsConnection) GetRemoteAddr() string {
   558  	return wsc.remoteAddr
   559  }
   560  
   561  // WriteRPCResponse pushes a response to the writeChan, and blocks until it is accepted.
   562  // It implements WSRPCConnection. It is Goroutine-safe.
   563  func (wsc *wsConnection) WriteRPCResponse(resp types.RPCResponse) {
   564  	select {
   565  	case <-wsc.Quit():
   566  		return
   567  	case wsc.writeChan <- resp:
   568  	}
   569  }
   570  
   571  // TryWriteRPCResponse attempts to push a response to the writeChan, but does not block.
   572  // It implements WSRPCConnection. It is Goroutine-safe
   573  func (wsc *wsConnection) TryWriteRPCResponse(resp types.RPCResponse) bool {
   574  	select {
   575  	case <-wsc.Quit():
   576  		return false
   577  	case wsc.writeChan <- resp:
   578  		return true
   579  	default:
   580  		return false
   581  	}
   582  }
   583  
   584  // Codec returns an amino codec used to decode parameters and encode results.
   585  // It implements WSRPCConnection.
   586  func (wsc *wsConnection) Codec() *amino.Codec {
   587  	return wsc.cdc
   588  }
   589  
   590  // Context returns the connection's context.
   591  // The context is canceled when the client's connection closes.
   592  func (wsc *wsConnection) Context() context.Context {
   593  	if wsc.ctx != nil {
   594  		return wsc.ctx
   595  	}
   596  	wsc.ctx, wsc.cancel = context.WithCancel(context.Background())
   597  	return wsc.ctx
   598  }
   599  
   600  // Read from the socket and subscribe to or unsubscribe from events
   601  func (wsc *wsConnection) readRoutine() {
   602  	defer func() {
   603  		if r := recover(); r != nil {
   604  			err, ok := r.(error)
   605  			if !ok {
   606  				err = fmt.Errorf("WSJSONRPC: %v", r)
   607  			}
   608  			wsc.Logger.Error("Panic in WSJSONRPC handler", "err", err, "stack", string(debug.Stack()))
   609  			wsc.WriteRPCResponse(types.RPCInternalError(types.JSONRPCStringID("unknown"), err))
   610  			go wsc.readRoutine()
   611  		} else {
   612  			wsc.baseConn.Close() // nolint: errcheck
   613  		}
   614  	}()
   615  
   616  	wsc.baseConn.SetPongHandler(func(m string) error {
   617  		return wsc.baseConn.SetReadDeadline(time.Now().Add(wsc.readWait))
   618  	})
   619  
   620  	for {
   621  		select {
   622  		case <-wsc.Quit():
   623  			return
   624  		default:
   625  			// reset deadline for every type of message (control or data)
   626  			if err := wsc.baseConn.SetReadDeadline(time.Now().Add(wsc.readWait)); err != nil {
   627  				wsc.Logger.Error("failed to set read deadline", "err", err)
   628  			}
   629  			var in []byte
   630  			_, in, err := wsc.baseConn.ReadMessage()
   631  			if err != nil {
   632  				if websocket.IsCloseError(err, websocket.CloseNormalClosure) {
   633  					wsc.Logger.Info("Client closed the connection")
   634  				} else {
   635  					wsc.Logger.Error("Failed to read request", "err", err)
   636  				}
   637  				wsc.Stop()
   638  				return
   639  			}
   640  
   641  			var request types.RPCRequest
   642  			err = json.Unmarshal(in, &request)
   643  			if err != nil {
   644  				wsc.WriteRPCResponse(types.RPCParseError(types.JSONRPCStringID(""), errors.Wrap(err, "error unmarshaling request")))
   645  				continue
   646  			}
   647  
   648  			// A Notification is a Request object without an "id" member.
   649  			// The Server MUST NOT reply to a Notification, including those that are within a batch request.
   650  			if request.ID == types.JSONRPCStringID("") {
   651  				wsc.Logger.Debug("WSJSONRPC received a notification, skipping... (please send a non-empty ID if you want to call a method)")
   652  				continue
   653  			}
   654  
   655  			// Now, fetch the RPCFunc and execute it.
   656  			rpcFunc := wsc.funcMap[request.Method]
   657  			if rpcFunc == nil {
   658  				wsc.WriteRPCResponse(types.RPCMethodNotFoundError(request.ID))
   659  				continue
   660  			}
   661  
   662  			ctx := &types.Context{JSONReq: &request, WSConn: wsc}
   663  			args := []reflect.Value{reflect.ValueOf(ctx)}
   664  			if len(request.Params) > 0 {
   665  				fnArgs, err := jsonParamsToArgs(rpcFunc, wsc.cdc, request.Params)
   666  				if err != nil {
   667  					wsc.WriteRPCResponse(types.RPCInternalError(request.ID, errors.Wrap(err, "error converting json params to arguments")))
   668  					continue
   669  				}
   670  				args = append(args, fnArgs...)
   671  			}
   672  
   673  			returns := rpcFunc.f.Call(args)
   674  
   675  			// TODO: Need to encode args/returns to string if we want to log them
   676  			wsc.Logger.Info("WSJSONRPC", "method", request.Method)
   677  
   678  			result, err := unreflectResult(returns)
   679  			if err != nil {
   680  				wsc.WriteRPCResponse(types.RPCInternalError(request.ID, err))
   681  				continue
   682  			}
   683  
   684  			wsc.WriteRPCResponse(types.NewRPCSuccessResponse(wsc.cdc, request.ID, result))
   685  		}
   686  	}
   687  }
   688  
   689  // receives on a write channel and writes out on the socket
   690  func (wsc *wsConnection) writeRoutine() {
   691  	pingTicker := time.NewTicker(wsc.pingPeriod)
   692  	defer func() {
   693  		pingTicker.Stop()
   694  		if err := wsc.baseConn.Close(); err != nil {
   695  			wsc.Logger.Error("Error closing connection", "err", err)
   696  		}
   697  	}()
   698  
   699  	// https://github.com/gorilla/websocket/issues/97
   700  	pongs := make(chan string, 1)
   701  	wsc.baseConn.SetPingHandler(func(m string) error {
   702  		select {
   703  		case pongs <- m:
   704  		default:
   705  		}
   706  		return nil
   707  	})
   708  
   709  	for {
   710  		select {
   711  		case m := <-pongs:
   712  			err := wsc.writeMessageWithDeadline(websocket.PongMessage, []byte(m))
   713  			if err != nil {
   714  				wsc.Logger.Info("Failed to write pong (client may disconnect)", "err", err)
   715  			}
   716  		case <-pingTicker.C:
   717  			err := wsc.writeMessageWithDeadline(websocket.PingMessage, []byte{})
   718  			if err != nil {
   719  				wsc.Logger.Error("Failed to write ping", "err", err)
   720  				wsc.Stop()
   721  				return
   722  			}
   723  		case msg := <-wsc.writeChan:
   724  			jsonBytes, err := json.MarshalIndent(msg, "", "  ")
   725  			if err != nil {
   726  				wsc.Logger.Error("Failed to marshal RPCResponse to JSON", "err", err)
   727  			} else {
   728  				if err = wsc.writeMessageWithDeadline(websocket.TextMessage, jsonBytes); err != nil {
   729  					wsc.Logger.Error("Failed to write response", "err", err)
   730  					wsc.Stop()
   731  					return
   732  				}
   733  			}
   734  		case <-wsc.Quit():
   735  			return
   736  		}
   737  	}
   738  }
   739  
   740  // All writes to the websocket must (re)set the write deadline.
   741  // If some writes don't set it while others do, they may timeout incorrectly (https://github.com/evdatsion/aphelion-dpos-bft/issues/553)
   742  func (wsc *wsConnection) writeMessageWithDeadline(msgType int, msg []byte) error {
   743  	if err := wsc.baseConn.SetWriteDeadline(time.Now().Add(wsc.writeWait)); err != nil {
   744  		return err
   745  	}
   746  	return wsc.baseConn.WriteMessage(msgType, msg)
   747  }
   748  
   749  //----------------------------------------
   750  
   751  // WebsocketManager provides a WS handler for incoming connections and passes a
   752  // map of functions along with any additional params to new connections.
   753  // NOTE: The websocket path is defined externally, e.g. in node/node.go
   754  type WebsocketManager struct {
   755  	websocket.Upgrader
   756  
   757  	funcMap       map[string]*RPCFunc
   758  	cdc           *amino.Codec
   759  	logger        log.Logger
   760  	wsConnOptions []func(*wsConnection)
   761  }
   762  
   763  // NewWebsocketManager returns a new WebsocketManager that passes a map of
   764  // functions, connection options and logger to new WS connections.
   765  func NewWebsocketManager(funcMap map[string]*RPCFunc, cdc *amino.Codec, wsConnOptions ...func(*wsConnection)) *WebsocketManager {
   766  	return &WebsocketManager{
   767  		funcMap: funcMap,
   768  		cdc:     cdc,
   769  		Upgrader: websocket.Upgrader{
   770  			CheckOrigin: func(r *http.Request) bool {
   771  				// TODO ???
   772  				return true
   773  			},
   774  		},
   775  		logger:        log.NewNopLogger(),
   776  		wsConnOptions: wsConnOptions,
   777  	}
   778  }
   779  
   780  // SetLogger sets the logger.
   781  func (wm *WebsocketManager) SetLogger(l log.Logger) {
   782  	wm.logger = l
   783  }
   784  
   785  // WebsocketHandler upgrades the request/response (via http.Hijack) and starts
   786  // the wsConnection.
   787  func (wm *WebsocketManager) WebsocketHandler(w http.ResponseWriter, r *http.Request) {
   788  	wsConn, err := wm.Upgrade(w, r, nil)
   789  	if err != nil {
   790  		// TODO - return http error
   791  		wm.logger.Error("Failed to upgrade to websocket connection", "err", err)
   792  		return
   793  	}
   794  
   795  	// register connection
   796  	con := NewWSConnection(wsConn, wm.funcMap, wm.cdc, wm.wsConnOptions...)
   797  	con.SetLogger(wm.logger.With("remote", wsConn.RemoteAddr()))
   798  	wm.logger.Info("New websocket connection", "remote", con.remoteAddr)
   799  	err = con.Start() // Blocking
   800  	if err != nil {
   801  		wm.logger.Error("Error starting connection", "err", err)
   802  	}
   803  }
   804  
   805  // rpc.websocket
   806  //-----------------------------------------------------------------------------
   807  
   808  // NOTE: assume returns is result struct and error. If error is not nil, return it
   809  func unreflectResult(returns []reflect.Value) (interface{}, error) {
   810  	errV := returns[1]
   811  	if errV.Interface() != nil {
   812  		return nil, errors.Errorf("%v", errV.Interface())
   813  	}
   814  	rv := returns[0]
   815  	// the result is a registered interface,
   816  	// we need a pointer to it so we can marshal with type byte
   817  	rvp := reflect.New(rv.Type())
   818  	rvp.Elem().Set(rv)
   819  	return rvp.Interface(), nil
   820  }
   821  
   822  // writes a list of available rpc endpoints as an html page
   823  func writeListOfEndpoints(w http.ResponseWriter, r *http.Request, funcMap map[string]*RPCFunc) {
   824  	noArgNames := []string{}
   825  	argNames := []string{}
   826  	for name, funcData := range funcMap {
   827  		if len(funcData.args) == 0 {
   828  			noArgNames = append(noArgNames, name)
   829  		} else {
   830  			argNames = append(argNames, name)
   831  		}
   832  	}
   833  	sort.Strings(noArgNames)
   834  	sort.Strings(argNames)
   835  	buf := new(bytes.Buffer)
   836  	buf.WriteString("<html><body>")
   837  	buf.WriteString("<br>Available endpoints:<br>")
   838  
   839  	for _, name := range noArgNames {
   840  		link := fmt.Sprintf("//%s/%s", r.Host, name)
   841  		buf.WriteString(fmt.Sprintf("<a href=\"%s\">%s</a></br>", link, link))
   842  	}
   843  
   844  	buf.WriteString("<br>Endpoints that require arguments:<br>")
   845  	for _, name := range argNames {
   846  		link := fmt.Sprintf("//%s/%s?", r.Host, name)
   847  		funcData := funcMap[name]
   848  		for i, argName := range funcData.argNames {
   849  			link += argName + "=_"
   850  			if i < len(funcData.argNames)-1 {
   851  				link += "&"
   852  			}
   853  		}
   854  		buf.WriteString(fmt.Sprintf("<a href=\"%s\">%s</a></br>", link, link))
   855  	}
   856  	buf.WriteString("</body></html>")
   857  	w.Header().Set("Content-Type", "text/html")
   858  	w.WriteHeader(200)
   859  	w.Write(buf.Bytes()) // nolint: errcheck
   860  }