mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-04 04:04:00 +00:00
Set cache control in the HTTP-RPC response header (#6265)
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user