github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/rpc/jsonrpc/jsonrpc_test.go (about) 1 package jsonrpc 2 3 import ( 4 "bytes" 5 "context" 6 crand "crypto/rand" 7 "encoding/json" 8 "fmt" 9 "net/http" 10 "os" 11 "os/exec" 12 "testing" 13 "time" 14 15 "github.com/go-kit/kit/log/term" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 19 tmbytes "github.com/tendermint/tendermint/libs/bytes" 20 "github.com/tendermint/tendermint/libs/log" 21 tmrand "github.com/tendermint/tendermint/libs/rand" 22 23 client "github.com/tendermint/tendermint/rpc/jsonrpc/client" 24 server "github.com/tendermint/tendermint/rpc/jsonrpc/server" 25 types "github.com/tendermint/tendermint/rpc/jsonrpc/types" 26 ) 27 28 // Client and Server should work over tcp or unix sockets 29 const ( 30 tcpAddr = "tcp://127.0.0.1:47768" 31 32 unixSocket = "/tmp/rpc_test.sock" 33 unixAddr = "unix://" + unixSocket 34 35 websocketEndpoint = "/websocket/endpoint" 36 37 testVal = "acbd" 38 ) 39 40 type ResultEcho struct { 41 Value string `json:"value"` 42 } 43 44 type ResultEchoInt struct { 45 Value int `json:"value"` 46 } 47 48 type ResultEchoBytes struct { 49 Value []byte `json:"value"` 50 } 51 52 type ResultEchoDataBytes struct { 53 Value tmbytes.HexBytes `json:"value"` 54 } 55 56 // Define some routes 57 var Routes = map[string]*server.RPCFunc{ 58 "echo": server.NewRPCFunc(EchoResult, "arg"), 59 "echo_ws": server.NewWSRPCFunc(EchoWSResult, "arg"), 60 "echo_bytes": server.NewRPCFunc(EchoBytesResult, "arg"), 61 "echo_data_bytes": server.NewRPCFunc(EchoDataBytesResult, "arg"), 62 "echo_int": server.NewRPCFunc(EchoIntResult, "arg"), 63 } 64 65 func EchoResult(ctx *types.Context, v string) (*ResultEcho, error) { 66 return &ResultEcho{v}, nil 67 } 68 69 func EchoWSResult(ctx *types.Context, v string) (*ResultEcho, error) { 70 return &ResultEcho{v}, nil 71 } 72 73 func EchoIntResult(ctx *types.Context, v int) (*ResultEchoInt, error) { 74 return &ResultEchoInt{v}, nil 75 } 76 77 func EchoBytesResult(ctx *types.Context, v []byte) (*ResultEchoBytes, error) { 78 return &ResultEchoBytes{v}, nil 79 } 80 81 func EchoDataBytesResult(ctx *types.Context, v tmbytes.HexBytes) (*ResultEchoDataBytes, error) { 82 return &ResultEchoDataBytes{v}, nil 83 } 84 85 func TestMain(m *testing.M) { 86 setup() 87 code := m.Run() 88 os.Exit(code) 89 } 90 91 var colorFn = func(keyvals ...interface{}) term.FgBgColor { 92 for i := 0; i < len(keyvals)-1; i += 2 { 93 if keyvals[i] == "socket" { 94 if keyvals[i+1] == "tcp" { 95 return term.FgBgColor{Fg: term.DarkBlue} 96 } else if keyvals[i+1] == "unix" { 97 return term.FgBgColor{Fg: term.DarkCyan} 98 } 99 } 100 } 101 return term.FgBgColor{} 102 } 103 104 // launch unix and tcp servers 105 func setup() { 106 logger := log.NewTMLoggerWithColorFn(log.NewSyncWriter(os.Stdout), colorFn) 107 108 cmd := exec.Command("rm", "-f", unixSocket) 109 err := cmd.Start() 110 if err != nil { 111 panic(err) 112 } 113 if err = cmd.Wait(); err != nil { 114 panic(err) 115 } 116 117 tcpLogger := logger.With("socket", "tcp") 118 mux := http.NewServeMux() 119 server.RegisterRPCFuncs(mux, Routes, tcpLogger) 120 wm := server.NewWebsocketManager(Routes, server.ReadWait(5*time.Second), server.PingPeriod(1*time.Second)) 121 wm.SetLogger(tcpLogger) 122 mux.HandleFunc(websocketEndpoint, wm.WebsocketHandler) 123 config := server.DefaultConfig() 124 listener1, err := server.Listen(tcpAddr, config) 125 if err != nil { 126 panic(err) 127 } 128 go server.Serve(listener1, mux, tcpLogger, config) 129 130 unixLogger := logger.With("socket", "unix") 131 mux2 := http.NewServeMux() 132 server.RegisterRPCFuncs(mux2, Routes, unixLogger) 133 wm = server.NewWebsocketManager(Routes) 134 wm.SetLogger(unixLogger) 135 mux2.HandleFunc(websocketEndpoint, wm.WebsocketHandler) 136 listener2, err := server.Listen(unixAddr, config) 137 if err != nil { 138 panic(err) 139 } 140 go server.Serve(listener2, mux2, unixLogger, config) 141 142 // wait for servers to start 143 time.Sleep(time.Second * 2) 144 } 145 146 func echoViaHTTP(cl client.Caller, val string) (string, error) { 147 params := map[string]interface{}{ 148 "arg": val, 149 } 150 result := new(ResultEcho) 151 if _, err := cl.Call("echo", params, result); err != nil { 152 return "", err 153 } 154 return result.Value, nil 155 } 156 157 func echoIntViaHTTP(cl client.Caller, val int) (int, error) { 158 params := map[string]interface{}{ 159 "arg": val, 160 } 161 result := new(ResultEchoInt) 162 if _, err := cl.Call("echo_int", params, result); err != nil { 163 return 0, err 164 } 165 return result.Value, nil 166 } 167 168 func echoBytesViaHTTP(cl client.Caller, bytes []byte) ([]byte, error) { 169 params := map[string]interface{}{ 170 "arg": bytes, 171 } 172 result := new(ResultEchoBytes) 173 if _, err := cl.Call("echo_bytes", params, result); err != nil { 174 return []byte{}, err 175 } 176 return result.Value, nil 177 } 178 179 func echoDataBytesViaHTTP(cl client.Caller, bytes tmbytes.HexBytes) (tmbytes.HexBytes, error) { 180 params := map[string]interface{}{ 181 "arg": bytes, 182 } 183 result := new(ResultEchoDataBytes) 184 if _, err := cl.Call("echo_data_bytes", params, result); err != nil { 185 return []byte{}, err 186 } 187 return result.Value, nil 188 } 189 190 func testWithHTTPClient(t *testing.T, cl client.HTTPClient) { 191 val := testVal 192 got, err := echoViaHTTP(cl, val) 193 require.Nil(t, err) 194 assert.Equal(t, got, val) 195 196 val2 := randBytes(t) 197 got2, err := echoBytesViaHTTP(cl, val2) 198 require.Nil(t, err) 199 assert.Equal(t, got2, val2) 200 201 val3 := tmbytes.HexBytes(randBytes(t)) 202 got3, err := echoDataBytesViaHTTP(cl, val3) 203 require.Nil(t, err) 204 assert.Equal(t, got3, val3) 205 206 val4 := tmrand.Intn(10000) 207 got4, err := echoIntViaHTTP(cl, val4) 208 require.Nil(t, err) 209 assert.Equal(t, got4, val4) 210 } 211 212 func echoViaWS(cl *client.WSClient, val string) (string, error) { 213 params := map[string]interface{}{ 214 "arg": val, 215 } 216 err := cl.Call(context.Background(), "echo", params) 217 if err != nil { 218 return "", err 219 } 220 221 msg := <-cl.ResponsesCh 222 if msg.Error != nil { 223 return "", err 224 225 } 226 result := new(ResultEcho) 227 err = json.Unmarshal(msg.Result, result) 228 if err != nil { 229 return "", nil 230 } 231 return result.Value, nil 232 } 233 234 func echoBytesViaWS(cl *client.WSClient, bytes []byte) ([]byte, error) { 235 params := map[string]interface{}{ 236 "arg": bytes, 237 } 238 err := cl.Call(context.Background(), "echo_bytes", params) 239 if err != nil { 240 return []byte{}, err 241 } 242 243 msg := <-cl.ResponsesCh 244 if msg.Error != nil { 245 return []byte{}, msg.Error 246 247 } 248 result := new(ResultEchoBytes) 249 err = json.Unmarshal(msg.Result, result) 250 if err != nil { 251 return []byte{}, nil 252 } 253 return result.Value, nil 254 } 255 256 func testWithWSClient(t *testing.T, cl *client.WSClient) { 257 val := testVal 258 got, err := echoViaWS(cl, val) 259 require.Nil(t, err) 260 assert.Equal(t, got, val) 261 262 val2 := randBytes(t) 263 got2, err := echoBytesViaWS(cl, val2) 264 require.Nil(t, err) 265 assert.Equal(t, got2, val2) 266 } 267 268 //------------- 269 270 func TestServersAndClientsBasic(t *testing.T) { 271 serverAddrs := [...]string{tcpAddr, unixAddr} 272 for _, addr := range serverAddrs { 273 cl1, err := client.NewURI(addr) 274 require.Nil(t, err) 275 fmt.Printf("=== testing server on %s using URI client", addr) 276 testWithHTTPClient(t, cl1) 277 278 cl2, err := client.New(addr) 279 require.Nil(t, err) 280 fmt.Printf("=== testing server on %s using JSONRPC client", addr) 281 testWithHTTPClient(t, cl2) 282 283 cl3, err := client.NewWS(addr, websocketEndpoint) 284 require.Nil(t, err) 285 cl3.SetLogger(log.TestingLogger()) 286 err = cl3.Start() 287 require.Nil(t, err) 288 fmt.Printf("=== testing server on %s using WS client", addr) 289 testWithWSClient(t, cl3) 290 err = cl3.Stop() 291 require.NoError(t, err) 292 } 293 } 294 295 func TestHexStringArg(t *testing.T) { 296 cl, err := client.NewURI(tcpAddr) 297 require.Nil(t, err) 298 // should NOT be handled as hex 299 val := "0xabc" 300 got, err := echoViaHTTP(cl, val) 301 require.Nil(t, err) 302 assert.Equal(t, got, val) 303 } 304 305 func TestQuotedStringArg(t *testing.T) { 306 cl, err := client.NewURI(tcpAddr) 307 require.Nil(t, err) 308 // should NOT be unquoted 309 val := "\"abc\"" 310 got, err := echoViaHTTP(cl, val) 311 require.Nil(t, err) 312 assert.Equal(t, got, val) 313 } 314 315 func TestWSNewWSRPCFunc(t *testing.T) { 316 cl, err := client.NewWS(tcpAddr, websocketEndpoint) 317 require.Nil(t, err) 318 cl.SetLogger(log.TestingLogger()) 319 err = cl.Start() 320 require.Nil(t, err) 321 t.Cleanup(func() { 322 if err := cl.Stop(); err != nil { 323 t.Error(err) 324 } 325 }) 326 327 val := testVal 328 params := map[string]interface{}{ 329 "arg": val, 330 } 331 err = cl.Call(context.Background(), "echo_ws", params) 332 require.Nil(t, err) 333 334 msg := <-cl.ResponsesCh 335 if msg.Error != nil { 336 t.Fatal(err) 337 } 338 result := new(ResultEcho) 339 err = json.Unmarshal(msg.Result, result) 340 require.Nil(t, err) 341 got := result.Value 342 assert.Equal(t, got, val) 343 } 344 345 func TestWSHandlesArrayParams(t *testing.T) { 346 cl, err := client.NewWS(tcpAddr, websocketEndpoint) 347 require.Nil(t, err) 348 cl.SetLogger(log.TestingLogger()) 349 err = cl.Start() 350 require.Nil(t, err) 351 t.Cleanup(func() { 352 if err := cl.Stop(); err != nil { 353 t.Error(err) 354 } 355 }) 356 357 val := testVal 358 params := []interface{}{val} 359 err = cl.CallWithArrayParams(context.Background(), "echo_ws", params) 360 require.Nil(t, err) 361 362 msg := <-cl.ResponsesCh 363 if msg.Error != nil { 364 t.Fatalf("%+v", err) 365 } 366 result := new(ResultEcho) 367 err = json.Unmarshal(msg.Result, result) 368 require.Nil(t, err) 369 got := result.Value 370 assert.Equal(t, got, val) 371 } 372 373 // TestWSClientPingPong checks that a client & server exchange pings 374 // & pongs so connection stays alive. 375 func TestWSClientPingPong(t *testing.T) { 376 cl, err := client.NewWS(tcpAddr, websocketEndpoint) 377 require.Nil(t, err) 378 cl.SetLogger(log.TestingLogger()) 379 err = cl.Start() 380 require.Nil(t, err) 381 t.Cleanup(func() { 382 if err := cl.Stop(); err != nil { 383 t.Error(err) 384 } 385 }) 386 387 time.Sleep(6 * time.Second) 388 } 389 390 func randBytes(t *testing.T) []byte { 391 n := tmrand.Intn(10) + 2 392 buf := make([]byte, n) 393 _, err := crand.Read(buf) 394 require.Nil(t, err) 395 return bytes.Replace(buf, []byte("="), []byte{100}, -1) 396 }