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