|
19 | 19 | package delegatingresolver_test
|
20 | 20 |
|
21 | 21 | import (
|
| 22 | + "context" |
| 23 | + "errors" |
22 | 24 | "net/http"
|
23 | 25 | "net/url"
|
24 | 26 | "testing"
|
@@ -548,3 +550,213 @@ func (s) TestDelegatingResolverForMutipleProxyAddress(t *testing.T) {
|
548 | 550 | t.Fatalf("Unexpected state from delegating resolver. Diff (-got +want):\n%v", diff)
|
549 | 551 | }
|
550 | 552 | }
|
| 553 | + |
| 554 | +// Tests that delegatingresolver doesn't panic when the channel closes the |
| 555 | +// resolver while it's handling an update from it's child. The test closes the |
| 556 | +// delegating resolver, verifies the target resolver is closed and blocks the |
| 557 | +// proxy resolver from being closed. The test sends an update from the proxy |
| 558 | +// resolver and verifies that the target resolver's ResolveNow method is not |
| 559 | +// called after the channels returns an error. |
| 560 | +func (s) TestDelegatingResolverUpdateStateDuringClose(t *testing.T) { |
| 561 | + const envProxyAddr = "proxytest.com" |
| 562 | + |
| 563 | + hpfe := func(req *http.Request) (*url.URL, error) { |
| 564 | + return &url.URL{ |
| 565 | + Scheme: "https", |
| 566 | + Host: envProxyAddr, |
| 567 | + }, nil |
| 568 | + } |
| 569 | + originalhpfe := delegatingresolver.HTTPSProxyFromEnvironment |
| 570 | + delegatingresolver.HTTPSProxyFromEnvironment = hpfe |
| 571 | + defer func() { |
| 572 | + delegatingresolver.HTTPSProxyFromEnvironment = originalhpfe |
| 573 | + }() |
| 574 | + |
| 575 | + // Manual resolver to control the target resolution. |
| 576 | + targetResolver := manual.NewBuilderWithScheme("test") |
| 577 | + targetResolverCalled := make(chan struct{}) |
| 578 | + targetResolver.ResolveNowCallback = func(resolver.ResolveNowOptions) { |
| 579 | + close(targetResolverCalled) |
| 580 | + } |
| 581 | + targetResolverCloseCalled := make(chan struct{}) |
| 582 | + targetResolver.CloseCallback = func() { |
| 583 | + close(targetResolverCloseCalled) |
| 584 | + t.Log("Target resolver is closed.") |
| 585 | + } |
| 586 | + |
| 587 | + target := targetResolver.Scheme() + ":///" + "ignored" |
| 588 | + // Set up a manual DNS resolver to control the proxy address resolution. |
| 589 | + proxyResolver := setupDNS(t) |
| 590 | + |
| 591 | + unblockProxyResolverClose := make(chan struct{}) |
| 592 | + proxyResolver.CloseCallback = func() { |
| 593 | + <-unblockProxyResolverClose |
| 594 | + t.Log("Proxy resolver is closed.") |
| 595 | + } |
| 596 | + |
| 597 | + tcc, _, _ := createTestResolverClientConn(t) |
| 598 | + tcc.UpdateStateF = func(resolver.State) error { |
| 599 | + return errors.New("test error") |
| 600 | + } |
| 601 | + dr, err := delegatingresolver.New(resolver.Target{URL: *testutils.MustParseURL(target)}, tcc, resolver.BuildOptions{}, targetResolver, false) |
| 602 | + if err != nil { |
| 603 | + t.Fatalf("Failed to create delegating resolver: %v", err) |
| 604 | + } |
| 605 | + |
| 606 | + targetResolver.UpdateState(resolver.State{ |
| 607 | + Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: "1.1.1.1"}}}}, |
| 608 | + }) |
| 609 | + |
| 610 | + // Closing the delegating resolver will block until the test writes to the |
| 611 | + // unblockProxyResolverClose channel. |
| 612 | + go dr.Close() |
| 613 | + ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) |
| 614 | + defer cancel() |
| 615 | + select { |
| 616 | + case <-targetResolverCloseCalled: |
| 617 | + case <-ctx.Done(): |
| 618 | + t.Fatalf("Context timed out waiting for target resolver's Close method to be called.") |
| 619 | + } |
| 620 | + |
| 621 | + // Updating the channel will result in an error being returned. Since the |
| 622 | + // target resolver's Close method is already called, the delegating resolver |
| 623 | + // must not call "ResolveNow" on it. |
| 624 | + go proxyResolver.UpdateState(resolver.State{ |
| 625 | + Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: "1.1.1.1"}}}}, |
| 626 | + }) |
| 627 | + unblockProxyResolverClose <- struct{}{} |
| 628 | + |
| 629 | + select { |
| 630 | + case <-targetResolverCalled: |
| 631 | + t.Fatalf("targetResolver.ResolveNow() called unexpectedly.") |
| 632 | + case <-time.After(defaultTestShortTimeout): |
| 633 | + } |
| 634 | +} |
| 635 | + |
| 636 | +// Tests that calling cc.UpdateState in a blocking manner from a child resolver |
| 637 | +// while handling a ResolveNow call doesn't result in a deadlock. The test uses |
| 638 | +// a fake ClientConn that returns an error when calling cc.UpdateState. The test |
| 639 | +// makes the proxy resolver update the resolver state. The test verifies that |
| 640 | +// the delegating resolver calls ResolveNow on the target resolver when the |
| 641 | +// ClientConn returns an error. |
| 642 | +func (s) TestDelegatingResolverUpdateStateFromResolveNow(t *testing.T) { |
| 643 | + const envProxyAddr = "proxytest.com" |
| 644 | + |
| 645 | + hpfe := func(req *http.Request) (*url.URL, error) { |
| 646 | + return &url.URL{ |
| 647 | + Scheme: "https", |
| 648 | + Host: envProxyAddr, |
| 649 | + }, nil |
| 650 | + } |
| 651 | + originalhpfe := delegatingresolver.HTTPSProxyFromEnvironment |
| 652 | + delegatingresolver.HTTPSProxyFromEnvironment = hpfe |
| 653 | + defer func() { |
| 654 | + delegatingresolver.HTTPSProxyFromEnvironment = originalhpfe |
| 655 | + }() |
| 656 | + |
| 657 | + // Manual resolver to control the target resolution. |
| 658 | + targetResolver := manual.NewBuilderWithScheme("test") |
| 659 | + targetResolverCalled := make(chan struct{}) |
| 660 | + targetResolver.ResolveNowCallback = func(resolver.ResolveNowOptions) { |
| 661 | + // Updating the resolver state should not deadlock. |
| 662 | + targetResolver.CC.UpdateState(resolver.State{ |
| 663 | + Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: "1.1.1.1"}}}}, |
| 664 | + }) |
| 665 | + close(targetResolverCalled) |
| 666 | + } |
| 667 | + |
| 668 | + target := targetResolver.Scheme() + ":///" + "ignored" |
| 669 | + // Set up a manual DNS resolver to control the proxy address resolution. |
| 670 | + proxyResolver := setupDNS(t) |
| 671 | + |
| 672 | + tcc, _, _ := createTestResolverClientConn(t) |
| 673 | + tcc.UpdateStateF = func(resolver.State) error { |
| 674 | + return errors.New("test error") |
| 675 | + } |
| 676 | + _, err := delegatingresolver.New(resolver.Target{URL: *testutils.MustParseURL(target)}, tcc, resolver.BuildOptions{}, targetResolver, false) |
| 677 | + if err != nil { |
| 678 | + t.Fatalf("Failed to create delegating resolver: %v", err) |
| 679 | + } |
| 680 | + |
| 681 | + targetResolver.UpdateState(resolver.State{ |
| 682 | + Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: "1.1.1.1"}}}}, |
| 683 | + }) |
| 684 | + |
| 685 | + // Updating the channel will result in an error being returned. The |
| 686 | + // delegating resolver should call call "ResolveNow" on the target resolver. |
| 687 | + proxyResolver.UpdateState(resolver.State{ |
| 688 | + Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: "1.1.1.1"}}}}, |
| 689 | + }) |
| 690 | + |
| 691 | + ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) |
| 692 | + defer cancel() |
| 693 | + select { |
| 694 | + case <-targetResolverCalled: |
| 695 | + case <-ctx.Done(): |
| 696 | + t.Fatalf("context timed out waiting for targetResolver.ResolveNow() to be called.") |
| 697 | + } |
| 698 | +} |
| 699 | + |
| 700 | +// Tests that calling cc.UpdateState in a blocking manner from child resolvers |
| 701 | +// doesn't result in deadlocks. |
| 702 | +func (s) TestDelegatingResolverResolveNow(t *testing.T) { |
| 703 | + const envProxyAddr = "proxytest.com" |
| 704 | + |
| 705 | + hpfe := func(req *http.Request) (*url.URL, error) { |
| 706 | + return &url.URL{ |
| 707 | + Scheme: "https", |
| 708 | + Host: envProxyAddr, |
| 709 | + }, nil |
| 710 | + } |
| 711 | + originalhpfe := delegatingresolver.HTTPSProxyFromEnvironment |
| 712 | + delegatingresolver.HTTPSProxyFromEnvironment = hpfe |
| 713 | + defer func() { |
| 714 | + delegatingresolver.HTTPSProxyFromEnvironment = originalhpfe |
| 715 | + }() |
| 716 | + |
| 717 | + // Manual resolver to control the target resolution. |
| 718 | + targetResolver := manual.NewBuilderWithScheme("test") |
| 719 | + targetResolverCalled := make(chan struct{}) |
| 720 | + targetResolver.ResolveNowCallback = func(resolver.ResolveNowOptions) { |
| 721 | + // Updating the resolver state should not deadlock. |
| 722 | + targetResolver.CC.UpdateState(resolver.State{ |
| 723 | + Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: "1.1.1.1"}}}}, |
| 724 | + }) |
| 725 | + close(targetResolverCalled) |
| 726 | + } |
| 727 | + |
| 728 | + target := targetResolver.Scheme() + ":///" + "ignored" |
| 729 | + // Set up a manual DNS resolver to control the proxy address resolution. |
| 730 | + proxyResolver := setupDNS(t) |
| 731 | + proxyResolverCalled := make(chan struct{}) |
| 732 | + proxyResolver.ResolveNowCallback = func(resolver.ResolveNowOptions) { |
| 733 | + // Updating the resolver state should not deadlock. |
| 734 | + proxyResolver.CC.UpdateState(resolver.State{ |
| 735 | + Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: "1.1.1.1"}}}}, |
| 736 | + }) |
| 737 | + close(proxyResolverCalled) |
| 738 | + } |
| 739 | + |
| 740 | + tcc, _, _ := createTestResolverClientConn(t) |
| 741 | + dr, err := delegatingresolver.New(resolver.Target{URL: *testutils.MustParseURL(target)}, tcc, resolver.BuildOptions{}, targetResolver, false) |
| 742 | + if err != nil { |
| 743 | + t.Fatalf("Failed to create delegating resolver: %v", err) |
| 744 | + } |
| 745 | + |
| 746 | + // Call ResolveNow on the delegatingResolver and verify both children |
| 747 | + // receive the ResolveNow call. |
| 748 | + dr.ResolveNow(resolver.ResolveNowOptions{}) |
| 749 | + |
| 750 | + ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) |
| 751 | + defer cancel() |
| 752 | + select { |
| 753 | + case <-targetResolverCalled: |
| 754 | + case <-ctx.Done(): |
| 755 | + t.Fatalf("context timed out waiting for targetResolver.ResolveNow() to be called.") |
| 756 | + } |
| 757 | + select { |
| 758 | + case <-proxyResolverCalled: |
| 759 | + case <-ctx.Done(): |
| 760 | + t.Fatalf("context timed out waiting for proxyResolver.ResolveNow() to be called.") |
| 761 | + } |
| 762 | +} |
0 commit comments