github.com/okex/exchain@v1.8.0/libs/tendermint/rpc/jsonrpc/server/http_json_handler_test.go (about) 1 package server 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "io/ioutil" 7 "net/http" 8 "net/http/httptest" 9 "strings" 10 "testing" 11 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 15 amino "github.com/tendermint/go-amino" 16 17 "github.com/okex/exchain/libs/tendermint/libs/log" 18 types "github.com/okex/exchain/libs/tendermint/rpc/jsonrpc/types" 19 ) 20 21 func testMux() *http.ServeMux { 22 funcMap := map[string]*RPCFunc{ 23 "c": NewRPCFunc(func(ctx *types.Context, s string, i int) (string, error) { return "foo", nil }, "s,i"), 24 } 25 cdc := amino.NewCodec() 26 mux := http.NewServeMux() 27 buf := new(bytes.Buffer) 28 logger := log.NewTMLogger(buf) 29 RegisterRPCFuncs(mux, funcMap, cdc, logger) 30 31 return mux 32 } 33 34 func statusOK(code int) bool { return code >= 200 && code <= 299 } 35 36 // Ensure that nefarious/unintended inputs to `params` 37 // do not crash our RPC handlers. 38 // See Issue https://github.com/tendermint/tendermint/issues/708. 39 func TestRPCParams(t *testing.T) { 40 mux := testMux() 41 tests := []struct { 42 payload string 43 wantErr string 44 expectedID interface{} 45 }{ 46 // bad 47 {`{"jsonrpc": "2.0", "id": "0"}`, "Method not found", types.JSONRPCStringID("0")}, 48 {`{"jsonrpc": "2.0", "method": "y", "id": "0"}`, "Method not found", types.JSONRPCStringID("0")}, 49 // id not captured in JSON parsing failures 50 {`{"method": "c", "id": "0", "params": a}`, "invalid character", nil}, 51 {`{"method": "c", "id": "0", "params": ["a"]}`, "got 1", types.JSONRPCStringID("0")}, 52 {`{"method": "c", "id": "0", "params": ["a", "b"]}`, "invalid character", types.JSONRPCStringID("0")}, 53 {`{"method": "c", "id": "0", "params": [1, 1]}`, "of type string", types.JSONRPCStringID("0")}, 54 55 // no ID - notification 56 // {`{"jsonrpc": "2.0", "method": "c", "params": ["a", "10"]}`, false, nil}, 57 58 // good 59 {`{"jsonrpc": "2.0", "method": "c", "id": "0", "params": null}`, "", types.JSONRPCStringID("0")}, 60 {`{"method": "c", "id": "0", "params": {}}`, "", types.JSONRPCStringID("0")}, 61 {`{"method": "c", "id": "0", "params": ["a", "10"]}`, "", types.JSONRPCStringID("0")}, 62 } 63 64 for i, tt := range tests { 65 req, _ := http.NewRequest("POST", "http://localhost/", strings.NewReader(tt.payload)) 66 rec := httptest.NewRecorder() 67 mux.ServeHTTP(rec, req) 68 res := rec.Result() 69 defer res.Body.Close() 70 // Always expecting back a JSONRPCResponse 71 assert.True(t, statusOK(res.StatusCode), "#%d: should always return 2XX", i) 72 blob, err := ioutil.ReadAll(res.Body) 73 if err != nil { 74 t.Errorf("#%d: err reading body: %v", i, err) 75 continue 76 } 77 78 recv := new(types.RPCResponse) 79 assert.Nil(t, json.Unmarshal(blob, recv), "#%d: expecting successful parsing of an RPCResponse:\nblob: %s", i, blob) 80 assert.NotEqual(t, recv, new(types.RPCResponse), "#%d: not expecting a blank RPCResponse", i) 81 assert.Equal(t, tt.expectedID, recv.ID, "#%d: expected ID not matched in RPCResponse", i) 82 if tt.wantErr == "" { 83 assert.Nil(t, recv.Error, "#%d: not expecting an error", i) 84 } else { 85 assert.True(t, recv.Error.Code < 0, "#%d: not expecting a positive JSONRPC code", i) 86 // The wanted error is either in the message or the data 87 assert.Contains(t, recv.Error.Message+recv.Error.Data, tt.wantErr, "#%d: expected substring", i) 88 } 89 } 90 } 91 92 func TestJSONRPCID(t *testing.T) { 93 mux := testMux() 94 tests := []struct { 95 payload string 96 wantErr bool 97 expectedID interface{} 98 }{ 99 // good id 100 {`{"jsonrpc": "2.0", "method": "c", "id": "0", "params": ["a", "10"]}`, false, types.JSONRPCStringID("0")}, 101 {`{"jsonrpc": "2.0", "method": "c", "id": "abc", "params": ["a", "10"]}`, false, types.JSONRPCStringID("abc")}, 102 {`{"jsonrpc": "2.0", "method": "c", "id": 0, "params": ["a", "10"]}`, false, types.JSONRPCIntID(0)}, 103 {`{"jsonrpc": "2.0", "method": "c", "id": 1, "params": ["a", "10"]}`, false, types.JSONRPCIntID(1)}, 104 {`{"jsonrpc": "2.0", "method": "c", "id": 1.3, "params": ["a", "10"]}`, false, types.JSONRPCIntID(1)}, 105 {`{"jsonrpc": "2.0", "method": "c", "id": -1, "params": ["a", "10"]}`, false, types.JSONRPCIntID(-1)}, 106 107 // bad id 108 {`{"jsonrpc": "2.0", "method": "c", "id": {}, "params": ["a", "10"]}`, true, nil}, 109 {`{"jsonrpc": "2.0", "method": "c", "id": [], "params": ["a", "10"]}`, true, nil}, 110 } 111 112 for i, tt := range tests { 113 req, _ := http.NewRequest("POST", "http://localhost/", strings.NewReader(tt.payload)) 114 rec := httptest.NewRecorder() 115 mux.ServeHTTP(rec, req) 116 res := rec.Result() 117 // Always expecting back a JSONRPCResponse 118 assert.True(t, statusOK(res.StatusCode), "#%d: should always return 2XX", i) 119 blob, err := ioutil.ReadAll(res.Body) 120 if err != nil { 121 t.Errorf("#%d: err reading body: %v", i, err) 122 continue 123 } 124 res.Body.Close() 125 126 recv := new(types.RPCResponse) 127 err = json.Unmarshal(blob, recv) 128 assert.Nil(t, err, "#%d: expecting successful parsing of an RPCResponse:\nblob: %s", i, blob) 129 if !tt.wantErr { 130 assert.NotEqual(t, recv, new(types.RPCResponse), "#%d: not expecting a blank RPCResponse", i) 131 assert.Equal(t, tt.expectedID, recv.ID, "#%d: expected ID not matched in RPCResponse", i) 132 assert.Nil(t, recv.Error, "#%d: not expecting an error", i) 133 } else { 134 assert.True(t, recv.Error.Code < 0, "#%d: not expecting a positive JSONRPC code", i) 135 } 136 } 137 } 138 139 func TestRPCNotification(t *testing.T) { 140 mux := testMux() 141 body := strings.NewReader(`{"jsonrpc": "2.0"}`) 142 req, _ := http.NewRequest("POST", "http://localhost/", body) 143 rec := httptest.NewRecorder() 144 mux.ServeHTTP(rec, req) 145 res := rec.Result() 146 147 // Always expecting back a JSONRPCResponse 148 require.True(t, statusOK(res.StatusCode), "should always return 2XX") 149 blob, err := ioutil.ReadAll(res.Body) 150 res.Body.Close() 151 require.Nil(t, err, "reading from the body should not give back an error") 152 require.Equal(t, len(blob), 0, "a notification SHOULD NOT be responded to by the server") 153 } 154 155 func TestRPCNotificationInBatch(t *testing.T) { 156 mux := testMux() 157 tests := []struct { 158 payload string 159 expectCount int 160 }{ 161 { 162 `[ 163 {"jsonrpc": "2.0"}, 164 {"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]} 165 ]`, 166 1, 167 }, 168 { 169 `[ 170 {"jsonrpc": "2.0"}, 171 {"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]}, 172 {"jsonrpc": "2.0"}, 173 {"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]} 174 ]`, 175 2, 176 }, 177 } 178 for i, tt := range tests { 179 req, _ := http.NewRequest("POST", "http://localhost/", strings.NewReader(tt.payload)) 180 rec := httptest.NewRecorder() 181 mux.ServeHTTP(rec, req) 182 res := rec.Result() 183 // Always expecting back a JSONRPCResponse 184 assert.True(t, statusOK(res.StatusCode), "#%d: should always return 2XX", i) 185 blob, err := ioutil.ReadAll(res.Body) 186 if err != nil { 187 t.Errorf("#%d: err reading body: %v", i, err) 188 continue 189 } 190 res.Body.Close() 191 192 var responses []types.RPCResponse 193 // try to unmarshal an array first 194 err = json.Unmarshal(blob, &responses) 195 if err != nil { 196 // if we were actually expecting an array, but got an error 197 if tt.expectCount > 1 { 198 t.Errorf("#%d: expected an array, couldn't unmarshal it\nblob: %s", i, blob) 199 continue 200 } else { 201 // we were expecting an error here, so let's unmarshal a single response 202 var response types.RPCResponse 203 err = json.Unmarshal(blob, &response) 204 if err != nil { 205 t.Errorf("#%d: expected successful parsing of an RPCResponse\nblob: %s", i, blob) 206 continue 207 } 208 // have a single-element result 209 responses = []types.RPCResponse{response} 210 } 211 } 212 if tt.expectCount != len(responses) { 213 t.Errorf("#%d: expected %d response(s), but got %d\nblob: %s", i, tt.expectCount, len(responses), blob) 214 continue 215 } 216 for _, response := range responses { 217 assert.NotEqual(t, response, new(types.RPCResponse), "#%d: not expecting a blank RPCResponse", i) 218 } 219 } 220 } 221 222 func TestUnknownRPCPath(t *testing.T) { 223 mux := testMux() 224 req, _ := http.NewRequest("GET", "http://localhost/unknownrpcpath", nil) 225 rec := httptest.NewRecorder() 226 mux.ServeHTTP(rec, req) 227 res := rec.Result() 228 229 // Always expecting back a 404 error 230 require.Equal(t, http.StatusNotFound, res.StatusCode, "should always return 404") 231 res.Body.Close() 232 }