You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

97 lines
3.1 KiB

  1. package rpcserver_test
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "io/ioutil"
  6. "net/http"
  7. "net/http/httptest"
  8. "strings"
  9. "testing"
  10. "github.com/stretchr/testify/assert"
  11. "github.com/stretchr/testify/require"
  12. rs "github.com/tendermint/tendermint/rpc/lib/server"
  13. types "github.com/tendermint/tendermint/rpc/lib/types"
  14. "github.com/tendermint/tmlibs/log"
  15. )
  16. func testMux() *http.ServeMux {
  17. funcMap := map[string]*rs.RPCFunc{
  18. "c": rs.NewRPCFunc(func(s string, i int) (string, error) { return "foo", nil }, "s,i"),
  19. }
  20. mux := http.NewServeMux()
  21. buf := new(bytes.Buffer)
  22. logger := log.NewTMLogger(buf)
  23. rs.RegisterRPCFuncs(mux, funcMap, logger)
  24. return mux
  25. }
  26. func statusOK(code int) bool { return code >= 200 && code <= 299 }
  27. // Ensure that nefarious/unintended inputs to `params`
  28. // do not crash our RPC handlers.
  29. // See Issue https://github.com/tendermint/tendermint/issues/708.
  30. func TestRPCParams(t *testing.T) {
  31. mux := testMux()
  32. tests := []struct {
  33. payload string
  34. wantErr string
  35. }{
  36. // bad
  37. {`{"jsonrpc": "2.0", "id": "0"}`, "Method not found"},
  38. {`{"jsonrpc": "2.0", "method": "y", "id": "0"}`, "Method not found"},
  39. {`{"method": "c", "id": "0", "params": a}`, "invalid character"},
  40. {`{"method": "c", "id": "0", "params": ["a"]}`, "got 1"},
  41. {`{"method": "c", "id": "0", "params": ["a", "b"]}`, "of type int"},
  42. {`{"method": "c", "id": "0", "params": [1, 1]}`, "of type string"},
  43. // good
  44. {`{"jsonrpc": "2.0", "method": "c", "id": "0", "params": null}`, ""},
  45. {`{"method": "c", "id": "0", "params": {}}`, ""},
  46. {`{"method": "c", "id": "0", "params": ["a", 10]}`, ""},
  47. }
  48. for i, tt := range tests {
  49. req, _ := http.NewRequest("POST", "http://localhost/", strings.NewReader(tt.payload))
  50. rec := httptest.NewRecorder()
  51. mux.ServeHTTP(rec, req)
  52. res := rec.Result()
  53. // Always expecting back a JSONRPCResponse
  54. assert.True(t, statusOK(res.StatusCode), "#%d: should always return 2XX", i)
  55. blob, err := ioutil.ReadAll(res.Body)
  56. if err != nil {
  57. t.Errorf("#%d: err reading body: %v", i, err)
  58. continue
  59. }
  60. recv := new(types.RPCResponse)
  61. assert.Nil(t, json.Unmarshal(blob, recv), "#%d: expecting successful parsing of an RPCResponse:\nblob: %s", i, blob)
  62. assert.NotEqual(t, recv, new(types.RPCResponse), "#%d: not expecting a blank RPCResponse", i)
  63. if tt.wantErr == "" {
  64. assert.Nil(t, recv.Error, "#%d: not expecting an error", i)
  65. } else {
  66. assert.True(t, recv.Error.Code < 0, "#%d: not expecting a positive JSONRPC code", i)
  67. // The wanted error is either in the message or the data
  68. assert.Contains(t, recv.Error.Message+recv.Error.Data, tt.wantErr, "#%d: expected substring", i)
  69. }
  70. }
  71. }
  72. func TestRPCNotification(t *testing.T) {
  73. mux := testMux()
  74. body := strings.NewReader(`{"jsonrpc": "2.0"}`)
  75. req, _ := http.NewRequest("POST", "http://localhost/", body)
  76. rec := httptest.NewRecorder()
  77. mux.ServeHTTP(rec, req)
  78. res := rec.Result()
  79. // Always expecting back a JSONRPCResponse
  80. require.True(t, statusOK(res.StatusCode), "should always return 2XX")
  81. blob, err := ioutil.ReadAll(res.Body)
  82. require.Nil(t, err, "reading from the body should not give back an error")
  83. require.Equal(t, len(blob), 0, "a notification SHOULD NOT be responded to by the server")
  84. }