github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/network/rpc/client_test.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rpc 18 19 import ( 20 "context" 21 "fmt" 22 "math/rand" 23 "net" 24 "net/http" 25 "net/http/httptest" 26 "os" 27 "reflect" 28 "runtime" 29 "sync" 30 "testing" 31 "time" 32 33 "github.com/davecgh/go-spew/spew" 34 "github.com/neatlab/neatio/chain/log" 35 ) 36 37 func TestClientRequest(t *testing.T) { 38 server := newTestServer() 39 defer server.Stop() 40 client := DialInProc(server) 41 defer client.Close() 42 43 var resp Result 44 if err := client.Call(&resp, "test_echo", "hello", 10, &Args{"world"}); err != nil { 45 t.Fatal(err) 46 } 47 if !reflect.DeepEqual(resp, Result{"hello", 10, &Args{"world"}}) { 48 t.Errorf("incorrect result %#v", resp) 49 } 50 } 51 52 func TestClientErrorData(t *testing.T) { 53 server := newTestServer() 54 defer server.Stop() 55 client := DialInProc(server) 56 defer client.Close() 57 58 var resp interface{} 59 err := client.Call(&resp, "test_returnError") 60 if err == nil { 61 t.Fatal("expected error") 62 } 63 64 // Check code. 65 if e, ok := err.(Error); !ok { 66 t.Fatalf("client did not return rpc.Error, got %#v", e) 67 } else if e.ErrorCode() != (testError{}.ErrorCode()) { 68 t.Fatalf("wrong error code %d, want %d", e.ErrorCode(), testError{}.ErrorCode()) 69 } 70 // Check data. 71 if e, ok := err.(DataError); !ok { 72 t.Fatalf("client did not return rpc.DataError, got %#v", e) 73 } else if e.ErrorData() != (testError{}.ErrorData()) { 74 t.Fatalf("wrong error data %#v, want %#v", e.ErrorData(), testError{}.ErrorData()) 75 } 76 } 77 78 func TestClientBatchRequest(t *testing.T) { 79 server := newTestServer() 80 defer server.Stop() 81 client := DialInProc(server) 82 defer client.Close() 83 84 batch := []BatchElem{ 85 { 86 Method: "test_echo", 87 Args: []interface{}{"hello", 10, &Args{"world"}}, 88 Result: new(Result), 89 }, 90 { 91 Method: "test_echo", 92 Args: []interface{}{"hello2", 11, &Args{"world"}}, 93 Result: new(Result), 94 }, 95 { 96 Method: "no_such_method", 97 Args: []interface{}{1, 2, 3}, 98 Result: new(int), 99 }, 100 } 101 if err := client.BatchCall(batch); err != nil { 102 t.Fatal(err) 103 } 104 wantResult := []BatchElem{ 105 { 106 Method: "test_echo", 107 Args: []interface{}{"hello", 10, &Args{"world"}}, 108 Result: &Result{"hello", 10, &Args{"world"}}, 109 }, 110 { 111 Method: "test_echo", 112 Args: []interface{}{"hello2", 11, &Args{"world"}}, 113 Result: &Result{"hello2", 11, &Args{"world"}}, 114 }, 115 { 116 Method: "no_such_method", 117 Args: []interface{}{1, 2, 3}, 118 Result: new(int), 119 Error: &jsonError{Code: -32601, Message: "the method no_such_method does not exist/is not available"}, 120 }, 121 } 122 if !reflect.DeepEqual(batch, wantResult) { 123 t.Errorf("batch results mismatch:\ngot %swant %s", spew.Sdump(batch), spew.Sdump(wantResult)) 124 } 125 } 126 127 func TestClientNotify(t *testing.T) { 128 server := newTestServer() 129 defer server.Stop() 130 client := DialInProc(server) 131 defer client.Close() 132 133 if err := client.Notify(context.Background(), "test_echo", "hello", 10, &Args{"world"}); err != nil { 134 t.Fatal(err) 135 } 136 } 137 138 // func TestClientCancelInproc(t *testing.T) { testClientCancel("inproc", t) } 139 func TestClientCancelWebsocket(t *testing.T) { testClientCancel("ws", t) } 140 func TestClientCancelHTTP(t *testing.T) { testClientCancel("http", t) } 141 func TestClientCancelIPC(t *testing.T) { testClientCancel("ipc", t) } 142 143 // This test checks that requests made through CallContext can be canceled by canceling 144 // the context. 145 func testClientCancel(transport string, t *testing.T) { 146 // These tests take a lot of time, run them all at once. 147 // You probably want to run with -parallel 1 or comment out 148 // the call to t.Parallel if you enable the logging. 149 t.Parallel() 150 151 server := newTestServer() 152 defer server.Stop() 153 154 // What we want to achieve is that the context gets canceled 155 // at various stages of request processing. The interesting cases 156 // are: 157 // - cancel during dial 158 // - cancel while performing a HTTP request 159 // - cancel while waiting for a response 160 // 161 // To trigger those, the times are chosen such that connections 162 // are killed within the deadline for every other call (maxKillTimeout 163 // is 2x maxCancelTimeout). 164 // 165 // Once a connection is dead, there is a fair chance it won't connect 166 // successfully because the accept is delayed by 1s. 167 maxContextCancelTimeout := 300 * time.Millisecond 168 fl := &flakeyListener{ 169 maxAcceptDelay: 1 * time.Second, 170 maxKillTimeout: 600 * time.Millisecond, 171 } 172 173 var client *Client 174 switch transport { 175 case "ws", "http": 176 c, hs := httpTestClient(server, transport, fl) 177 defer hs.Close() 178 client = c 179 case "ipc": 180 c, l := ipcTestClient(server, fl) 181 defer l.Close() 182 client = c 183 default: 184 panic("unknown transport: " + transport) 185 } 186 187 // The actual test starts here. 188 var ( 189 wg sync.WaitGroup 190 nreqs = 10 191 ncallers = 6 192 ) 193 caller := func(index int) { 194 defer wg.Done() 195 for i := 0; i < nreqs; i++ { 196 var ( 197 ctx context.Context 198 cancel func() 199 timeout = time.Duration(rand.Int63n(int64(maxContextCancelTimeout))) 200 ) 201 if index < ncallers/2 { 202 // For half of the callers, create a context without deadline 203 // and cancel it later. 204 ctx, cancel = context.WithCancel(context.Background()) 205 time.AfterFunc(timeout, cancel) 206 } else { 207 // For the other half, create a context with a deadline instead. This is 208 // different because the context deadline is used to set the socket write 209 // deadline. 210 ctx, cancel = context.WithTimeout(context.Background(), timeout) 211 } 212 // Now perform a call with the context. 213 // The key thing here is that no call will ever complete successfully. 214 sleepTime := maxContextCancelTimeout + 20*time.Millisecond 215 err := client.CallContext(ctx, nil, "test_sleep", sleepTime) 216 if err != nil { 217 log.Debug(fmt.Sprint("got expected error:", err)) 218 } else { 219 t.Errorf("no error for call with %v wait time", timeout) 220 } 221 cancel() 222 } 223 } 224 wg.Add(ncallers) 225 for i := 0; i < ncallers; i++ { 226 go caller(i) 227 } 228 wg.Wait() 229 } 230 231 func TestClientSubscribeInvalidArg(t *testing.T) { 232 server := newTestServer() 233 defer server.Stop() 234 client := DialInProc(server) 235 defer client.Close() 236 237 check := func(shouldPanic bool, arg interface{}) { 238 defer func() { 239 err := recover() 240 if shouldPanic && err == nil { 241 t.Errorf("EthSubscribe should've panicked for %#v", arg) 242 } 243 if !shouldPanic && err != nil { 244 t.Errorf("EthSubscribe shouldn't have panicked for %#v", arg) 245 buf := make([]byte, 1024*1024) 246 buf = buf[:runtime.Stack(buf, false)] 247 t.Error(err) 248 t.Error(string(buf)) 249 } 250 }() 251 client.EthSubscribe(context.Background(), arg, "foo_bar") 252 } 253 check(true, nil) 254 check(true, 1) 255 check(true, (chan int)(nil)) 256 check(true, make(<-chan int)) 257 check(false, make(chan int)) 258 check(false, make(chan<- int)) 259 } 260 261 func TestClientSubscribe(t *testing.T) { 262 server := newTestServer() 263 defer server.Stop() 264 client := DialInProc(server) 265 defer client.Close() 266 267 nc := make(chan int) 268 count := 10 269 sub, err := client.Subscribe(context.Background(), "nftest", nc, "someSubscription", count, 0) 270 if err != nil { 271 t.Fatal("can't subscribe:", err) 272 } 273 for i := 0; i < count; i++ { 274 if val := <-nc; val != i { 275 t.Fatalf("value mismatch: got %d, want %d", val, i) 276 } 277 } 278 279 sub.Unsubscribe() 280 select { 281 case v := <-nc: 282 t.Fatal("received value after unsubscribe:", v) 283 case err := <-sub.Err(): 284 if err != nil { 285 t.Fatalf("Err returned a non-nil error after explicit unsubscribe: %q", err) 286 } 287 case <-time.After(1 * time.Second): 288 t.Fatalf("subscription not closed within 1s after unsubscribe") 289 } 290 } 291 292 // In this test, the connection drops while Subscribe is waiting for a response. 293 func TestClientSubscribeClose(t *testing.T) { 294 server := newTestServer() 295 service := ¬ificationTestService{ 296 gotHangSubscriptionReq: make(chan struct{}), 297 unblockHangSubscription: make(chan struct{}), 298 } 299 if err := server.RegisterName("nftest2", service); err != nil { 300 t.Fatal(err) 301 } 302 303 defer server.Stop() 304 client := DialInProc(server) 305 defer client.Close() 306 307 var ( 308 nc = make(chan int) 309 errc = make(chan error) 310 sub *ClientSubscription 311 err error 312 ) 313 go func() { 314 sub, err = client.Subscribe(context.Background(), "nftest2", nc, "hangSubscription", 999) 315 errc <- err 316 }() 317 318 <-service.gotHangSubscriptionReq 319 client.Close() 320 service.unblockHangSubscription <- struct{}{} 321 322 select { 323 case err := <-errc: 324 if err == nil { 325 t.Errorf("Subscribe returned nil error after Close") 326 } 327 if sub != nil { 328 t.Error("Subscribe returned non-nil subscription after Close") 329 } 330 case <-time.After(1 * time.Second): 331 t.Fatalf("Subscribe did not return within 1s after Close") 332 } 333 } 334 335 func TestClientCloseUnsubscribeRace(t *testing.T) { 336 server := newTestServer() 337 defer server.Stop() 338 339 for i := 0; i < 20; i++ { 340 client := DialInProc(server) 341 nc := make(chan int) 342 sub, err := client.Subscribe(context.Background(), "nftest", nc, "someSubscription", 3, 1) 343 if err != nil { 344 t.Fatal(err) 345 } 346 go client.Close() 347 go sub.Unsubscribe() 348 select { 349 case <-sub.Err(): 350 case <-time.After(5 * time.Second): 351 t.Fatal("subscription not closed within timeout") 352 } 353 } 354 } 355 356 // This test checks that Client doesn't lock up when a single subscriber 357 // doesn't read subscription events. 358 func TestClientNotificationStorm(t *testing.T) { 359 server := newTestServer() 360 defer server.Stop() 361 362 doTest := func(count int, wantError bool) { 363 client := DialInProc(server) 364 defer client.Close() 365 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 366 defer cancel() 367 368 // Subscribe on the server. It will start sending many notifications 369 // very quickly. 370 nc := make(chan int) 371 sub, err := client.Subscribe(ctx, "nftest", nc, "someSubscription", count, 0) 372 if err != nil { 373 t.Fatal("can't subscribe:", err) 374 } 375 defer sub.Unsubscribe() 376 377 // Process each notification, try to run a call in between each of them. 378 for i := 0; i < count; i++ { 379 select { 380 case val := <-nc: 381 if val != i { 382 t.Fatalf("(%d/%d) unexpected value %d", i, count, val) 383 } 384 case err := <-sub.Err(): 385 if wantError && err != ErrSubscriptionQueueOverflow { 386 t.Fatalf("(%d/%d) got error %q, want %q", i, count, err, ErrSubscriptionQueueOverflow) 387 } else if !wantError { 388 t.Fatalf("(%d/%d) got unexpected error %q", i, count, err) 389 } 390 return 391 } 392 var r int 393 err := client.CallContext(ctx, &r, "nftest_echo", i) 394 if err != nil { 395 if !wantError { 396 t.Fatalf("(%d/%d) call error: %v", i, count, err) 397 } 398 return 399 } 400 } 401 } 402 403 doTest(8000, false) 404 doTest(10000, true) 405 } 406 407 func TestClientHTTP(t *testing.T) { 408 server := newTestServer() 409 defer server.Stop() 410 411 client, hs := httpTestClient(server, "http", nil) 412 defer hs.Close() 413 defer client.Close() 414 415 // Launch concurrent requests. 416 var ( 417 results = make([]Result, 100) 418 errc = make(chan error) 419 wantResult = Result{"a", 1, new(Args)} 420 ) 421 defer client.Close() 422 for i := range results { 423 i := i 424 go func() { 425 errc <- client.Call(&results[i], "test_echo", 426 wantResult.String, wantResult.Int, wantResult.Args) 427 }() 428 } 429 430 // Wait for all of them to complete. 431 timeout := time.NewTimer(5 * time.Second) 432 defer timeout.Stop() 433 for i := range results { 434 select { 435 case err := <-errc: 436 if err != nil { 437 t.Fatal(err) 438 } 439 case <-timeout.C: 440 t.Fatalf("timeout (got %d/%d) results)", i+1, len(results)) 441 } 442 } 443 444 // Check results. 445 for i := range results { 446 if !reflect.DeepEqual(results[i], wantResult) { 447 t.Errorf("result %d mismatch: got %#v, want %#v", i, results[i], wantResult) 448 } 449 } 450 } 451 452 func TestClientReconnect(t *testing.T) { 453 startServer := func(addr string) (*Server, net.Listener) { 454 srv := newTestServer() 455 l, err := net.Listen("tcp", addr) 456 if err != nil { 457 t.Fatal("can't listen:", err) 458 } 459 go http.Serve(l, srv.WebsocketHandler([]string{"*"})) 460 return srv, l 461 } 462 463 ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second) 464 defer cancel() 465 466 // Start a server and corresponding client. 467 s1, l1 := startServer("127.0.0.1:0") 468 client, err := DialContext(ctx, "ws://"+l1.Addr().String()) 469 if err != nil { 470 t.Fatal("can't dial", err) 471 } 472 473 // Perform a call. This should work because the server is up. 474 var resp Result 475 if err := client.CallContext(ctx, &resp, "test_echo", "", 1, nil); err != nil { 476 t.Fatal(err) 477 } 478 479 // Shut down the server and allow for some cool down time so we can listen on the same 480 // address again. 481 l1.Close() 482 s1.Stop() 483 time.Sleep(2 * time.Second) 484 485 // Try calling again. It shouldn't work. 486 if err := client.CallContext(ctx, &resp, "test_echo", "", 2, nil); err == nil { 487 t.Error("successful call while the server is down") 488 t.Logf("resp: %#v", resp) 489 } 490 491 // Start it up again and call again. The connection should be reestablished. 492 // We spawn multiple calls here to check whether this hangs somehow. 493 s2, l2 := startServer(l1.Addr().String()) 494 defer l2.Close() 495 defer s2.Stop() 496 497 start := make(chan struct{}) 498 errors := make(chan error, 20) 499 for i := 0; i < cap(errors); i++ { 500 go func() { 501 <-start 502 var resp Result 503 errors <- client.CallContext(ctx, &resp, "test_echo", "", 3, nil) 504 }() 505 } 506 close(start) 507 errcount := 0 508 for i := 0; i < cap(errors); i++ { 509 if err = <-errors; err != nil { 510 errcount++ 511 } 512 } 513 t.Logf("%d errors, last error: %v", errcount, err) 514 if errcount > 1 { 515 t.Errorf("expected one error after disconnect, got %d", errcount) 516 } 517 } 518 519 func httpTestClient(srv *Server, transport string, fl *flakeyListener) (*Client, *httptest.Server) { 520 // Create the HTTP server. 521 var hs *httptest.Server 522 switch transport { 523 case "ws": 524 hs = httptest.NewUnstartedServer(srv.WebsocketHandler([]string{"*"})) 525 case "http": 526 hs = httptest.NewUnstartedServer(srv) 527 default: 528 panic("unknown HTTP transport: " + transport) 529 } 530 // Wrap the listener if required. 531 if fl != nil { 532 fl.Listener = hs.Listener 533 hs.Listener = fl 534 } 535 // Connect the client. 536 hs.Start() 537 client, err := Dial(transport + "://" + hs.Listener.Addr().String()) 538 if err != nil { 539 panic(err) 540 } 541 return client, hs 542 } 543 544 func ipcTestClient(srv *Server, fl *flakeyListener) (*Client, net.Listener) { 545 // Listen on a random endpoint. 546 endpoint := fmt.Sprintf("go-ethereum-test-ipc-%d-%d", os.Getpid(), rand.Int63()) 547 if runtime.GOOS == "windows" { 548 endpoint = `\\.\pipe\` + endpoint 549 } else { 550 endpoint = os.TempDir() + "/" + endpoint 551 } 552 l, err := ipcListen(endpoint) 553 if err != nil { 554 panic(err) 555 } 556 // Connect the listener to the server. 557 if fl != nil { 558 fl.Listener = l 559 l = fl 560 } 561 go srv.ServeListener(l) 562 // Connect the client. 563 client, err := Dial(endpoint) 564 if err != nil { 565 panic(err) 566 } 567 return client, l 568 } 569 570 // flakeyListener kills accepted connections after a random timeout. 571 type flakeyListener struct { 572 net.Listener 573 maxKillTimeout time.Duration 574 maxAcceptDelay time.Duration 575 } 576 577 func (l *flakeyListener) Accept() (net.Conn, error) { 578 delay := time.Duration(rand.Int63n(int64(l.maxAcceptDelay))) 579 time.Sleep(delay) 580 581 c, err := l.Listener.Accept() 582 if err == nil { 583 timeout := time.Duration(rand.Int63n(int64(l.maxKillTimeout))) 584 time.AfterFunc(timeout, func() { 585 log.Debug(fmt.Sprintf("killing conn %v after %v", c.LocalAddr(), timeout)) 586 c.Close() 587 }) 588 } 589 return c, err 590 }