rpc/jsonrpc/server: return an error in WriteRPCResponseHTTP(Error) (bp #6204) (#6230)

* rpc/jsonrpc/server: return an error in WriteRPCResponseHTTP(Error) (#6204)

instead of panicking
Closes #5529

(cherry picked from commit 00b9524168)

# Conflicts:
#	CHANGELOG_PENDING.md
#	rpc/jsonrpc/server/http_json_handler.go
#	rpc/jsonrpc/server/http_server.go
#	rpc/jsonrpc/server/http_server_test.go
#	rpc/jsonrpc/server/http_uri_handler.go

* resolve conflicts

* fix linting

* fix conflict

Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
Co-authored-by: Marko Baricevic <marbar3778@yahoo.com>
This commit is contained in:
mergify[bot]
2021-03-17 14:55:05 +00:00
committed by GitHub
parent d004a584f8
commit 4e25703d58
12 changed files with 76 additions and 66 deletions

View File

@@ -15,6 +15,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
- P2P Protocol - P2P Protocol
- Go API - Go API
- [rpc/jsonrpc/server] \#6204 Modify `WriteRPCResponseHTTP(Error)` to return an error (@melekes)
- Blockchain Protocol - Blockchain Protocol

View File

@@ -59,19 +59,22 @@ func (mp mockPeer) TrySend(byte, []byte) bool { return true }
func (mp mockPeer) Set(string, interface{}) {} func (mp mockPeer) Set(string, interface{}) {}
func (mp mockPeer) Get(string) interface{} { return struct{}{} } func (mp mockPeer) Get(string) interface{} { return struct{}{} }
//nolint:unused // nolint:unused // ignore
type mockBlockStore struct { type mockBlockStore struct {
blocks map[int64]*types.Block blocks map[int64]*types.Block
} }
// nolint:unused // ignore
func (ml *mockBlockStore) Height() int64 { func (ml *mockBlockStore) Height() int64 {
return int64(len(ml.blocks)) return int64(len(ml.blocks))
} }
// nolint:unused // ignore
func (ml *mockBlockStore) LoadBlock(height int64) *types.Block { func (ml *mockBlockStore) LoadBlock(height int64) *types.Block {
return ml.blocks[height] return ml.blocks[height]
} }
// nolint:unused // ignore
func (ml *mockBlockStore) SaveBlock(block *types.Block, part *types.PartSet, commit *types.Commit) { func (ml *mockBlockStore) SaveBlock(block *types.Block, part *types.PartSet, commit *types.Commit) {
ml.blocks[block.Height] = block ml.blocks[block.Height] = block
} }

View File

@@ -6,6 +6,6 @@ import (
func Sha256(bytes []byte) []byte { func Sha256(bytes []byte) []byte {
hasher := sha256.New() hasher := sha256.New()
hasher.Write(bytes) //nolint:errcheck // ignore error hasher.Write(bytes)
return hasher.Sum(nil) return hasher.Sum(nil)
} }

View File

@@ -80,13 +80,13 @@ func (op ValueOp) Run(args [][]byte) ([][]byte, error) {
} }
value := args[0] value := args[0]
hasher := tmhash.New() hasher := tmhash.New()
hasher.Write(value) //nolint: errcheck // does not error hasher.Write(value)
vhash := hasher.Sum(nil) vhash := hasher.Sum(nil)
bz := new(bytes.Buffer) bz := new(bytes.Buffer)
// Wrap <op.Key, vhash> to hash the KVPair. // Wrap <op.Key, vhash> to hash the KVPair.
encodeByteSlice(bz, op.key) //nolint: errcheck // does not error encodeByteSlice(bz, op.key) // nolint: errcheck // does not error
encodeByteSlice(bz, vhash) //nolint: errcheck // does not error encodeByteSlice(bz, vhash) // nolint: errcheck // does not error
kvhash := leafHash(bz.Bytes()) kvhash := leafHash(bz.Bytes())
if !bytes.Equal(kvhash, op.Proof.LeafHash) { if !bytes.Equal(kvhash, op.Proof.LeafHash) {

View File

@@ -662,7 +662,7 @@ func newRemoteApp(
} }
func checksumIt(data []byte) string { func checksumIt(data []byte) string {
h := sha256.New() h := sha256.New()
h.Write(data) //nolint: errcheck // ignore errcheck h.Write(data)
return fmt.Sprintf("%x", h.Sum(nil)) return fmt.Sprintf("%x", h.Sum(nil))
} }

View File

@@ -275,7 +275,6 @@ func (sc *SecretConnection) Read(data []byte) (n int, err error) {
} }
// Implements net.Conn // Implements net.Conn
// nolint
func (sc *SecretConnection) Close() error { return sc.conn.Close() } func (sc *SecretConnection) Close() error { return sc.conn.Close() }
func (sc *SecretConnection) LocalAddr() net.Addr { return sc.conn.(net.Conn).LocalAddr() } func (sc *SecretConnection) LocalAddr() net.Addr { return sc.conn.(net.Conn).LocalAddr() }
func (sc *SecretConnection) RemoteAddr() net.Addr { return sc.conn.(net.Conn).RemoteAddr() } func (sc *SecretConnection) RemoteAddr() net.Addr { return sc.conn.(net.Conn).RemoteAddr() }

View File

@@ -941,6 +941,6 @@ func (a *addrBook) hash(b []byte) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
hasher.Write(b) //nolint:errcheck // ignore error hasher.Write(b)
return hasher.Sum(nil), nil return hasher.Sum(nil), nil
} }

View File

@@ -21,14 +21,12 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
b, err := ioutil.ReadAll(r.Body) b, err := ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
WriteRPCResponseHTTPError( res := types.RPCInvalidRequestError(nil,
w, fmt.Errorf("error reading request body: %w", err),
http.StatusBadRequest,
types.RPCInvalidRequestError(
nil,
fmt.Errorf("error reading request body: %w", err),
),
) )
if wErr := WriteRPCResponseHTTPError(w, http.StatusBadRequest, res); wErr != nil {
logger.Error("failed to write response", "res", res, "err", wErr)
}
return return
} }
@@ -48,13 +46,10 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
// next, try to unmarshal as a single request // next, try to unmarshal as a single request
var request types.RPCRequest var request types.RPCRequest
if err := json.Unmarshal(b, &request); err != nil { if err := json.Unmarshal(b, &request); err != nil {
WriteRPCResponseHTTPError( res := types.RPCParseError(fmt.Errorf("error unmarshaling request: %w", err))
w, if wErr := WriteRPCResponseHTTPError(w, http.StatusInternalServerError, res); wErr != nil {
http.StatusInternalServerError, logger.Error("failed to write response", "res", res, "err", wErr)
types.RPCParseError( }
fmt.Errorf("error unmarshalling request: %w", err),
),
)
return return
} }
requests = []types.RPCRequest{request} requests = []types.RPCRequest{request}
@@ -108,7 +103,9 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
} }
if len(responses) > 0 { if len(responses) > 0 {
WriteRPCResponseHTTP(w, responses...) if wErr := WriteRPCResponseHTTP(w, responses...); wErr != nil {
logger.Error("failed to write responses", "res", responses, "err", wErr)
}
} }
} }
} }

View File

@@ -89,30 +89,32 @@ func ServeTLS(
return err return err
} }
// WriteRPCResponseHTTPError marshals res as JSON and writes it to w. // WriteRPCResponseHTTPError marshals res as JSON (with indent) and writes it
// to w.
// //
// Panics if it can't Marshal res or write to w. // source: https://www.jsonrpc.org/historical/json-rpc-over-http.html
func WriteRPCResponseHTTPError( func WriteRPCResponseHTTPError(
w http.ResponseWriter, w http.ResponseWriter,
httpCode int, httpCode int,
res types.RPCResponse, res types.RPCResponse,
) { ) error {
if res.Error == nil {
panic("tried to write http error response without RPC error")
}
jsonBytes, err := json.MarshalIndent(res, "", " ") jsonBytes, err := json.MarshalIndent(res, "", " ")
if err != nil { if err != nil {
panic(err) return fmt.Errorf("json marshal: %w", err)
} }
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(httpCode) w.WriteHeader(httpCode)
if _, err := w.Write(jsonBytes); err != nil { _, err = w.Write(jsonBytes)
panic(err) return err
}
} }
// WriteRPCResponseHTTP marshals res as JSON and writes it to w. // WriteRPCResponseHTTP marshals res as JSON (with indent) and writes it to w.
// func WriteRPCResponseHTTP(w http.ResponseWriter, res ...types.RPCResponse) error {
// Panics if it can't Marshal res or write to w.
func WriteRPCResponseHTTP(w http.ResponseWriter, res ...types.RPCResponse) {
var v interface{} var v interface{}
if len(res) == 1 { if len(res) == 1 {
v = res[0] v = res[0]
@@ -122,13 +124,12 @@ func WriteRPCResponseHTTP(w http.ResponseWriter, res ...types.RPCResponse) {
jsonBytes, err := json.MarshalIndent(v, "", " ") jsonBytes, err := json.MarshalIndent(v, "", " ")
if err != nil { if err != nil {
panic(err) return fmt.Errorf("json marshal: %w", err)
} }
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200) w.WriteHeader(200)
if _, err := w.Write(jsonBytes); err != nil { _, err = w.Write(jsonBytes)
panic(err) return err
}
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@@ -166,7 +167,9 @@ func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler
// If RPCResponse // If RPCResponse
if res, ok := e.(types.RPCResponse); ok { if res, ok := e.(types.RPCResponse); ok {
WriteRPCResponseHTTP(rww, res) if wErr := WriteRPCResponseHTTP(rww, res); wErr != nil {
logger.Error("failed to write response", "res", res, "err", wErr)
}
} else { } else {
// Panics can contain anything, attempt to normalize it as an error. // Panics can contain anything, attempt to normalize it as an error.
var err error var err error
@@ -180,15 +183,12 @@ func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler
default: default:
} }
logger.Error( logger.Error("panic in RPC HTTP handler", "err", e, "stack", string(debug.Stack()))
"Panic in RPC HTTP handler", "err", e, "stack",
string(debug.Stack()), res := types.RPCInternalError(types.JSONRPCIntID(-1), err)
) if wErr := WriteRPCResponseHTTPError(rww, http.StatusInternalServerError, res); wErr != nil {
WriteRPCResponseHTTPError( logger.Error("failed to write response", "res", res, "err", wErr)
rww, }
http.StatusInternalServerError,
types.RPCInternalError(types.JSONRPCIntID(-1), err),
)
} }
} }

View File

@@ -112,7 +112,8 @@ func TestWriteRPCResponseHTTP(t *testing.T) {
// one argument // one argument
w := httptest.NewRecorder() w := httptest.NewRecorder()
WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(id, &sampleResult{"hello"})) err := WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(id, &sampleResult{"hello"}))
require.NoError(t, err)
resp := w.Result() resp := w.Result()
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
_ = resp.Body.Close() _ = resp.Body.Close()
@@ -129,9 +130,10 @@ func TestWriteRPCResponseHTTP(t *testing.T) {
// multiple arguments // multiple arguments
w = httptest.NewRecorder() w = httptest.NewRecorder()
WriteRPCResponseHTTP(w, err = WriteRPCResponseHTTP(w,
types.NewRPCSuccessResponse(id, &sampleResult{"hello"}), types.NewRPCSuccessResponse(id, &sampleResult{"hello"}),
types.NewRPCSuccessResponse(id, &sampleResult{"world"})) types.NewRPCSuccessResponse(id, &sampleResult{"world"}))
require.NoError(t, err)
resp = w.Result() resp = w.Result()
body, err = ioutil.ReadAll(resp.Body) body, err = ioutil.ReadAll(resp.Body)
_ = resp.Body.Close() _ = resp.Body.Close()
@@ -159,9 +161,11 @@ func TestWriteRPCResponseHTTP(t *testing.T) {
func TestWriteRPCResponseHTTPError(t *testing.T) { func TestWriteRPCResponseHTTPError(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
WriteRPCResponseHTTPError(w, err := WriteRPCResponseHTTPError(
w,
http.StatusInternalServerError, http.StatusInternalServerError,
types.RPCInternalError(types.JSONRPCIntID(-1), errors.New("foo"))) types.RPCInternalError(types.JSONRPCIntID(-1), errors.New("foo")))
require.NoError(t, err)
resp := w.Result() resp := w.Result()
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
_ = resp.Body.Close() _ = resp.Body.Close()

View File

@@ -25,8 +25,10 @@ func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWrit
// 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) {
WriteRPCResponseHTTPError(w, http.StatusNotFound, res := types.RPCMethodNotFoundError(dummyID)
types.RPCMethodNotFoundError(dummyID)) if wErr := WriteRPCResponseHTTPError(w, http.StatusNotFound, res); wErr != nil {
logger.Error("failed to write response", "res", res, "err", wErr)
}
} }
} }
@@ -39,14 +41,12 @@ func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWrit
fnArgs, err := httpParamsToArgs(rpcFunc, r) fnArgs, err := httpParamsToArgs(rpcFunc, r)
if err != nil { if err != nil {
WriteRPCResponseHTTPError( res := types.RPCInvalidParamsError(dummyID,
w, fmt.Errorf("error converting http params to arguments: %w", err),
http.StatusInternalServerError,
types.RPCInvalidParamsError(
dummyID,
fmt.Errorf("error converting http params to arguments: %w", err),
),
) )
if wErr := WriteRPCResponseHTTPError(w, http.StatusInternalServerError, res); wErr != nil {
logger.Error("failed to write response", "res", res, "err", wErr)
}
return return
} }
args = append(args, fnArgs...) args = append(args, fnArgs...)
@@ -56,11 +56,17 @@ func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWrit
logger.Debug("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns) logger.Debug("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns)
result, err := unreflectResult(returns) result, err := unreflectResult(returns)
if err != nil { if err != nil {
WriteRPCResponseHTTPError(w, http.StatusInternalServerError, if err := WriteRPCResponseHTTPError(w, http.StatusInternalServerError,
types.RPCInternalError(dummyID, err)) types.RPCInternalError(dummyID, err)); err != nil {
logger.Error("failed to write response", "res", result, "err", err)
return
}
return
}
if err := WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(dummyID, result)); err != nil {
logger.Error("failed to write response", "res", result, "err", err)
return return
} }
WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(dummyID, result))
} }
} }

View File

@@ -32,9 +32,9 @@ type snapshot struct {
func (s *snapshot) Key() snapshotKey { func (s *snapshot) Key() snapshotKey {
// Hash.Write() never returns an error. // Hash.Write() never returns an error.
hasher := sha256.New() hasher := sha256.New()
hasher.Write([]byte(fmt.Sprintf("%v:%v:%v", s.Height, s.Format, s.Chunks))) //nolint:errcheck // ignore error hasher.Write([]byte(fmt.Sprintf("%v:%v:%v", s.Height, s.Format, s.Chunks)))
hasher.Write(s.Hash) //nolint:errcheck // ignore error hasher.Write(s.Hash)
hasher.Write(s.Metadata) //nolint:errcheck // ignore error hasher.Write(s.Metadata)
var key snapshotKey var key snapshotKey
copy(key[:], hasher.Sum(nil)) copy(key[:], hasher.Sum(nil))
return key return key