Browse Source

Set cache control in the HTTP-RPC response header (#6265)

pull/6343/head
JayT106 4 years ago
committed by GitHub
parent
commit
ca7dbea05b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 144 additions and 74 deletions
  1. +2
    -1
      CHANGELOG_PENDING.md
  2. +24
    -24
      light/proxy/routes.go
  3. +28
    -28
      rpc/core/routes.go
  4. +1
    -1
      rpc/jsonrpc/doc.go
  5. +4
    -4
      rpc/jsonrpc/jsonrpc_test.go
  6. +30
    -3
      rpc/jsonrpc/server/http_json_handler.go
  7. +34
    -1
      rpc/jsonrpc/server/http_json_handler_test.go
  8. +6
    -2
      rpc/jsonrpc/server/http_server.go
  9. +3
    -1
      rpc/jsonrpc/server/http_server_test.go
  10. +1
    -1
      rpc/jsonrpc/server/http_uri_handler.go
  11. +2
    -2
      rpc/jsonrpc/server/parse_test.go
  12. +7
    -4
      rpc/jsonrpc/server/rpc_func.go
  13. +1
    -1
      rpc/jsonrpc/test/main.go
  14. +1
    -1
      test/fuzz/rpc/jsonrpc/server/handler.go

+ 2
- 1
CHANGELOG_PENDING.md View File

@ -79,7 +79,8 @@ Friendly reminder: We have a [bug bounty program](https://hackerone.com/tendermi
- [types] \#6120 use batch verification for verifying commits signatures.
- If the key type supports the batch verification API it will try to batch verify. If the verification fails we will single verify each signature.
- [privval/file] \#6185 Return error on `LoadFilePV`, `LoadFilePVEmptyState`. Allows for better programmatic control of Tendermint.
- [privval] /#6240 Add `context.Context` to privval interface.
- [privval] \#6240 Add `context.Context` to privval interface.
- [rpc] \#6265 set cache control in http-rpc response header (@JayT106)
### BUG FIXES


+ 24
- 24
light/proxy/routes.go View File

@ -18,36 +18,36 @@ func RPCRoutes(c *lrpc.Client) map[string]*rpcserver.RPCFunc {
"unsubscribe_all": rpcserver.NewWSRPCFunc(c.UnsubscribeAllWS, ""),
// info API
"health": rpcserver.NewRPCFunc(makeHealthFunc(c), ""),
"status": rpcserver.NewRPCFunc(makeStatusFunc(c), ""),
"net_info": rpcserver.NewRPCFunc(makeNetInfoFunc(c), ""),
"blockchain": rpcserver.NewRPCFunc(makeBlockchainInfoFunc(c), "minHeight,maxHeight"),
"genesis": rpcserver.NewRPCFunc(makeGenesisFunc(c), ""),
"block": rpcserver.NewRPCFunc(makeBlockFunc(c), "height"),
"block_by_hash": rpcserver.NewRPCFunc(makeBlockByHashFunc(c), "hash"),
"block_results": rpcserver.NewRPCFunc(makeBlockResultsFunc(c), "height"),
"commit": rpcserver.NewRPCFunc(makeCommitFunc(c), "height"),
"tx": rpcserver.NewRPCFunc(makeTxFunc(c), "hash,prove"),
"tx_search": rpcserver.NewRPCFunc(makeTxSearchFunc(c), "query,prove,page,per_page,order_by"),
"block_search": rpcserver.NewRPCFunc(makeBlockSearchFunc(c), "query,page,per_page,order_by"),
"validators": rpcserver.NewRPCFunc(makeValidatorsFunc(c), "height,page,per_page"),
"dump_consensus_state": rpcserver.NewRPCFunc(makeDumpConsensusStateFunc(c), ""),
"consensus_state": rpcserver.NewRPCFunc(makeConsensusStateFunc(c), ""),
"consensus_params": rpcserver.NewRPCFunc(makeConsensusParamsFunc(c), "height"),
"unconfirmed_txs": rpcserver.NewRPCFunc(makeUnconfirmedTxsFunc(c), "limit"),
"num_unconfirmed_txs": rpcserver.NewRPCFunc(makeNumUnconfirmedTxsFunc(c), ""),
"health": rpcserver.NewRPCFunc(makeHealthFunc(c), "", false),
"status": rpcserver.NewRPCFunc(makeStatusFunc(c), "", false),
"net_info": rpcserver.NewRPCFunc(makeNetInfoFunc(c), "", false),
"blockchain": rpcserver.NewRPCFunc(makeBlockchainInfoFunc(c), "minHeight,maxHeight", true),
"genesis": rpcserver.NewRPCFunc(makeGenesisFunc(c), "", true),
"block": rpcserver.NewRPCFunc(makeBlockFunc(c), "height", true),
"block_by_hash": rpcserver.NewRPCFunc(makeBlockByHashFunc(c), "hash", true),
"block_results": rpcserver.NewRPCFunc(makeBlockResultsFunc(c), "height", true),
"commit": rpcserver.NewRPCFunc(makeCommitFunc(c), "height", true),
"tx": rpcserver.NewRPCFunc(makeTxFunc(c), "hash,prove", true),
"tx_search": rpcserver.NewRPCFunc(makeTxSearchFunc(c), "query,prove,page,per_page,order_by", false),
"block_search": rpcserver.NewRPCFunc(makeBlockSearchFunc(c), "query,page,per_page,order_by", false),
"validators": rpcserver.NewRPCFunc(makeValidatorsFunc(c), "height,page,per_page", true),
"dump_consensus_state": rpcserver.NewRPCFunc(makeDumpConsensusStateFunc(c), "", false),
"consensus_state": rpcserver.NewRPCFunc(makeConsensusStateFunc(c), "", false),
"consensus_params": rpcserver.NewRPCFunc(makeConsensusParamsFunc(c), "height", true),
"unconfirmed_txs": rpcserver.NewRPCFunc(makeUnconfirmedTxsFunc(c), "limit", false),
"num_unconfirmed_txs": rpcserver.NewRPCFunc(makeNumUnconfirmedTxsFunc(c), "", false),
// tx broadcast API
"broadcast_tx_commit": rpcserver.NewRPCFunc(makeBroadcastTxCommitFunc(c), "tx"),
"broadcast_tx_sync": rpcserver.NewRPCFunc(makeBroadcastTxSyncFunc(c), "tx"),
"broadcast_tx_async": rpcserver.NewRPCFunc(makeBroadcastTxAsyncFunc(c), "tx"),
"broadcast_tx_commit": rpcserver.NewRPCFunc(makeBroadcastTxCommitFunc(c), "tx", false),
"broadcast_tx_sync": rpcserver.NewRPCFunc(makeBroadcastTxSyncFunc(c), "tx", false),
"broadcast_tx_async": rpcserver.NewRPCFunc(makeBroadcastTxAsyncFunc(c), "tx", false),
// abci API
"abci_query": rpcserver.NewRPCFunc(makeABCIQueryFunc(c), "path,data,height,prove"),
"abci_info": rpcserver.NewRPCFunc(makeABCIInfoFunc(c), ""),
"abci_query": rpcserver.NewRPCFunc(makeABCIQueryFunc(c), "path,data,height,prove", false),
"abci_info": rpcserver.NewRPCFunc(makeABCIInfoFunc(c), "", true),
// evidence API
"broadcast_evidence": rpcserver.NewRPCFunc(makeBroadcastEvidenceFunc(c), "evidence"),
"broadcast_evidence": rpcserver.NewRPCFunc(makeBroadcastEvidenceFunc(c), "evidence", false),
}
}


+ 28
- 28
rpc/core/routes.go View File

@ -14,43 +14,43 @@ var Routes = map[string]*rpc.RPCFunc{
"unsubscribe_all": rpc.NewWSRPCFunc(UnsubscribeAll, ""),
// info API
"health": rpc.NewRPCFunc(Health, ""),
"status": rpc.NewRPCFunc(Status, ""),
"net_info": rpc.NewRPCFunc(NetInfo, ""),
"blockchain": rpc.NewRPCFunc(BlockchainInfo, "minHeight,maxHeight"),
"genesis": rpc.NewRPCFunc(Genesis, ""),
"block": rpc.NewRPCFunc(Block, "height"),
"block_by_hash": rpc.NewRPCFunc(BlockByHash, "hash"),
"block_results": rpc.NewRPCFunc(BlockResults, "height"),
"commit": rpc.NewRPCFunc(Commit, "height"),
"check_tx": rpc.NewRPCFunc(CheckTx, "tx"),
"tx": rpc.NewRPCFunc(Tx, "hash,prove"),
"tx_search": rpc.NewRPCFunc(TxSearch, "query,prove,page,per_page,order_by"),
"block_search": rpc.NewRPCFunc(BlockSearch, "query,page,per_page,order_by"),
"validators": rpc.NewRPCFunc(Validators, "height,page,per_page"),
"dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, ""),
"consensus_state": rpc.NewRPCFunc(ConsensusState, ""),
"consensus_params": rpc.NewRPCFunc(ConsensusParams, "height"),
"unconfirmed_txs": rpc.NewRPCFunc(UnconfirmedTxs, "limit"),
"num_unconfirmed_txs": rpc.NewRPCFunc(NumUnconfirmedTxs, ""),
"health": rpc.NewRPCFunc(Health, "", false),
"status": rpc.NewRPCFunc(Status, "", false),
"net_info": rpc.NewRPCFunc(NetInfo, "", false),
"blockchain": rpc.NewRPCFunc(BlockchainInfo, "minHeight,maxHeight", true),
"genesis": rpc.NewRPCFunc(Genesis, "", true),
"block": rpc.NewRPCFunc(Block, "height", true),
"block_by_hash": rpc.NewRPCFunc(BlockByHash, "hash", true),
"block_results": rpc.NewRPCFunc(BlockResults, "height", true),
"commit": rpc.NewRPCFunc(Commit, "height", true),
"check_tx": rpc.NewRPCFunc(CheckTx, "tx", true),
"tx": rpc.NewRPCFunc(Tx, "hash,prove", true),
"tx_search": rpc.NewRPCFunc(TxSearch, "query,prove,page,per_page,order_by", false),
"block_search": rpc.NewRPCFunc(BlockSearch, "query,page,per_page,order_by", false),
"validators": rpc.NewRPCFunc(Validators, "height,page,per_page", true),
"dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, "", false),
"consensus_state": rpc.NewRPCFunc(ConsensusState, "", false),
"consensus_params": rpc.NewRPCFunc(ConsensusParams, "height", true),
"unconfirmed_txs": rpc.NewRPCFunc(UnconfirmedTxs, "limit", false),
"num_unconfirmed_txs": rpc.NewRPCFunc(NumUnconfirmedTxs, "", false),
// tx broadcast API
"broadcast_tx_commit": rpc.NewRPCFunc(BroadcastTxCommit, "tx"),
"broadcast_tx_sync": rpc.NewRPCFunc(BroadcastTxSync, "tx"),
"broadcast_tx_async": rpc.NewRPCFunc(BroadcastTxAsync, "tx"),
"broadcast_tx_commit": rpc.NewRPCFunc(BroadcastTxCommit, "tx", false),
"broadcast_tx_sync": rpc.NewRPCFunc(BroadcastTxSync, "tx", false),
"broadcast_tx_async": rpc.NewRPCFunc(BroadcastTxAsync, "tx", false),
// abci API
"abci_query": rpc.NewRPCFunc(ABCIQuery, "path,data,height,prove"),
"abci_info": rpc.NewRPCFunc(ABCIInfo, ""),
"abci_query": rpc.NewRPCFunc(ABCIQuery, "path,data,height,prove", false),
"abci_info": rpc.NewRPCFunc(ABCIInfo, "", true),
// evidence API
"broadcast_evidence": rpc.NewRPCFunc(BroadcastEvidence, "evidence"),
"broadcast_evidence": rpc.NewRPCFunc(BroadcastEvidence, "evidence", false),
}
// AddUnsafeRoutes adds unsafe routes.
func AddUnsafeRoutes() {
// control API
Routes["dial_seeds"] = rpc.NewRPCFunc(UnsafeDialSeeds, "seeds")
Routes["dial_peers"] = rpc.NewRPCFunc(UnsafeDialPeers, "peers,persistent,unconditional,private")
Routes["unsafe_flush_mempool"] = rpc.NewRPCFunc(UnsafeFlushMempool, "")
Routes["dial_seeds"] = rpc.NewRPCFunc(UnsafeDialSeeds, "seeds", false)
Routes["dial_peers"] = rpc.NewRPCFunc(UnsafeDialPeers, "peers,persistent,unconditional,private", false)
Routes["unsafe_flush_mempool"] = rpc.NewRPCFunc(UnsafeFlushMempool, "", false)
}

+ 1
- 1
rpc/jsonrpc/doc.go View File

@ -55,7 +55,7 @@
// Define some routes
//
// var Routes = map[string]*rpcserver.RPCFunc{
// "status": rpcserver.NewRPCFunc(Status, "arg"),
// "status": rpcserver.NewRPCFunc(Status, "arg", false),
// }
//
// An rpc function:


+ 4
- 4
rpc/jsonrpc/jsonrpc_test.go View File

@ -59,11 +59,11 @@ type ResultEchoDataBytes struct {
// Define some routes
var Routes = map[string]*server.RPCFunc{
"echo": server.NewRPCFunc(EchoResult, "arg"),
"echo": server.NewRPCFunc(EchoResult, "arg", false),
"echo_ws": server.NewWSRPCFunc(EchoWSResult, "arg"),
"echo_bytes": server.NewRPCFunc(EchoBytesResult, "arg"),
"echo_data_bytes": server.NewRPCFunc(EchoDataBytesResult, "arg"),
"echo_int": server.NewRPCFunc(EchoIntResult, "arg"),
"echo_bytes": server.NewRPCFunc(EchoBytesResult, "arg", false),
"echo_data_bytes": server.NewRPCFunc(EchoDataBytesResult, "arg", false),
"echo_int": server.NewRPCFunc(EchoIntResult, "arg", false),
}
func EchoResult(ctx *types.Context, v string) (*ResultEcho, error) {


+ 30
- 3
rpc/jsonrpc/server/http_json_handler.go View File

@ -57,6 +57,11 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
requests = []types.RPCRequest{request}
}
// Set the default response cache to true unless
// 1. Any RPC request rrror.
// 2. Any RPC request doesn't allow to be cached.
// 3. Any RPC request has the height argument and the value is 0 (the default).
var c = true
for _, request := range requests {
request := request
@ -74,11 +79,13 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
responses,
types.RPCInvalidRequestError(request.ID, fmt.Errorf("path %s is invalid", r.URL.Path)),
)
c = false
continue
}
rpcFunc, ok := funcMap[request.Method]
if !ok || rpcFunc.ws {
responses = append(responses, types.RPCMethodNotFoundError(request.ID))
c = false
continue
}
ctx := &types.Context{JSONReq: &request, HTTPReq: r}
@ -90,9 +97,15 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
responses,
types.RPCInvalidParamsError(request.ID, fmt.Errorf("error converting json params to arguments: %w", err)),
)
c = false
continue
}
args = append(args, fnArgs...)
}
if hasDefaultHeight(request, args) {
c = false
}
returns := rpcFunc.f.Call(args)
@ -106,23 +119,28 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
// if this already of type RPC error then forward that error
case *types.RPCError:
responses = append(responses, types.NewRPCErrorResponse(request.ID, e.Code, e.Message, e.Data))
c = false
default: // we need to unwrap the error and parse it accordingly
switch errors.Unwrap(err) {
// check if the error was due to an invald request
case ctypes.ErrZeroOrNegativeHeight, ctypes.ErrZeroOrNegativePerPage,
ctypes.ErrPageOutOfRange, ctypes.ErrInvalidRequest:
responses = append(responses, types.RPCInvalidRequestError(request.ID, err))
c = false
// lastly default all remaining errors as internal errors
default: // includes ctypes.ErrHeightNotAvailable and ctypes.ErrHeightExceedsChainHead
responses = append(responses, types.RPCInternalError(request.ID, err))
c = false
}
}
if c && !rpcFunc.cache {
c = false
}
}
if len(responses) > 0 {
if wErr := WriteRPCResponseHTTP(w, responses...); wErr != nil {
if wErr := WriteRPCResponseHTTP(w, c, responses...); wErr != nil {
logger.Error("failed to write responses", "res", responses, "err", wErr)
}
}
@ -258,3 +276,12 @@ func writeListOfEndpoints(w http.ResponseWriter, r *http.Request, funcMap map[st
w.WriteHeader(200)
w.Write(buf.Bytes()) // nolint: errcheck
}
func hasDefaultHeight(r types.RPCRequest, h []reflect.Value) bool {
switch r.Method {
case "block", "block_results", "commit", "consensus_params", "validators":
return len(h) < 2 || h[1].IsZero()
default:
return false
}
}

+ 34
- 1
rpc/jsonrpc/server/http_json_handler_test.go View File

@ -18,7 +18,8 @@ import (
func testMux() *http.ServeMux {
funcMap := map[string]*RPCFunc{
"c": NewRPCFunc(func(ctx *types.Context, s string, i int) (string, error) { return "foo", nil }, "s,i"),
"c": NewRPCFunc(func(ctx *types.Context, s string, i int) (string, error) { return "foo", nil }, "s,i", false),
"block": NewRPCFunc(func(ctx *types.Context, h int) (string, error) { return "block", nil }, "height", true),
}
mux := http.NewServeMux()
buf := new(bytes.Buffer)
@ -227,3 +228,35 @@ func TestUnknownRPCPath(t *testing.T) {
require.Equal(t, http.StatusNotFound, res.StatusCode, "should always return 404")
res.Body.Close()
}
func TestRPCResponseCache(t *testing.T) {
mux := testMux()
body := strings.NewReader(`{"jsonrpc": "2.0","method":"block","id": 0, "params": ["1"]}`)
req, _ := http.NewRequest("Get", "http://localhost/", body)
rec := httptest.NewRecorder()
mux.ServeHTTP(rec, req)
res := rec.Result()
// Always expecting back a JSONRPCResponse
require.True(t, statusOK(res.StatusCode), "should always return 2XX")
require.Equal(t, "max-age=31536000", res.Header.Get("Cache-control"))
_, err := ioutil.ReadAll(res.Body)
res.Body.Close()
require.Nil(t, err, "reading from the body should not give back an error")
// send a request with default height.
body = strings.NewReader(`{"jsonrpc": "2.0","method":"block","id": 0, "params": ["0"]}`)
req, _ = http.NewRequest("Get", "http://localhost/", body)
rec = httptest.NewRecorder()
mux.ServeHTTP(rec, req)
res = rec.Result()
// Always expecting back a JSONRPCResponse
require.True(t, statusOK(res.StatusCode), "should always return 2XX")
require.Equal(t, "", res.Header.Get("Cache-control"))
_, err = ioutil.ReadAll(res.Body)
res.Body.Close()
require.Nil(t, err, "reading from the body should not give back an error")
}

+ 6
- 2
rpc/jsonrpc/server/http_server.go View File

@ -133,7 +133,8 @@ func WriteRPCResponseHTTPError(
}
// WriteRPCResponseHTTP marshals res as JSON (with indent) and writes it to w.
func WriteRPCResponseHTTP(w http.ResponseWriter, res ...types.RPCResponse) error {
// If the rpc response can be cached, add cache-control to the response header.
func WriteRPCResponseHTTP(w http.ResponseWriter, c bool, res ...types.RPCResponse) error {
var v interface{}
if len(res) == 1 {
v = res[0]
@ -146,6 +147,9 @@ func WriteRPCResponseHTTP(w http.ResponseWriter, res ...types.RPCResponse) error
return fmt.Errorf("json marshal: %w", err)
}
w.Header().Set("Content-Type", "application/json")
if c {
w.Header().Set("Cache-Control", "max-age=31536000") // expired after one year
}
w.WriteHeader(200)
_, err = w.Write(jsonBytes)
return err
@ -186,7 +190,7 @@ func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler
// If RPCResponse
if res, ok := e.(types.RPCResponse); ok {
if wErr := WriteRPCResponseHTTP(rww, res); wErr != nil {
if wErr := WriteRPCResponseHTTP(rww, false, res); wErr != nil {
logger.Error("failed to write response", "res", res, "err", wErr)
}
} else {


+ 3
- 1
rpc/jsonrpc/server/http_server_test.go View File

@ -112,7 +112,7 @@ func TestWriteRPCResponseHTTP(t *testing.T) {
// one argument
w := httptest.NewRecorder()
err := WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(id, &sampleResult{"hello"}))
err := WriteRPCResponseHTTP(w, true, types.NewRPCSuccessResponse(id, &sampleResult{"hello"}))
require.NoError(t, err)
resp := w.Result()
body, err := ioutil.ReadAll(resp.Body)
@ -120,6 +120,7 @@ func TestWriteRPCResponseHTTP(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, 200, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
assert.Equal(t, "max-age=31536000", resp.Header.Get("Cache-control"))
assert.Equal(t, `{
"jsonrpc": "2.0",
"id": -1,
@ -131,6 +132,7 @@ func TestWriteRPCResponseHTTP(t *testing.T) {
// multiple arguments
w = httptest.NewRecorder()
err = WriteRPCResponseHTTP(w,
false,
types.NewRPCSuccessResponse(id, &sampleResult{"hello"}),
types.NewRPCSuccessResponse(id, &sampleResult{"world"}))
require.NoError(t, err)


+ 1
- 1
rpc/jsonrpc/server/http_uri_handler.go View File

@ -61,7 +61,7 @@ func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWrit
// if no error then return a success response
case nil:
res := types.NewRPCSuccessResponse(dummyID, result)
if wErr := WriteRPCResponseHTTP(w, res); wErr != nil {
if wErr := WriteRPCResponseHTTP(w, rpcFunc.cache, res); wErr != nil {
logger.Error("failed to write response", "res", res, "err", wErr)
}


+ 2
- 2
rpc/jsonrpc/server/parse_test.go View File

@ -135,7 +135,7 @@ func TestParseJSONArray(t *testing.T) {
func TestParseJSONRPC(t *testing.T) {
demo := func(ctx *types.Context, height int, name string) {}
call := NewRPCFunc(demo, "height,name")
call := NewRPCFunc(demo, "height,name", false)
cases := []struct {
raw string
@ -172,7 +172,7 @@ func TestParseJSONRPC(t *testing.T) {
func TestParseURI(t *testing.T) {
demo := func(ctx *types.Context, height int, name string) {}
call := NewRPCFunc(demo, "height,name")
call := NewRPCFunc(demo, "height,name", false)
cases := []struct {
raw []string


+ 7
- 4
rpc/jsonrpc/server/rpc_func.go View File

@ -31,20 +31,22 @@ type RPCFunc struct {
returns []reflect.Type // type of each return arg
argNames []string // name of each argument
ws bool // websocket only
cache bool // allow the RPC response can be cached by the proxy cache server
}
// NewRPCFunc wraps a function for introspection.
// f is the function, args are comma separated argument names
func NewRPCFunc(f interface{}, args string) *RPCFunc {
return newRPCFunc(f, args, false)
// cache is a bool value to allow the client proxy server to cache the RPC results
func NewRPCFunc(f interface{}, args string, cache bool) *RPCFunc {
return newRPCFunc(f, args, false, cache)
}
// NewWSRPCFunc wraps a function for introspection and use in the websockets.
func NewWSRPCFunc(f interface{}, args string) *RPCFunc {
return newRPCFunc(f, args, true)
return newRPCFunc(f, args, true, false)
}
func newRPCFunc(f interface{}, args string, ws bool) *RPCFunc {
func newRPCFunc(f interface{}, args string, ws bool, c bool) *RPCFunc {
var argNames []string
if args != "" {
argNames = strings.Split(args, ",")
@ -55,6 +57,7 @@ func newRPCFunc(f interface{}, args string, ws bool) *RPCFunc {
returns: funcReturnTypes(f),
argNames: argNames,
ws: ws,
cache: c,
}
}


+ 1
- 1
rpc/jsonrpc/test/main.go View File

@ -12,7 +12,7 @@ import (
)
var routes = map[string]*rpcserver.RPCFunc{
"hello_world": rpcserver.NewRPCFunc(HelloWorld, "name,num"),
"hello_world": rpcserver.NewRPCFunc(HelloWorld, "name,num", false),
}
func HelloWorld(ctx *rpctypes.Context, name string, num int) (Result, error) {


+ 1
- 1
test/fuzz/rpc/jsonrpc/server/handler.go View File

@ -14,7 +14,7 @@ import (
)
var rpcFuncMap = map[string]*rs.RPCFunc{
"c": rs.NewRPCFunc(func(s string, i int) (string, int) { return "foo", 200 }, "s,i"),
"c": rs.NewRPCFunc(func(s string, i int) (string, int) { return "foo", 200 }, "s,i", false),
}
var mux *http.ServeMux


Loading…
Cancel
Save