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  }