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