github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/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 "net" 25 "net/url" 26 "testing" 27 "time" 28 29 "github.com/google/go-cmp/cmp" 30 31 "github.com/hxx258456/ccgo/grpc/resolver" 32 ) 33 34 func (s) TestParsedTarget_Success_WithoutCustomDialer(t *testing.T) { 35 defScheme := resolver.GetDefaultScheme() 36 tests := []struct { 37 target string 38 wantParsed resolver.Target 39 }{ 40 // No scheme is specified. 41 {target: "", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "", URL: url.URL{Scheme: defScheme, Path: "/"}}}, 42 {target: "://", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "://", URL: url.URL{Scheme: defScheme, Path: "/://"}}}, 43 {target: ":///", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: ":///", URL: url.URL{Scheme: defScheme, Path: "/:///"}}}, 44 {target: "://a/", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "://a/", URL: url.URL{Scheme: defScheme, Path: "/://a/"}}}, 45 {target: ":///a", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: ":///a", URL: url.URL{Scheme: defScheme, Path: "/:///a"}}}, 46 {target: "://a/b", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "://a/b", URL: url.URL{Scheme: defScheme, Path: "/://a/b"}}}, 47 {target: "/", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "/", URL: url.URL{Scheme: defScheme, Path: "//"}}}, 48 {target: "a/b", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "a/b", URL: url.URL{Scheme: defScheme, Path: "/a/b"}}}, 49 {target: "a//b", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "a//b", URL: url.URL{Scheme: defScheme, Path: "/a//b"}}}, 50 {target: "google.com", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "google.com", URL: url.URL{Scheme: defScheme, Path: "/google.com"}}}, 51 {target: "google.com/?a=b", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "google.com/", URL: url.URL{Scheme: defScheme, Path: "/google.com/", RawQuery: "a=b"}}}, 52 {target: "/unix/socket/address", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "/unix/socket/address", URL: url.URL{Scheme: defScheme, Path: "//unix/socket/address"}}}, 53 54 // An unregistered scheme is specified. 55 {target: "a:///", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "a:///", URL: url.URL{Scheme: defScheme, Path: "/a:///"}}}, 56 {target: "a://b/", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "a://b/", URL: url.URL{Scheme: defScheme, Path: "/a://b/"}}}, 57 {target: "a:///b", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "a:///b", URL: url.URL{Scheme: defScheme, Path: "/a:///b"}}}, 58 {target: "a://b/c", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "a://b/c", URL: url.URL{Scheme: defScheme, Path: "/a://b/c"}}}, 59 {target: "a:b", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "a:b", URL: url.URL{Scheme: defScheme, Path: "/a:b"}}}, 60 {target: "a:/b", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "a:/b", URL: url.URL{Scheme: defScheme, Path: "/a:/b"}}}, 61 {target: "a://b", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "a://b", URL: url.URL{Scheme: defScheme, Path: "/a://b"}}}, 62 63 // A registered scheme is specified. 64 {target: "dns:///google.com", wantParsed: resolver.Target{Scheme: "dns", Authority: "", Endpoint: "google.com", URL: url.URL{Scheme: "dns", Path: "/google.com"}}}, 65 {target: "dns://a.server.com/google.com", wantParsed: resolver.Target{Scheme: "dns", Authority: "a.server.com", Endpoint: "google.com", URL: url.URL{Scheme: "dns", Host: "a.server.com", Path: "/google.com"}}}, 66 {target: "dns://a.server.com/google.com/?a=b", wantParsed: resolver.Target{Scheme: "dns", Authority: "a.server.com", Endpoint: "google.com/", URL: url.URL{Scheme: "dns", Host: "a.server.com", Path: "/google.com/", RawQuery: "a=b"}}}, 67 {target: "unix:///a/b/c", wantParsed: resolver.Target{Scheme: "unix", Authority: "", Endpoint: "a/b/c", URL: url.URL{Scheme: "unix", Path: "/a/b/c"}}}, 68 {target: "unix-abstract:a/b/c", wantParsed: resolver.Target{Scheme: "unix-abstract", Authority: "", Endpoint: "a/b/c", URL: url.URL{Scheme: "unix-abstract", Path: "", Opaque: "a/b/c"}}}, 69 {target: "unix-abstract:a b", wantParsed: resolver.Target{Scheme: "unix-abstract", Authority: "", Endpoint: "a b", URL: url.URL{Scheme: "unix-abstract", Path: "", Opaque: "a b"}}}, 70 {target: "unix-abstract:a:b", wantParsed: resolver.Target{Scheme: "unix-abstract", Authority: "", Endpoint: "a:b", URL: url.URL{Scheme: "unix-abstract", Path: "", Opaque: "a:b"}}}, 71 {target: "unix-abstract:a-b", wantParsed: resolver.Target{Scheme: "unix-abstract", Authority: "", Endpoint: "a-b", URL: url.URL{Scheme: "unix-abstract", Path: "", Opaque: "a-b"}}}, 72 {target: "unix-abstract:/ a///://::!@#$%25^&*()b", wantParsed: resolver.Target{Scheme: "unix-abstract", Authority: "", Endpoint: " a///://::!@", URL: url.URL{Scheme: "unix-abstract", Path: "/ a///://::!@", RawPath: "/ a///://::!@", Fragment: "$%^&*()b", RawFragment: "$%25^&*()b"}}}, 73 {target: "unix-abstract:passthrough:abc", wantParsed: resolver.Target{Scheme: "unix-abstract", Authority: "", Endpoint: "passthrough:abc", URL: url.URL{Scheme: "unix-abstract", Path: "", Opaque: "passthrough:abc"}}}, 74 {target: "unix-abstract:unix:///abc", wantParsed: resolver.Target{Scheme: "unix-abstract", Authority: "", Endpoint: "unix:///abc", URL: url.URL{Scheme: "unix-abstract", Path: "", Opaque: "unix:///abc"}}}, 75 {target: "unix-abstract:///a/b/c", wantParsed: resolver.Target{Scheme: "unix-abstract", Authority: "", Endpoint: "a/b/c", URL: url.URL{Scheme: "unix-abstract", Path: "/a/b/c"}}}, 76 {target: "unix-abstract:///", wantParsed: resolver.Target{Scheme: "unix-abstract", Authority: "", Endpoint: "", URL: url.URL{Scheme: "unix-abstract", Path: "/"}}}, 77 {target: "passthrough:///unix:///a/b/c", wantParsed: resolver.Target{Scheme: "passthrough", Authority: "", Endpoint: "unix:///a/b/c", URL: url.URL{Scheme: "passthrough", Path: "/unix:///a/b/c"}}}, 78 79 // Cases for `scheme:absolute-path`. 80 {target: "dns:/a/b/c", wantParsed: resolver.Target{Scheme: "dns", Authority: "", Endpoint: "a/b/c", URL: url.URL{Scheme: "dns", Path: "/a/b/c"}}}, 81 {target: "unregistered:/a/b/c", wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "unregistered:/a/b/c", URL: url.URL{Scheme: defScheme, Path: "/unregistered:/a/b/c"}}}, 82 } 83 84 for _, test := range tests { 85 t.Run(test.target, func(t *testing.T) { 86 cc, err := Dial(test.target, WithInsecure()) 87 if err != nil { 88 t.Fatalf("Dial(%q) failed: %v", test.target, err) 89 } 90 defer cc.Close() 91 92 if !cmp.Equal(cc.parsedTarget, test.wantParsed) { 93 t.Errorf("cc.parsedTarget for dial target %q = %+v, want %+v", test.target, cc.parsedTarget, test.wantParsed) 94 } 95 }) 96 } 97 } 98 99 func (s) TestParsedTarget_Failure_WithoutCustomDialer(t *testing.T) { 100 targets := []string{ 101 "unix://a/b/c", 102 "unix://authority", 103 "unix-abstract://authority/a/b/c", 104 "unix-abstract://authority", 105 } 106 107 for _, target := range targets { 108 t.Run(target, func(t *testing.T) { 109 if cc, err := Dial(target, WithInsecure()); err == nil { 110 defer cc.Close() 111 t.Fatalf("Dial(%q) succeeded cc.parsedTarget = %+v, expected to fail", target, cc.parsedTarget) 112 } 113 }) 114 } 115 } 116 117 func (s) TestParsedTarget_WithCustomDialer(t *testing.T) { 118 defScheme := resolver.GetDefaultScheme() 119 tests := []struct { 120 target string 121 wantParsed resolver.Target 122 wantDialerAddress string 123 }{ 124 // unix:[local_path], unix:[/absolute], and unix://[/absolute] have 125 // different behaviors with a custom dialer. 126 { 127 target: "unix:a/b/c", 128 wantParsed: resolver.Target{Scheme: "unix", Authority: "", Endpoint: "a/b/c", URL: url.URL{Scheme: "unix", Opaque: "a/b/c"}}, 129 wantDialerAddress: "unix:a/b/c", 130 }, 131 { 132 target: "unix:/a/b/c", 133 wantParsed: resolver.Target{Scheme: "unix", Authority: "", Endpoint: "a/b/c", URL: url.URL{Scheme: "unix", Path: "/a/b/c"}}, 134 wantDialerAddress: "unix:///a/b/c", 135 }, 136 { 137 target: "unix:///a/b/c", 138 wantParsed: resolver.Target{Scheme: "unix", Authority: "", Endpoint: "a/b/c", URL: url.URL{Scheme: "unix", Path: "/a/b/c"}}, 139 wantDialerAddress: "unix:///a/b/c", 140 }, 141 { 142 target: "dns:///127.0.0.1:50051", 143 wantParsed: resolver.Target{Scheme: "dns", Authority: "", Endpoint: "127.0.0.1:50051", URL: url.URL{Scheme: "dns", Path: "/127.0.0.1:50051"}}, 144 wantDialerAddress: "127.0.0.1:50051", 145 }, 146 { 147 target: ":///127.0.0.1:50051", 148 wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: ":///127.0.0.1:50051", URL: url.URL{Scheme: defScheme, Path: "/:///127.0.0.1:50051"}}, 149 wantDialerAddress: ":///127.0.0.1:50051", 150 }, 151 { 152 target: "dns://authority/127.0.0.1:50051", 153 wantParsed: resolver.Target{Scheme: "dns", Authority: "authority", Endpoint: "127.0.0.1:50051", URL: url.URL{Scheme: "dns", Host: "authority", Path: "/127.0.0.1:50051"}}, 154 wantDialerAddress: "127.0.0.1:50051", 155 }, 156 { 157 target: "://authority/127.0.0.1:50051", 158 wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "://authority/127.0.0.1:50051", URL: url.URL{Scheme: defScheme, Path: "/://authority/127.0.0.1:50051"}}, 159 wantDialerAddress: "://authority/127.0.0.1:50051", 160 }, 161 { 162 target: "/unix/socket/address", 163 wantParsed: resolver.Target{Scheme: defScheme, Authority: "", Endpoint: "/unix/socket/address", URL: url.URL{Scheme: defScheme, Path: "//unix/socket/address"}}, 164 wantDialerAddress: "/unix/socket/address", 165 }, 166 { 167 target: "passthrough://a.server.com/google.com", 168 wantParsed: resolver.Target{Scheme: "passthrough", Authority: "a.server.com", Endpoint: "google.com", URL: url.URL{Scheme: "passthrough", Host: "a.server.com", Path: "/google.com"}}, 169 wantDialerAddress: "google.com", 170 }, 171 } 172 173 for _, test := range tests { 174 t.Run(test.target, func(t *testing.T) { 175 addrCh := make(chan string, 1) 176 dialer := func(ctx context.Context, address string) (net.Conn, error) { 177 addrCh <- address 178 return nil, errors.New("dialer error") 179 } 180 181 cc, err := Dial(test.target, WithInsecure(), WithContextDialer(dialer)) 182 if err != nil { 183 t.Fatalf("Dial(%q) failed: %v", test.target, err) 184 } 185 defer cc.Close() 186 187 select { 188 case addr := <-addrCh: 189 if addr != test.wantDialerAddress { 190 t.Fatalf("address in custom dialer is %q, want %q", addr, test.wantDialerAddress) 191 } 192 case <-time.After(time.Second): 193 t.Fatal("timeout when waiting for custom dialer to be invoked") 194 } 195 if !cmp.Equal(cc.parsedTarget, test.wantParsed) { 196 t.Errorf("cc.parsedTarget for dial target %q = %+v, want %+v", test.target, cc.parsedTarget, test.wantParsed) 197 } 198 }) 199 } 200 }