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