storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/http/dial_dnscache_test.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2020 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package http 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "math/rand" 24 "net" 25 "testing" 26 "time" 27 ) 28 29 var ( 30 testFreq = 1 * time.Second 31 testDefaultLookupTimeout = 1 * time.Second 32 ) 33 34 func logOnce(ctx context.Context, err error, id interface{}, errKind ...interface{}) { 35 // no-op 36 } 37 38 func testDNSCache(t *testing.T) *DNSCache { 39 t.Helper() // skip printing file and line information from this function 40 return NewDNSCache(testFreq, testDefaultLookupTimeout, logOnce) 41 } 42 43 func TestDialContextWithDNSCache(t *testing.T) { 44 resolver := &DNSCache{ 45 cache: map[string][]string{ 46 "play.min.io": { 47 "127.0.0.1", 48 "127.0.0.2", 49 "127.0.0.3", 50 }, 51 }, 52 } 53 54 cases := []struct { 55 permF func(n int) []int 56 dialF DialContext 57 }{ 58 { 59 permF: func(n int) []int { 60 return []int{0} 61 }, 62 dialF: func(ctx context.Context, network, addr string) (net.Conn, error) { 63 if got, want := addr, net.JoinHostPort("127.0.0.1", "443"); got != want { 64 t.Fatalf("got addr %q, want %q", got, want) 65 } 66 return nil, nil 67 }, 68 }, 69 { 70 permF: func(n int) []int { 71 return []int{1} 72 }, 73 dialF: func(ctx context.Context, network, addr string) (net.Conn, error) { 74 if got, want := addr, net.JoinHostPort("127.0.0.2", "443"); got != want { 75 t.Fatalf("got addr %q, want %q", got, want) 76 } 77 return nil, nil 78 }, 79 }, 80 { 81 permF: func(n int) []int { 82 return []int{2} 83 }, 84 dialF: func(ctx context.Context, network, addr string) (net.Conn, error) { 85 if got, want := addr, net.JoinHostPort("127.0.0.3", "443"); got != want { 86 t.Fatalf("got addr %q, want %q", got, want) 87 } 88 return nil, nil 89 }, 90 }, 91 } 92 93 origFunc := randPerm 94 defer func() { 95 randPerm = origFunc 96 }() 97 98 for _, tc := range cases { 99 t.Run("", func(t *testing.T) { 100 randPerm = tc.permF 101 if _, err := DialContextWithDNSCache(resolver, tc.dialF)(context.Background(), "tcp", "play.min.io:443"); err != nil { 102 t.Fatalf("err: %s", err) 103 } 104 }) 105 } 106 107 } 108 109 func TestDialContextWithDNSCacheRand(t *testing.T) { 110 rand.Seed(time.Now().UTC().UnixNano()) 111 defer func() { 112 rand.Seed(1) 113 }() 114 115 resolver := &DNSCache{ 116 cache: map[string][]string{ 117 "play.min.io": { 118 "127.0.0.1", 119 "127.0.0.2", 120 "127.0.0.3", 121 }, 122 }, 123 } 124 125 count := make(map[string]int) 126 dialF := func(ctx context.Context, network, addr string) (net.Conn, error) { 127 count[addr]++ 128 return nil, nil 129 } 130 131 for i := 0; i < 100; i++ { 132 if _, err := DialContextWithDNSCache(resolver, dialF)(context.Background(), "tcp", "play.min.io:443"); err != nil { 133 t.Fatalf("err: %s", err) 134 } 135 } 136 137 for _, c := range count { 138 got := float32(c) / float32(100) 139 if got < float32(0.1) { 140 t.Fatalf("expected 0.1 rate got %f", got) 141 } 142 } 143 } 144 145 // Verify without port Dial fails, Go stdlib net.Dial expects port 146 func TestDialContextWithDNSCacheScenario1(t *testing.T) { 147 resolver := testDNSCache(t) 148 if _, err := DialContextWithDNSCache(resolver, nil)(context.Background(), "tcp", "play.min.io"); err == nil { 149 t.Fatalf("expect to fail") // expected port 150 } 151 } 152 153 // Verify if the host lookup function failed to return addresses 154 func TestDialContextWithDNSCacheScenario2(t *testing.T) { 155 res := testDNSCache(t) 156 originalFunc := res.lookupHostFn 157 defer func() { 158 res.lookupHostFn = originalFunc 159 }() 160 161 res.lookupHostFn = func(ctx context.Context, host string) ([]string, error) { 162 return nil, fmt.Errorf("err") 163 } 164 165 if _, err := DialContextWithDNSCache(res, nil)(context.Background(), "tcp", "min.io:443"); err == nil { 166 t.Fatalf("exect to fail") 167 } 168 } 169 170 // Verify we always return the first error from net.Dial failure 171 func TestDialContextWithDNSCacheScenario3(t *testing.T) { 172 resolver := &DNSCache{ 173 cache: map[string][]string{ 174 "min.io": { 175 "1.1.1.1", 176 "2.2.2.2", 177 "3.3.3.3", 178 }, 179 }, 180 } 181 182 origFunc := randPerm 183 randPerm = func(n int) []int { 184 return []int{0, 1, 2} 185 } 186 defer func() { 187 randPerm = origFunc 188 }() 189 190 want := errors.New("error1") 191 dialF := func(ctx context.Context, network, addr string) (net.Conn, error) { 192 if addr == net.JoinHostPort("1.1.1.1", "443") { 193 return nil, want // first error should be returned 194 } 195 if addr == net.JoinHostPort("2.2.2.2", "443") { 196 return nil, fmt.Errorf("error2") 197 } 198 if addr == net.JoinHostPort("3.3.3.3", "443") { 199 return nil, fmt.Errorf("error3") 200 } 201 return nil, nil 202 } 203 204 _, got := DialContextWithDNSCache(resolver, dialF)(context.Background(), "tcp", "min.io:443") 205 if got != want { 206 t.Fatalf("got error %v, want %v", got, want) 207 } 208 }