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