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