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