github.com/neatio-net/neatio@v1.7.3-0.20231114194659-f4d7a2226baa/network/rpc/client_test.go (about) 1 package rpc 2 3 import ( 4 "context" 5 "fmt" 6 "math/rand" 7 "net" 8 "net/http" 9 "net/http/httptest" 10 "os" 11 "reflect" 12 "runtime" 13 "sync" 14 "testing" 15 "time" 16 17 "github.com/davecgh/go-spew/spew" 18 "github.com/neatio-net/neatio/chain/log" 19 ) 20 21 func TestClientRequest(t *testing.T) { 22 server := newTestServer() 23 defer server.Stop() 24 client := DialInProc(server) 25 defer client.Close() 26 27 var resp Result 28 if err := client.Call(&resp, "test_echo", "hello", 10, &Args{"world"}); err != nil { 29 t.Fatal(err) 30 } 31 if !reflect.DeepEqual(resp, Result{"hello", 10, &Args{"world"}}) { 32 t.Errorf("incorrect result %#v", resp) 33 } 34 } 35 36 func TestClientErrorData(t *testing.T) { 37 server := newTestServer() 38 defer server.Stop() 39 client := DialInProc(server) 40 defer client.Close() 41 42 var resp interface{} 43 err := client.Call(&resp, "test_returnError") 44 if err == nil { 45 t.Fatal("expected error") 46 } 47 48 if e, ok := err.(Error); !ok { 49 t.Fatalf("client did not return rpc.Error, got %#v", e) 50 } else if e.ErrorCode() != (testError{}.ErrorCode()) { 51 t.Fatalf("wrong error code %d, want %d", e.ErrorCode(), testError{}.ErrorCode()) 52 } 53 if e, ok := err.(DataError); !ok { 54 t.Fatalf("client did not return rpc.DataError, got %#v", e) 55 } else if e.ErrorData() != (testError{}.ErrorData()) { 56 t.Fatalf("wrong error data %#v, want %#v", e.ErrorData(), testError{}.ErrorData()) 57 } 58 } 59 60 func TestClientBatchRequest(t *testing.T) { 61 server := newTestServer() 62 defer server.Stop() 63 client := DialInProc(server) 64 defer client.Close() 65 66 batch := []BatchElem{ 67 { 68 Method: "test_echo", 69 Args: []interface{}{"hello", 10, &Args{"world"}}, 70 Result: new(Result), 71 }, 72 { 73 Method: "test_echo", 74 Args: []interface{}{"hello2", 11, &Args{"world"}}, 75 Result: new(Result), 76 }, 77 { 78 Method: "no_such_method", 79 Args: []interface{}{1, 2, 3}, 80 Result: new(int), 81 }, 82 } 83 if err := client.BatchCall(batch); err != nil { 84 t.Fatal(err) 85 } 86 wantResult := []BatchElem{ 87 { 88 Method: "test_echo", 89 Args: []interface{}{"hello", 10, &Args{"world"}}, 90 Result: &Result{"hello", 10, &Args{"world"}}, 91 }, 92 { 93 Method: "test_echo", 94 Args: []interface{}{"hello2", 11, &Args{"world"}}, 95 Result: &Result{"hello2", 11, &Args{"world"}}, 96 }, 97 { 98 Method: "no_such_method", 99 Args: []interface{}{1, 2, 3}, 100 Result: new(int), 101 Error: &jsonError{Code: -32601, Message: "the method no_such_method does not exist/is not available"}, 102 }, 103 } 104 if !reflect.DeepEqual(batch, wantResult) { 105 t.Errorf("batch results mismatch:\ngot %swant %s", spew.Sdump(batch), spew.Sdump(wantResult)) 106 } 107 } 108 109 func TestClientNotify(t *testing.T) { 110 server := newTestServer() 111 defer server.Stop() 112 client := DialInProc(server) 113 defer client.Close() 114 115 if err := client.Notify(context.Background(), "test_echo", "hello", 10, &Args{"world"}); err != nil { 116 t.Fatal(err) 117 } 118 } 119 120 func TestClientCancelWebsocket(t *testing.T) { testClientCancel("ws", t) } 121 func TestClientCancelHTTP(t *testing.T) { testClientCancel("http", t) } 122 func TestClientCancelIPC(t *testing.T) { testClientCancel("ipc", t) } 123 124 func testClientCancel(transport string, t *testing.T) { 125 t.Parallel() 126 127 server := newTestServer() 128 defer server.Stop() 129 130 maxContextCancelTimeout := 300 * time.Millisecond 131 fl := &flakeyListener{ 132 maxAcceptDelay: 1 * time.Second, 133 maxKillTimeout: 600 * time.Millisecond, 134 } 135 136 var client *Client 137 switch transport { 138 case "ws", "http": 139 c, hs := httpTestClient(server, transport, fl) 140 defer hs.Close() 141 client = c 142 case "ipc": 143 c, l := ipcTestClient(server, fl) 144 defer l.Close() 145 client = c 146 default: 147 panic("unknown transport: " + transport) 148 } 149 150 var ( 151 wg sync.WaitGroup 152 nreqs = 10 153 ncallers = 6 154 ) 155 caller := func(index int) { 156 defer wg.Done() 157 for i := 0; i < nreqs; i++ { 158 var ( 159 ctx context.Context 160 cancel func() 161 timeout = time.Duration(rand.Int63n(int64(maxContextCancelTimeout))) 162 ) 163 if index < ncallers/2 { 164 ctx, cancel = context.WithCancel(context.Background()) 165 time.AfterFunc(timeout, cancel) 166 } else { 167 ctx, cancel = context.WithTimeout(context.Background(), timeout) 168 } 169 sleepTime := maxContextCancelTimeout + 20*time.Millisecond 170 err := client.CallContext(ctx, nil, "test_sleep", sleepTime) 171 if err != nil { 172 log.Debug(fmt.Sprint("got expected error:", err)) 173 } else { 174 t.Errorf("no error for call with %v wait time", timeout) 175 } 176 cancel() 177 } 178 } 179 wg.Add(ncallers) 180 for i := 0; i < ncallers; i++ { 181 go caller(i) 182 } 183 wg.Wait() 184 } 185 186 func TestClientSubscribeInvalidArg(t *testing.T) { 187 server := newTestServer() 188 defer server.Stop() 189 client := DialInProc(server) 190 defer client.Close() 191 192 check := func(shouldPanic bool, arg interface{}) { 193 defer func() { 194 err := recover() 195 if shouldPanic && err == nil { 196 t.Errorf("EthSubscribe should've panicked for %#v", arg) 197 } 198 if !shouldPanic && err != nil { 199 t.Errorf("EthSubscribe shouldn't have panicked for %#v", arg) 200 buf := make([]byte, 1024*1024) 201 buf = buf[:runtime.Stack(buf, false)] 202 t.Error(err) 203 t.Error(string(buf)) 204 } 205 }() 206 client.EthSubscribe(context.Background(), arg, "foo_bar") 207 } 208 check(true, nil) 209 check(true, 1) 210 check(true, (chan int)(nil)) 211 check(true, make(<-chan int)) 212 check(false, make(chan int)) 213 check(false, make(chan<- int)) 214 } 215 216 func TestClientSubscribe(t *testing.T) { 217 server := newTestServer() 218 defer server.Stop() 219 client := DialInProc(server) 220 defer client.Close() 221 222 nc := make(chan int) 223 count := 10 224 sub, err := client.Subscribe(context.Background(), "nftest", nc, "someSubscription", count, 0) 225 if err != nil { 226 t.Fatal("can't subscribe:", err) 227 } 228 for i := 0; i < count; i++ { 229 if val := <-nc; val != i { 230 t.Fatalf("value mismatch: got %d, want %d", val, i) 231 } 232 } 233 234 sub.Unsubscribe() 235 select { 236 case v := <-nc: 237 t.Fatal("received value after unsubscribe:", v) 238 case err := <-sub.Err(): 239 if err != nil { 240 t.Fatalf("Err returned a non-nil error after explicit unsubscribe: %q", err) 241 } 242 case <-time.After(1 * time.Second): 243 t.Fatalf("subscription not closed within 1s after unsubscribe") 244 } 245 } 246 247 func TestClientSubscribeClose(t *testing.T) { 248 server := newTestServer() 249 service := ¬ificationTestService{ 250 gotHangSubscriptionReq: make(chan struct{}), 251 unblockHangSubscription: make(chan struct{}), 252 } 253 if err := server.RegisterName("nftest2", service); err != nil { 254 t.Fatal(err) 255 } 256 257 defer server.Stop() 258 client := DialInProc(server) 259 defer client.Close() 260 261 var ( 262 nc = make(chan int) 263 errc = make(chan error) 264 sub *ClientSubscription 265 err error 266 ) 267 go func() { 268 sub, err = client.Subscribe(context.Background(), "nftest2", nc, "hangSubscription", 999) 269 errc <- err 270 }() 271 272 <-service.gotHangSubscriptionReq 273 client.Close() 274 service.unblockHangSubscription <- struct{}{} 275 276 select { 277 case err := <-errc: 278 if err == nil { 279 t.Errorf("Subscribe returned nil error after Close") 280 } 281 if sub != nil { 282 t.Error("Subscribe returned non-nil subscription after Close") 283 } 284 case <-time.After(1 * time.Second): 285 t.Fatalf("Subscribe did not return within 1s after Close") 286 } 287 } 288 289 func TestClientCloseUnsubscribeRace(t *testing.T) { 290 server := newTestServer() 291 defer server.Stop() 292 293 for i := 0; i < 20; i++ { 294 client := DialInProc(server) 295 nc := make(chan int) 296 sub, err := client.Subscribe(context.Background(), "nftest", nc, "someSubscription", 3, 1) 297 if err != nil { 298 t.Fatal(err) 299 } 300 go client.Close() 301 go sub.Unsubscribe() 302 select { 303 case <-sub.Err(): 304 case <-time.After(5 * time.Second): 305 t.Fatal("subscription not closed within timeout") 306 } 307 } 308 } 309 310 func TestClientNotificationStorm(t *testing.T) { 311 server := newTestServer() 312 defer server.Stop() 313 314 doTest := func(count int, wantError bool) { 315 client := DialInProc(server) 316 defer client.Close() 317 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 318 defer cancel() 319 320 nc := make(chan int) 321 sub, err := client.Subscribe(ctx, "nftest", nc, "someSubscription", count, 0) 322 if err != nil { 323 t.Fatal("can't subscribe:", err) 324 } 325 defer sub.Unsubscribe() 326 327 for i := 0; i < count; i++ { 328 select { 329 case val := <-nc: 330 if val != i { 331 t.Fatalf("(%d/%d) unexpected value %d", i, count, val) 332 } 333 case err := <-sub.Err(): 334 if wantError && err != ErrSubscriptionQueueOverflow { 335 t.Fatalf("(%d/%d) got error %q, want %q", i, count, err, ErrSubscriptionQueueOverflow) 336 } else if !wantError { 337 t.Fatalf("(%d/%d) got unexpected error %q", i, count, err) 338 } 339 return 340 } 341 var r int 342 err := client.CallContext(ctx, &r, "nftest_echo", i) 343 if err != nil { 344 if !wantError { 345 t.Fatalf("(%d/%d) call error: %v", i, count, err) 346 } 347 return 348 } 349 } 350 } 351 352 doTest(8000, false) 353 doTest(10000, true) 354 } 355 356 func TestClientHTTP(t *testing.T) { 357 server := newTestServer() 358 defer server.Stop() 359 360 client, hs := httpTestClient(server, "http", nil) 361 defer hs.Close() 362 defer client.Close() 363 364 var ( 365 results = make([]Result, 100) 366 errc = make(chan error) 367 wantResult = Result{"a", 1, new(Args)} 368 ) 369 defer client.Close() 370 for i := range results { 371 i := i 372 go func() { 373 errc <- client.Call(&results[i], "test_echo", 374 wantResult.String, wantResult.Int, wantResult.Args) 375 }() 376 } 377 378 timeout := time.NewTimer(5 * time.Second) 379 defer timeout.Stop() 380 for i := range results { 381 select { 382 case err := <-errc: 383 if err != nil { 384 t.Fatal(err) 385 } 386 case <-timeout.C: 387 t.Fatalf("timeout (got %d/%d) results)", i+1, len(results)) 388 } 389 } 390 391 for i := range results { 392 if !reflect.DeepEqual(results[i], wantResult) { 393 t.Errorf("result %d mismatch: got %#v, want %#v", i, results[i], wantResult) 394 } 395 } 396 } 397 398 func TestClientReconnect(t *testing.T) { 399 startServer := func(addr string) (*Server, net.Listener) { 400 srv := newTestServer() 401 l, err := net.Listen("tcp", addr) 402 if err != nil { 403 t.Fatal("can't listen:", err) 404 } 405 go http.Serve(l, srv.WebsocketHandler([]string{"*"})) 406 return srv, l 407 } 408 409 ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second) 410 defer cancel() 411 412 s1, l1 := startServer("127.0.0.1:0") 413 client, err := DialContext(ctx, "ws://"+l1.Addr().String()) 414 if err != nil { 415 t.Fatal("can't dial", err) 416 } 417 418 var resp Result 419 if err := client.CallContext(ctx, &resp, "test_echo", "", 1, nil); err != nil { 420 t.Fatal(err) 421 } 422 423 l1.Close() 424 s1.Stop() 425 time.Sleep(2 * time.Second) 426 427 if err := client.CallContext(ctx, &resp, "test_echo", "", 2, nil); err == nil { 428 t.Error("successful call while the server is down") 429 t.Logf("resp: %#v", resp) 430 } 431 432 s2, l2 := startServer(l1.Addr().String()) 433 defer l2.Close() 434 defer s2.Stop() 435 436 start := make(chan struct{}) 437 errors := make(chan error, 20) 438 for i := 0; i < cap(errors); i++ { 439 go func() { 440 <-start 441 var resp Result 442 errors <- client.CallContext(ctx, &resp, "test_echo", "", 3, nil) 443 }() 444 } 445 close(start) 446 errcount := 0 447 for i := 0; i < cap(errors); i++ { 448 if err = <-errors; err != nil { 449 errcount++ 450 } 451 } 452 t.Logf("%d errors, last error: %v", errcount, err) 453 if errcount > 1 { 454 t.Errorf("expected one error after disconnect, got %d", errcount) 455 } 456 } 457 458 func httpTestClient(srv *Server, transport string, fl *flakeyListener) (*Client, *httptest.Server) { 459 var hs *httptest.Server 460 switch transport { 461 case "ws": 462 hs = httptest.NewUnstartedServer(srv.WebsocketHandler([]string{"*"})) 463 case "http": 464 hs = httptest.NewUnstartedServer(srv) 465 default: 466 panic("unknown HTTP transport: " + transport) 467 } 468 if fl != nil { 469 fl.Listener = hs.Listener 470 hs.Listener = fl 471 } 472 hs.Start() 473 client, err := Dial(transport + "://" + hs.Listener.Addr().String()) 474 if err != nil { 475 panic(err) 476 } 477 return client, hs 478 } 479 480 func ipcTestClient(srv *Server, fl *flakeyListener) (*Client, net.Listener) { 481 endpoint := fmt.Sprintf("go-ethereum-test-ipc-%d-%d", os.Getpid(), rand.Int63()) 482 if runtime.GOOS == "windows" { 483 endpoint = `\\.\pipe\` + endpoint 484 } else { 485 endpoint = os.TempDir() + "/" + endpoint 486 } 487 l, err := ipcListen(endpoint) 488 if err != nil { 489 panic(err) 490 } 491 if fl != nil { 492 fl.Listener = l 493 l = fl 494 } 495 go srv.ServeListener(l) 496 client, err := Dial(endpoint) 497 if err != nil { 498 panic(err) 499 } 500 return client, l 501 } 502 503 type flakeyListener struct { 504 net.Listener 505 maxKillTimeout time.Duration 506 maxAcceptDelay time.Duration 507 } 508 509 func (l *flakeyListener) Accept() (net.Conn, error) { 510 delay := time.Duration(rand.Int63n(int64(l.maxAcceptDelay))) 511 time.Sleep(delay) 512 513 c, err := l.Listener.Accept() 514 if err == nil { 515 timeout := time.Duration(rand.Int63n(int64(l.maxKillTimeout))) 516 time.AfterFunc(timeout, func() { 517 log.Debug(fmt.Sprintf("killing conn %v after %v", c.LocalAddr(), timeout)) 518 c.Close() 519 }) 520 } 521 return c, err 522 }