github.com/MetalBlockchain/subnet-evm@v0.4.9/rpc/client_test.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2016 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 package rpc 28 29 import ( 30 "context" 31 "encoding/json" 32 "errors" 33 "fmt" 34 "math/rand" 35 "net" 36 "net/http" 37 "net/http/httptest" 38 "reflect" 39 "runtime" 40 "strings" 41 "testing" 42 "time" 43 44 "github.com/davecgh/go-spew/spew" 45 "github.com/ethereum/go-ethereum/log" 46 ) 47 48 func TestClientRequest(t *testing.T) { 49 server := newTestServer() 50 defer server.Stop() 51 client := DialInProc(server) 52 defer client.Close() 53 54 var resp echoResult 55 if err := client.Call(&resp, "test_echo", "hello", 10, &echoArgs{"world"}); err != nil { 56 t.Fatal(err) 57 } 58 if !reflect.DeepEqual(resp, echoResult{"hello", 10, &echoArgs{"world"}}) { 59 t.Errorf("incorrect result %#v", resp) 60 } 61 } 62 63 func TestClientResponseType(t *testing.T) { 64 server := newTestServer() 65 defer server.Stop() 66 client := DialInProc(server) 67 defer client.Close() 68 69 if err := client.Call(nil, "test_echo", "hello", 10, &echoArgs{"world"}); err != nil { 70 t.Errorf("Passing nil as result should be fine, but got an error: %v", err) 71 } 72 var resultVar echoResult 73 // Note: passing the var, not a ref 74 err := client.Call(resultVar, "test_echo", "hello", 10, &echoArgs{"world"}) 75 if err == nil { 76 t.Error("Passing a var as result should be an error") 77 } 78 } 79 80 // This test checks that server-returned errors with code and data come out of Client.Call. 81 func TestClientErrorData(t *testing.T) { 82 server := newTestServer() 83 defer server.Stop() 84 client := DialInProc(server) 85 defer client.Close() 86 87 var resp interface{} 88 err := client.Call(&resp, "test_returnError") 89 if err == nil { 90 t.Fatal("expected error") 91 } 92 93 // Check code. 94 if e, ok := err.(Error); !ok { 95 t.Fatalf("client did not return rpc.Error, got %#v", e) 96 } else if e.ErrorCode() != (testError{}.ErrorCode()) { 97 t.Fatalf("wrong error code %d, want %d", e.ErrorCode(), testError{}.ErrorCode()) 98 } 99 // Check data. 100 if e, ok := err.(DataError); !ok { 101 t.Fatalf("client did not return rpc.DataError, got %#v", e) 102 } else if e.ErrorData() != (testError{}.ErrorData()) { 103 t.Fatalf("wrong error data %#v, want %#v", e.ErrorData(), testError{}.ErrorData()) 104 } 105 } 106 107 func TestClientBatchRequest(t *testing.T) { 108 server := newTestServer() 109 defer server.Stop() 110 client := DialInProc(server) 111 defer client.Close() 112 113 batch := []BatchElem{ 114 { 115 Method: "test_echo", 116 Args: []interface{}{"hello", 10, &echoArgs{"world"}}, 117 Result: new(echoResult), 118 }, 119 { 120 Method: "test_echo", 121 Args: []interface{}{"hello2", 11, &echoArgs{"world"}}, 122 Result: new(echoResult), 123 }, 124 { 125 Method: "no_such_method", 126 Args: []interface{}{1, 2, 3}, 127 Result: new(int), 128 }, 129 } 130 if err := client.BatchCall(batch); err != nil { 131 t.Fatal(err) 132 } 133 wantResult := []BatchElem{ 134 { 135 Method: "test_echo", 136 Args: []interface{}{"hello", 10, &echoArgs{"world"}}, 137 Result: &echoResult{"hello", 10, &echoArgs{"world"}}, 138 }, 139 { 140 Method: "test_echo", 141 Args: []interface{}{"hello2", 11, &echoArgs{"world"}}, 142 Result: &echoResult{"hello2", 11, &echoArgs{"world"}}, 143 }, 144 { 145 Method: "no_such_method", 146 Args: []interface{}{1, 2, 3}, 147 Result: new(int), 148 Error: &jsonError{Code: -32601, Message: "the method no_such_method does not exist/is not available"}, 149 }, 150 } 151 if !reflect.DeepEqual(batch, wantResult) { 152 t.Errorf("batch results mismatch:\ngot %swant %s", spew.Sdump(batch), spew.Sdump(wantResult)) 153 } 154 } 155 156 func TestClientBatchRequest_len(t *testing.T) { 157 b, err := json.Marshal([]jsonrpcMessage{ 158 {Version: "2.0", ID: json.RawMessage("1"), Method: "foo", Result: json.RawMessage(`"0x1"`)}, 159 {Version: "2.0", ID: json.RawMessage("2"), Method: "bar", Result: json.RawMessage(`"0x2"`)}, 160 }) 161 if err != nil { 162 t.Fatal("failed to encode jsonrpc message:", err) 163 } 164 s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { 165 _, err := rw.Write(b) 166 if err != nil { 167 t.Error("failed to write response:", err) 168 } 169 })) 170 t.Cleanup(s.Close) 171 172 client, err := Dial(s.URL) 173 if err != nil { 174 t.Fatal("failed to dial test server:", err) 175 } 176 defer client.Close() 177 178 t.Run("too-few", func(t *testing.T) { 179 batch := []BatchElem{ 180 {Method: "foo"}, 181 {Method: "bar"}, 182 {Method: "baz"}, 183 } 184 ctx, cancelFn := context.WithTimeout(context.Background(), time.Second) 185 defer cancelFn() 186 if err := client.BatchCallContext(ctx, batch); !errors.Is(err, ErrBadResult) { 187 t.Errorf("expected %q but got: %v", ErrBadResult, err) 188 } 189 }) 190 191 t.Run("too-many", func(t *testing.T) { 192 batch := []BatchElem{ 193 {Method: "foo"}, 194 } 195 ctx, cancelFn := context.WithTimeout(context.Background(), time.Second) 196 defer cancelFn() 197 if err := client.BatchCallContext(ctx, batch); !errors.Is(err, ErrBadResult) { 198 t.Errorf("expected %q but got: %v", ErrBadResult, err) 199 } 200 }) 201 } 202 203 func TestClientNotify(t *testing.T) { 204 server := newTestServer() 205 defer server.Stop() 206 client := DialInProc(server) 207 defer client.Close() 208 209 if err := client.Notify(context.Background(), "test_echo", "hello", 10, &echoArgs{"world"}); err != nil { 210 t.Fatal(err) 211 } 212 } 213 214 // func TestClientCancelInproc(t *testing.T) { testClientCancel("inproc", t) } 215 func TestClientCancelWebsocket(t *testing.T) { testClientCancel("ws", t) } 216 func TestClientCancelHTTP(t *testing.T) { testClientCancel("http", t) } 217 218 // func TestClientCancelIPC(t *testing.T) { testClientCancel("ipc", t) } 219 220 // This test checks that requests made through CallContext can be canceled by canceling 221 // the context. 222 func testClientCancel(transport string, t *testing.T) { 223 // These tests take a lot of time, run them all at once. 224 // You probably want to run with -parallel 1 or comment out 225 // the call to t.Parallel if you enable the logging. 226 t.Parallel() 227 228 server := newTestServer() 229 defer server.Stop() 230 } 231 232 // func TestClientCancelIPC(t *testing.T) { testClientCancel("ipc", t) } 233 234 // // This test checks that requests made through CallContext can be canceled by canceling 235 // // the context. 236 // func testClientCancel(transport string, t *testing.T) { 237 // // These tests take a lot of time, run them all at once. 238 // // You probably want to run with -parallel 1 or comment out 239 // // the call to t.Parallel if you enable the logging. 240 // t.Parallel() 241 242 // server := newTestServer() 243 // defer server.Stop() 244 245 // // What we want to achieve is that the context gets canceled 246 // // at various stages of request processing. The interesting cases 247 // // are: 248 // // - cancel during dial 249 // // - cancel while performing a HTTP request 250 // // - cancel while waiting for a response 251 // // 252 // // To trigger those, the times are chosen such that connections 253 // // are killed within the deadline for every other call (maxKillTimeout 254 // // is 2x maxCancelTimeout). 255 // // 256 // // Once a connection is dead, there is a fair chance it won't connect 257 // // successfully because the accept is delayed by 1s. 258 // maxContextCancelTimeout := 300 * time.Millisecond 259 // fl := &flakeyListener{ 260 // maxAcceptDelay: 1 * time.Second, 261 // maxKillTimeout: 600 * time.Millisecond, 262 // } 263 264 // var client *Client 265 // switch transport { 266 // case "ws", "http": 267 // c, hs := httpTestClient(server, transport, fl) 268 // defer hs.Close() 269 // client = c 270 // case "ipc": 271 // c, l := ipcTestClient(server, fl) 272 // defer l.Close() 273 // client = c 274 // default: 275 // panic("unknown transport: " + transport) 276 // } 277 278 // // The actual test starts here. 279 // var ( 280 // wg sync.WaitGroup 281 // nreqs = 10 282 // ncallers = 10 283 // ) 284 // caller := func(index int) { 285 // defer wg.Done() 286 // for i := 0; i < nreqs; i++ { 287 // var ( 288 // ctx context.Context 289 // cancel func() 290 // timeout = time.Duration(rand.Int63n(int64(maxContextCancelTimeout))) 291 // ) 292 // if index < ncallers/2 { 293 // // For half of the callers, create a context without deadline 294 // // and cancel it later. 295 // ctx, cancel = context.WithCancel(context.Background()) 296 // time.AfterFunc(timeout, cancel) 297 // } else { 298 // // For the other half, create a context with a deadline instead. This is 299 // // different because the context deadline is used to set the socket write 300 // // deadline. 301 // ctx, cancel = context.WithTimeout(context.Background(), timeout) 302 // } 303 304 // // Now perform a call with the context. 305 // // The key thing here is that no call will ever complete successfully. 306 // err := client.CallContext(ctx, nil, "test_block") 307 // switch { 308 // case err == nil: 309 // _, hasDeadline := ctx.Deadline() 310 // t.Errorf("no error for call with %v wait time (deadline: %v)", timeout, hasDeadline) 311 // // default: 312 // // t.Logf("got expected error with %v wait time: %v", timeout, err) 313 // } 314 // cancel() 315 // } 316 // } 317 // wg.Add(ncallers) 318 // for i := 0; i < ncallers; i++ { 319 // go caller(i) 320 // } 321 // wg.Wait() 322 // } 323 324 func TestClientSubscribeInvalidArg(t *testing.T) { 325 server := newTestServer() 326 defer server.Stop() 327 client := DialInProc(server) 328 defer client.Close() 329 330 check := func(shouldPanic bool, arg interface{}) { 331 defer func() { 332 err := recover() 333 if shouldPanic && err == nil { 334 t.Errorf("EthSubscribe should've panicked for %#v", arg) 335 } 336 if !shouldPanic && err != nil { 337 t.Errorf("EthSubscribe shouldn't have panicked for %#v", arg) 338 buf := make([]byte, 1024*1024) 339 buf = buf[:runtime.Stack(buf, false)] 340 t.Error(err) 341 t.Error(string(buf)) 342 } 343 }() 344 client.EthSubscribe(context.Background(), arg, "foo_bar") 345 } 346 check(true, nil) 347 check(true, 1) 348 check(true, (chan int)(nil)) 349 check(true, make(<-chan int)) 350 check(false, make(chan int)) 351 check(false, make(chan<- int)) 352 } 353 354 func TestClientSubscribe(t *testing.T) { 355 server := newTestServer() 356 defer server.Stop() 357 client := DialInProc(server) 358 defer client.Close() 359 360 nc := make(chan int) 361 count := 10 362 sub, err := client.Subscribe(context.Background(), "nftest", nc, "someSubscription", count, 0) 363 if err != nil { 364 t.Fatal("can't subscribe:", err) 365 } 366 for i := 0; i < count; i++ { 367 if val := <-nc; val != i { 368 t.Fatalf("value mismatch: got %d, want %d", val, i) 369 } 370 } 371 372 sub.Unsubscribe() 373 select { 374 case v := <-nc: 375 t.Fatal("received value after unsubscribe:", v) 376 case err := <-sub.Err(): 377 if err != nil { 378 t.Fatalf("Err returned a non-nil error after explicit unsubscribe: %q", err) 379 } 380 case <-time.After(1 * time.Second): 381 t.Fatalf("subscription not closed within 1s after unsubscribe") 382 } 383 } 384 385 // In this test, the connection drops while Subscribe is waiting for a response. 386 func TestClientSubscribeClose(t *testing.T) { 387 server := newTestServer() 388 service := ¬ificationTestService{ 389 gotHangSubscriptionReq: make(chan struct{}), 390 unblockHangSubscription: make(chan struct{}), 391 } 392 if err := server.RegisterName("nftest2", service); err != nil { 393 t.Fatal(err) 394 } 395 396 defer server.Stop() 397 client := DialInProc(server) 398 defer client.Close() 399 400 var ( 401 nc = make(chan int) 402 errc = make(chan error, 1) 403 sub *ClientSubscription 404 err error 405 ) 406 go func() { 407 sub, err = client.Subscribe(context.Background(), "nftest2", nc, "hangSubscription", 999) 408 errc <- err 409 }() 410 411 <-service.gotHangSubscriptionReq 412 client.Close() 413 service.unblockHangSubscription <- struct{}{} 414 415 select { 416 case err := <-errc: 417 if err == nil { 418 t.Errorf("Subscribe returned nil error after Close") 419 } 420 if sub != nil { 421 t.Error("Subscribe returned non-nil subscription after Close") 422 } 423 case <-time.After(1 * time.Second): 424 t.Fatalf("Subscribe did not return within 1s after Close") 425 } 426 } 427 428 // This test reproduces https://github.com/ethereum/go-ethereum/issues/17837 where the 429 // client hangs during shutdown when Unsubscribe races with Client.Close. 430 func TestClientCloseUnsubscribeRace(t *testing.T) { 431 server := newTestServer() 432 defer server.Stop() 433 434 for i := 0; i < 20; i++ { 435 client := DialInProc(server) 436 nc := make(chan int) 437 sub, err := client.Subscribe(context.Background(), "nftest", nc, "someSubscription", 3, 1) 438 if err != nil { 439 t.Fatal(err) 440 } 441 go client.Close() 442 go sub.Unsubscribe() 443 select { 444 case <-sub.Err(): 445 case <-time.After(5 * time.Second): 446 t.Fatal("subscription not closed within timeout") 447 } 448 } 449 } 450 451 // unsubscribeRecorder collects the subscription IDs of *_unsubscribe calls. 452 type unsubscribeRecorder struct { 453 ServerCodec 454 unsubscribes map[string]bool 455 } 456 457 func (r *unsubscribeRecorder) readBatch() ([]*jsonrpcMessage, bool, error) { 458 if r.unsubscribes == nil { 459 r.unsubscribes = make(map[string]bool) 460 } 461 462 msgs, batch, err := r.ServerCodec.readBatch() 463 for _, msg := range msgs { 464 if msg.isUnsubscribe() { 465 var params []string 466 if err := json.Unmarshal(msg.Params, ¶ms); err != nil { 467 panic("unsubscribe decode error: " + err.Error()) 468 } 469 r.unsubscribes[params[0]] = true 470 } 471 } 472 return msgs, batch, err 473 } 474 475 // This checks that Client calls the _unsubscribe method on the server when Unsubscribe is 476 // called on a subscription. 477 func TestClientSubscriptionUnsubscribeServer(t *testing.T) { 478 t.Parallel() 479 480 // Create the server. 481 srv := NewServer(0) 482 srv.RegisterName("nftest", new(notificationTestService)) 483 p1, p2 := net.Pipe() 484 recorder := &unsubscribeRecorder{ServerCodec: NewCodec(p1)} 485 go srv.ServeCodec(recorder, OptionMethodInvocation|OptionSubscriptions, 0, 0, 0) 486 defer srv.Stop() 487 488 // Create the client on the other end of the pipe. 489 client, _ := newClient(context.Background(), func(context.Context) (ServerCodec, error) { 490 return NewCodec(p2), nil 491 }) 492 defer client.Close() 493 494 // Create the subscription. 495 ch := make(chan int) 496 sub, err := client.Subscribe(context.Background(), "nftest", ch, "someSubscription", 1, 1) 497 if err != nil { 498 t.Fatal(err) 499 } 500 501 // Unsubscribe and check that unsubscribe was called. 502 sub.Unsubscribe() 503 if !recorder.unsubscribes[sub.subid] { 504 t.Fatal("client did not call unsubscribe method") 505 } 506 if _, open := <-sub.Err(); open { 507 t.Fatal("subscription error channel not closed after unsubscribe") 508 } 509 } 510 511 // This checks that the subscribed channel can be closed after Unsubscribe. 512 // It is the reproducer for https://github.com/ethereum/go-ethereum/issues/22322 513 func TestClientSubscriptionChannelClose(t *testing.T) { 514 t.Parallel() 515 516 var ( 517 srv = NewServer(0) 518 httpsrv = httptest.NewServer(srv.WebsocketHandler(nil)) 519 wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") 520 ) 521 defer srv.Stop() 522 defer httpsrv.Close() 523 524 srv.RegisterName("nftest", new(notificationTestService)) 525 client, _ := Dial(wsURL) 526 527 for i := 0; i < 5; i++ { 528 ch := make(chan int, 100) 529 sub, err := client.Subscribe(context.Background(), "nftest", ch, "someSubscription", maxClientSubscriptionBuffer-1, 1) 530 if err != nil { 531 t.Fatal(err) 532 } 533 sub.Unsubscribe() 534 close(ch) 535 } 536 } 537 538 // This test checks that Client doesn't lock up when a single subscriber 539 // doesn't read subscription events. 540 func TestClientNotificationStorm(t *testing.T) { 541 server := newTestServer() 542 defer server.Stop() 543 544 doTest := func(count int, wantError bool) { 545 client := DialInProc(server) 546 defer client.Close() 547 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) 548 defer cancel() 549 550 // Subscribe on the server. It will start sending many notifications 551 // very quickly. 552 nc := make(chan int) 553 sub, err := client.Subscribe(ctx, "nftest", nc, "someSubscription", count, 0) 554 if err != nil { 555 t.Fatal("can't subscribe:", err) 556 } 557 defer sub.Unsubscribe() 558 559 // Process each notification, try to run a call in between each of them. 560 for i := 0; i < count; i++ { 561 select { 562 case val := <-nc: 563 if val != i { 564 t.Fatalf("(%d/%d) unexpected value %d", i, count, val) 565 } 566 case err := <-sub.Err(): 567 if wantError && err != ErrSubscriptionQueueOverflow { 568 t.Fatalf("(%d/%d) got error %q, want %q", i, count, err, ErrSubscriptionQueueOverflow) 569 } else if !wantError { 570 t.Fatalf("(%d/%d) got unexpected error %q", i, count, err) 571 } 572 return 573 } 574 var r int 575 err := client.CallContext(ctx, &r, "nftest_echo", i) 576 if err != nil { 577 if !wantError { 578 t.Fatalf("(%d/%d) call error: %v", i, count, err) 579 } 580 return 581 } 582 } 583 if wantError { 584 t.Fatalf("didn't get expected error") 585 } 586 } 587 588 doTest(8000, false) 589 doTest(100000, true) 590 } 591 592 func TestClientSetHeader(t *testing.T) { 593 var gotHeader bool 594 srv := newTestServer() 595 httpsrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 596 if r.Header.Get("test") == "ok" { 597 gotHeader = true 598 } 599 srv.ServeHTTP(w, r) 600 })) 601 defer httpsrv.Close() 602 defer srv.Stop() 603 604 client, err := Dial(httpsrv.URL) 605 if err != nil { 606 t.Fatal(err) 607 } 608 defer client.Close() 609 610 client.SetHeader("test", "ok") 611 if _, err := client.SupportedModules(); err != nil { 612 t.Fatal(err) 613 } 614 if !gotHeader { 615 t.Fatal("client did not set custom header") 616 } 617 618 // Check that Content-Type can be replaced. 619 client.SetHeader("content-type", "application/x-garbage") 620 _, err = client.SupportedModules() 621 if err == nil { 622 t.Fatal("no error for invalid content-type header") 623 } else if !strings.Contains(err.Error(), "Unsupported Media Type") { 624 t.Fatalf("error is not related to content-type: %q", err) 625 } 626 } 627 628 func TestClientHTTP(t *testing.T) { 629 server := newTestServer() 630 defer server.Stop() 631 632 client, hs := httpTestClient(server, "http", nil) 633 defer hs.Close() 634 defer client.Close() 635 636 // Launch concurrent requests. 637 var ( 638 results = make([]echoResult, 100) 639 errc = make(chan error, len(results)) 640 wantResult = echoResult{"a", 1, new(echoArgs)} 641 ) 642 defer client.Close() 643 for i := range results { 644 i := i 645 go func() { 646 errc <- client.Call(&results[i], "test_echo", wantResult.String, wantResult.Int, wantResult.Args) 647 }() 648 } 649 650 // Wait for all of them to complete. 651 timeout := time.NewTimer(5 * time.Second) 652 defer timeout.Stop() 653 for i := range results { 654 select { 655 case err := <-errc: 656 if err != nil { 657 t.Fatal(err) 658 } 659 case <-timeout.C: 660 t.Fatalf("timeout (got %d/%d) results)", i+1, len(results)) 661 } 662 } 663 664 // Check results. 665 for i := range results { 666 if !reflect.DeepEqual(results[i], wantResult) { 667 t.Errorf("result %d mismatch: got %#v, want %#v", i, results[i], wantResult) 668 } 669 } 670 } 671 672 func TestClientReconnect(t *testing.T) { 673 startServer := func(addr string) (*Server, net.Listener) { 674 srv := newTestServer() 675 l, err := net.Listen("tcp", addr) 676 if err != nil { 677 t.Fatal("can't listen:", err) 678 } 679 go http.Serve(l, srv.WebsocketHandler([]string{"*"})) 680 return srv, l 681 } 682 683 ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second) 684 defer cancel() 685 686 // Start a server and corresponding client. 687 s1, l1 := startServer("127.0.0.1:0") 688 client, err := DialContext(ctx, "ws://"+l1.Addr().String()) 689 if err != nil { 690 t.Fatal("can't dial", err) 691 } 692 defer client.Close() 693 694 // Perform a call. This should work because the server is up. 695 var resp echoResult 696 if err := client.CallContext(ctx, &resp, "test_echo", "", 1, nil); err != nil { 697 t.Fatal(err) 698 } 699 700 // Shut down the server and allow for some cool down time so we can listen on the same 701 // address again. 702 l1.Close() 703 s1.Stop() 704 time.Sleep(2 * time.Second) 705 706 // Try calling again. It shouldn't work. 707 if err := client.CallContext(ctx, &resp, "test_echo", "", 2, nil); err == nil { 708 t.Error("successful call while the server is down") 709 t.Logf("resp: %#v", resp) 710 } 711 712 // Start it up again and call again. The connection should be reestablished. 713 // We spawn multiple calls here to check whether this hangs somehow. 714 s2, l2 := startServer(l1.Addr().String()) 715 defer l2.Close() 716 defer s2.Stop() 717 718 start := make(chan struct{}) 719 errors := make(chan error, 20) 720 for i := 0; i < cap(errors); i++ { 721 go func() { 722 <-start 723 var resp echoResult 724 errors <- client.CallContext(ctx, &resp, "test_echo", "", 3, nil) 725 }() 726 } 727 close(start) 728 errcount := 0 729 for i := 0; i < cap(errors); i++ { 730 if err = <-errors; err != nil { 731 errcount++ 732 } 733 } 734 t.Logf("%d errors, last error: %v", errcount, err) 735 if errcount > 1 { 736 t.Errorf("expected one error after disconnect, got %d", errcount) 737 } 738 } 739 740 func httpTestClient(srv *Server, transport string, fl *flakeyListener) (*Client, *httptest.Server) { 741 // Create the HTTP server. 742 var hs *httptest.Server 743 switch transport { 744 case "ws": 745 hs = httptest.NewUnstartedServer(srv.WebsocketHandler([]string{"*"})) 746 case "http": 747 hs = httptest.NewUnstartedServer(srv) 748 default: 749 panic("unknown HTTP transport: " + transport) 750 } 751 // Wrap the listener if required. 752 if fl != nil { 753 fl.Listener = hs.Listener 754 hs.Listener = fl 755 } 756 // Connect the client. 757 hs.Start() 758 client, err := Dial(transport + "://" + hs.Listener.Addr().String()) 759 if err != nil { 760 panic(err) 761 } 762 return client, hs 763 } 764 765 // func ipcTestClient(srv *Server, fl *flakeyListener) (*Client, net.Listener) { 766 // // Listen on a random endpoint. 767 // endpoint := fmt.Sprintf("go-ethereum-test-ipc-%d-%d", os.Getpid(), rand.Int63()) 768 // if runtime.GOOS == "windows" { 769 // endpoint = `\\.\pipe\` + endpoint 770 // } else { 771 // endpoint = os.TempDir() + "/" + endpoint 772 // } 773 // l, err := ipcListen(endpoint) 774 // if err != nil { 775 // panic(err) 776 // } 777 // // Connect the listener to the server. 778 // if fl != nil { 779 // fl.Listener = l 780 // l = fl 781 // } 782 // go srv.ServeListener(l) 783 // // Connect the client. 784 // client, err := Dial(endpoint) 785 // if err != nil { 786 // panic(err) 787 // } 788 // return client, l 789 // } 790 791 // flakeyListener kills accepted connections after a random timeout. 792 type flakeyListener struct { 793 net.Listener 794 maxKillTimeout time.Duration 795 maxAcceptDelay time.Duration 796 } 797 798 func (l *flakeyListener) Accept() (net.Conn, error) { 799 delay := time.Duration(rand.Int63n(int64(l.maxAcceptDelay))) 800 time.Sleep(delay) 801 802 c, err := l.Listener.Accept() 803 if err == nil { 804 timeout := time.Duration(rand.Int63n(int64(l.maxKillTimeout))) 805 time.AfterFunc(timeout, func() { 806 log.Debug(fmt.Sprintf("killing conn %v after %v", c.LocalAddr(), timeout)) 807 c.Close() 808 }) 809 } 810 return c, err 811 }