google.golang.org/grpc@v1.72.2/clientconn_parsed_target_test.go (about) 1 /* 2 * 3 * Copyright 2021 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package grpc 20 21 import ( 22 "context" 23 "errors" 24 "fmt" 25 "net" 26 "testing" 27 "time" 28 29 "github.com/google/go-cmp/cmp" 30 "google.golang.org/grpc/credentials/insecure" 31 "google.golang.org/grpc/internal" 32 "google.golang.org/grpc/internal/testutils" 33 "google.golang.org/grpc/resolver" 34 ) 35 36 func generateTarget(target string) resolver.Target { 37 return resolver.Target{URL: *testutils.MustParseURL(target)} 38 } 39 40 // Resets the default scheme as though it was never set by the user. 41 func resetInitialResolverState() { 42 resolver.SetDefaultScheme("passthrough") 43 internal.UserSetDefaultScheme = false 44 } 45 46 type testResolverForParser struct { 47 resolver.Resolver 48 } 49 50 func (testResolverForParser) Build(resolver.Target, resolver.ClientConn, resolver.BuildOptions) (resolver.Resolver, error) { 51 return testResolverForParser{}, nil 52 } 53 54 func (testResolverForParser) Close() {} 55 56 func (testResolverForParser) Scheme() string { 57 return "testresolverforparser" 58 } 59 60 func init() { resolver.Register(testResolverForParser{}) } 61 62 func (s) TestParsedTarget_Success_WithoutCustomDialer(t *testing.T) { 63 tests := []struct { 64 target string 65 wantDialParse resolver.Target 66 wantNewClientParse resolver.Target 67 wantCustomParse resolver.Target 68 }{ 69 // No scheme is specified. 70 { 71 target: "://a/b", 72 wantDialParse: generateTarget("passthrough:///://a/b"), 73 wantNewClientParse: generateTarget("dns:///://a/b"), 74 wantCustomParse: generateTarget("testresolverforparser:///://a/b"), 75 }, 76 { 77 target: "a//b", 78 wantDialParse: generateTarget("passthrough:///a//b"), 79 wantNewClientParse: generateTarget("dns:///a//b"), 80 wantCustomParse: generateTarget("testresolverforparser:///a//b"), 81 }, 82 83 // An unregistered scheme is specified. 84 { 85 target: "a:///", 86 wantDialParse: generateTarget("passthrough:///a:///"), 87 wantNewClientParse: generateTarget("dns:///a:///"), 88 wantCustomParse: generateTarget("testresolverforparser:///a:///"), 89 }, 90 { 91 target: "a:b", 92 wantDialParse: generateTarget("passthrough:///a:b"), 93 wantNewClientParse: generateTarget("dns:///a:b"), 94 wantCustomParse: generateTarget("testresolverforparser:///a:b"), 95 }, 96 97 // A registered scheme is specified. 98 { 99 target: "dns://a.server.com/google.com", 100 wantDialParse: generateTarget("dns://a.server.com/google.com"), 101 wantNewClientParse: generateTarget("dns://a.server.com/google.com"), 102 wantCustomParse: generateTarget("dns://a.server.com/google.com"), 103 }, 104 { 105 target: "unix-abstract:/ a///://::!@#$%25^&*()b", 106 wantDialParse: generateTarget("unix-abstract:/ a///://::!@#$%25^&*()b"), 107 wantNewClientParse: generateTarget("unix-abstract:/ a///://::!@#$%25^&*()b"), 108 wantCustomParse: generateTarget("unix-abstract:/ a///://::!@#$%25^&*()b"), 109 }, 110 { 111 target: "unix-abstract:passthrough:abc", 112 wantDialParse: generateTarget("unix-abstract:passthrough:abc"), 113 wantNewClientParse: generateTarget("unix-abstract:passthrough:abc"), 114 wantCustomParse: generateTarget("unix-abstract:passthrough:abc"), 115 }, 116 { 117 target: "passthrough:///unix:///a/b/c", 118 wantDialParse: generateTarget("passthrough:///unix:///a/b/c"), 119 wantNewClientParse: generateTarget("passthrough:///unix:///a/b/c"), 120 wantCustomParse: generateTarget("passthrough:///unix:///a/b/c"), 121 }, 122 123 // Cases for `scheme:absolute-path`. 124 { 125 target: "dns:/a/b/c", 126 wantDialParse: generateTarget("dns:/a/b/c"), 127 wantNewClientParse: generateTarget("dns:/a/b/c"), 128 wantCustomParse: generateTarget("dns:/a/b/c"), 129 }, 130 { 131 target: "unregistered:/a/b/c", 132 wantDialParse: generateTarget("passthrough:///unregistered:/a/b/c"), 133 wantNewClientParse: generateTarget("dns:///unregistered:/a/b/c"), 134 wantCustomParse: generateTarget("testresolverforparser:///unregistered:/a/b/c"), 135 }, 136 } 137 138 for _, test := range tests { 139 t.Run(test.target, func(t *testing.T) { 140 resetInitialResolverState() 141 cc, err := Dial(test.target, WithTransportCredentials(insecure.NewCredentials())) 142 if err != nil { 143 t.Fatalf("Dial(%q) failed: %v", test.target, err) 144 } 145 cc.Close() 146 147 if !cmp.Equal(cc.parsedTarget, test.wantDialParse) { 148 t.Errorf("cc.parsedTarget for dial target %q = %+v, want %+v", test.target, cc.parsedTarget, test.wantDialParse) 149 } 150 151 cc, err = NewClient(test.target, WithTransportCredentials(insecure.NewCredentials())) 152 if err != nil { 153 t.Fatalf("NewClient(%q) failed: %v", test.target, err) 154 } 155 cc.Close() 156 157 if !cmp.Equal(cc.parsedTarget, test.wantNewClientParse) { 158 t.Errorf("cc.parsedTarget for newClient target %q = %+v, want %+v", test.target, cc.parsedTarget, test.wantNewClientParse) 159 } 160 161 resolver.SetDefaultScheme("testresolverforparser") 162 cc, err = Dial(test.target, WithTransportCredentials(insecure.NewCredentials())) 163 if err != nil { 164 t.Fatalf("Dial(%q) failed: %v", test.target, err) 165 } 166 cc.Close() 167 168 if !cmp.Equal(cc.parsedTarget, test.wantCustomParse) { 169 t.Errorf("cc.parsedTarget for dial target %q = %+v, want %+v", test.target, cc.parsedTarget, test.wantDialParse) 170 } 171 172 cc, err = NewClient(test.target, WithTransportCredentials(insecure.NewCredentials())) 173 if err != nil { 174 t.Fatalf("NewClient(%q) failed: %v", test.target, err) 175 } 176 cc.Close() 177 178 if !cmp.Equal(cc.parsedTarget, test.wantCustomParse) { 179 t.Errorf("cc.parsedTarget for newClient target %q = %+v, want %+v", test.target, cc.parsedTarget, test.wantNewClientParse) 180 } 181 182 }) 183 } 184 resetInitialResolverState() 185 } 186 187 func (s) TestParsedTarget_Failure_WithoutCustomDialer(t *testing.T) { 188 targets := []string{ 189 "", 190 "unix://a/b/c", 191 "unix://authority", 192 "unix-abstract://authority/a/b/c", 193 "unix-abstract://authority", 194 } 195 196 for _, target := range targets { 197 t.Run(target, func(t *testing.T) { 198 if cc, err := Dial(target, WithTransportCredentials(insecure.NewCredentials())); err == nil { 199 defer cc.Close() 200 t.Fatalf("Dial(%q) succeeded cc.parsedTarget = %+v, expected to fail", target, cc.parsedTarget) 201 } 202 }) 203 } 204 } 205 206 func (s) TestParsedTarget_WithCustomDialer(t *testing.T) { 207 resetInitialResolverState() 208 defScheme := resolver.GetDefaultScheme() 209 tests := []struct { 210 target string 211 wantParsed resolver.Target 212 wantDialerAddress string 213 }{ 214 // unix:[local_path], unix:[/absolute], and unix://[/absolute] have 215 // different behaviors with a custom dialer. 216 { 217 target: "unix:a/b/c", 218 wantParsed: resolver.Target{URL: *testutils.MustParseURL("unix:a/b/c")}, 219 wantDialerAddress: "unix:a/b/c", 220 }, 221 { 222 target: "unix:/a/b/c", 223 wantParsed: resolver.Target{URL: *testutils.MustParseURL("unix:/a/b/c")}, 224 wantDialerAddress: "unix:///a/b/c", 225 }, 226 { 227 target: "unix:///a/b/c", 228 wantParsed: resolver.Target{URL: *testutils.MustParseURL("unix:///a/b/c")}, 229 wantDialerAddress: "unix:///a/b/c", 230 }, 231 { 232 target: "dns:///127.0.0.1:50051", 233 wantParsed: resolver.Target{URL: *testutils.MustParseURL("dns:///127.0.0.1:50051")}, 234 wantDialerAddress: "127.0.0.1:50051", 235 }, 236 { 237 target: ":///127.0.0.1:50051", 238 wantParsed: resolver.Target{URL: *testutils.MustParseURL(fmt.Sprintf("%s:///%s", defScheme, ":///127.0.0.1:50051"))}, 239 wantDialerAddress: ":///127.0.0.1:50051", 240 }, 241 { 242 target: "dns://authority/127.0.0.1:50051", 243 wantParsed: resolver.Target{URL: *testutils.MustParseURL("dns://authority/127.0.0.1:50051")}, 244 wantDialerAddress: "127.0.0.1:50051", 245 }, 246 { 247 target: "://authority/127.0.0.1:50051", 248 wantParsed: resolver.Target{URL: *testutils.MustParseURL(fmt.Sprintf("%s:///%s", defScheme, "://authority/127.0.0.1:50051"))}, 249 wantDialerAddress: "://authority/127.0.0.1:50051", 250 }, 251 { 252 target: "/unix/socket/address", 253 wantParsed: resolver.Target{URL: *testutils.MustParseURL(fmt.Sprintf("%s:///%s", defScheme, "/unix/socket/address"))}, 254 wantDialerAddress: "/unix/socket/address", 255 }, 256 { 257 target: "", 258 wantParsed: resolver.Target{URL: *testutils.MustParseURL(fmt.Sprintf("%s:///%s", defScheme, ""))}, 259 wantDialerAddress: "", 260 }, 261 { 262 target: "passthrough://a.server.com/google.com", 263 wantParsed: resolver.Target{URL: *testutils.MustParseURL("passthrough://a.server.com/google.com")}, 264 wantDialerAddress: "google.com", 265 }, 266 } 267 268 for _, test := range tests { 269 t.Run(test.target, func(t *testing.T) { 270 addrCh := make(chan string, 1) 271 dialer := func(_ context.Context, address string) (net.Conn, error) { 272 addrCh <- address 273 return nil, errors.New("dialer error") 274 } 275 276 cc, err := Dial(test.target, WithTransportCredentials(insecure.NewCredentials()), WithContextDialer(dialer)) 277 if err != nil { 278 t.Fatalf("Dial(%q) failed: %v", test.target, err) 279 } 280 defer cc.Close() 281 282 select { 283 case addr := <-addrCh: 284 if addr != test.wantDialerAddress { 285 t.Fatalf("address in custom dialer is %q, want %q", addr, test.wantDialerAddress) 286 } 287 case <-time.After(time.Second): 288 t.Fatal("timeout when waiting for custom dialer to be invoked") 289 } 290 if !cmp.Equal(cc.parsedTarget, test.wantParsed) { 291 t.Errorf("cc.parsedTarget for dial target %q = %+v, want %+v", test.target, cc.parsedTarget, test.wantParsed) 292 } 293 }) 294 } 295 }