storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/net_test.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2017 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 cmd
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"net"
    23  	"reflect"
    24  	"runtime"
    25  	"testing"
    26  
    27  	"github.com/minio/minio-go/v7/pkg/set"
    28  )
    29  
    30  func TestMustSplitHostPort(t *testing.T) {
    31  	testCases := []struct {
    32  		hostPort     string
    33  		expectedHost string
    34  		expectedPort string
    35  	}{
    36  		{":54321", "", "54321"},
    37  		{"server:54321", "server", "54321"},
    38  		{":0", "", "0"},
    39  		{"server:https", "server", "443"},
    40  		{"server:http", "server", "80"},
    41  	}
    42  
    43  	for _, testCase := range testCases {
    44  		host, port := mustSplitHostPort(testCase.hostPort)
    45  		if testCase.expectedHost != host {
    46  			t.Fatalf("host: expected = %v, got = %v", testCase.expectedHost, host)
    47  		}
    48  
    49  		if testCase.expectedPort != port {
    50  			t.Fatalf("port: expected = %v, got = %v", testCase.expectedPort, port)
    51  		}
    52  	}
    53  }
    54  
    55  func TestSortIPs(t *testing.T) {
    56  	testCases := []struct {
    57  		ipList       []string
    58  		sortedIPList []string
    59  	}{
    60  		// Default case of two ips one with higher octet moves
    61  		// to the beginning of the list.
    62  		{
    63  			ipList:       []string{"127.0.0.1", "10.0.0.13"},
    64  			sortedIPList: []string{"10.0.0.13", "127.0.0.1"},
    65  		},
    66  		// With multiple types of octet, chooses a higher octet.
    67  		{
    68  			ipList:       []string{"127.0.0.1", "172.0.21.1", "192.168.1.106"},
    69  			sortedIPList: []string{"192.168.1.106", "172.0.21.1", "127.0.0.1"},
    70  		},
    71  		// With different ip along with localhost.
    72  		{
    73  			ipList:       []string{"127.0.0.1", "192.168.1.106"},
    74  			sortedIPList: []string{"192.168.1.106", "127.0.0.1"},
    75  		},
    76  		// With a list of only one element nothing to sort.
    77  		{
    78  			ipList:       []string{"hostname"},
    79  			sortedIPList: []string{"hostname"},
    80  		},
    81  		// With a list of only one element nothing to sort.
    82  		{
    83  			ipList:       []string{"127.0.0.1"},
    84  			sortedIPList: []string{"127.0.0.1"},
    85  		},
    86  		// Non parsable ip is assumed to be hostame and gets preserved
    87  		// as the left most elements, regardless of IP based sorting.
    88  		{
    89  			ipList:       []string{"hostname", "127.0.0.1", "192.168.1.106"},
    90  			sortedIPList: []string{"hostname", "192.168.1.106", "127.0.0.1"},
    91  		},
    92  		// Non parsable ip is assumed to be hostname, with a mixed input of ip and hostname.
    93  		// gets preserved and moved into left most elements, regardless of
    94  		// IP based sorting.
    95  		{
    96  			ipList:       []string{"hostname1", "10.0.0.13", "hostname2", "127.0.0.1", "192.168.1.106"},
    97  			sortedIPList: []string{"hostname1", "hostname2", "192.168.1.106", "10.0.0.13", "127.0.0.1"},
    98  		},
    99  		// With same higher octets, preferentially move the localhost.
   100  		{
   101  			ipList:       []string{"127.0.0.1", "10.0.0.1", "192.168.0.1"},
   102  			sortedIPList: []string{"10.0.0.1", "192.168.0.1", "127.0.0.1"},
   103  		},
   104  	}
   105  	for i, testCase := range testCases {
   106  		gotIPList := sortIPs(testCase.ipList)
   107  		if !reflect.DeepEqual(testCase.sortedIPList, gotIPList) {
   108  			t.Errorf("Test %d: Expected %s, got %s", i+1, testCase.sortedIPList, gotIPList)
   109  		}
   110  	}
   111  }
   112  
   113  func TestMustGetLocalIP4(t *testing.T) {
   114  	testCases := []struct {
   115  		expectedIPList set.StringSet
   116  	}{
   117  		{set.CreateStringSet("127.0.0.1")},
   118  	}
   119  
   120  	for _, testCase := range testCases {
   121  		ipList := mustGetLocalIP4()
   122  		if testCase.expectedIPList != nil && testCase.expectedIPList.Intersection(ipList).IsEmpty() {
   123  			t.Fatalf("host: expected = %v, got = %v", testCase.expectedIPList, ipList)
   124  		}
   125  	}
   126  }
   127  
   128  func TestGetHostIP(t *testing.T) {
   129  	testCases := []struct {
   130  		host           string
   131  		expectedIPList set.StringSet
   132  		expectedErr    error
   133  	}{
   134  		{"localhost", set.CreateStringSet("127.0.0.1"), nil},
   135  		{"example.org", set.CreateStringSet("93.184.216.34"), nil},
   136  	}
   137  
   138  	for _, testCase := range testCases {
   139  		ipList, err := getHostIP(testCase.host)
   140  		if testCase.expectedErr == nil {
   141  			if err != nil {
   142  				t.Fatalf("error: expected = <nil>, got = %v", err)
   143  			}
   144  		} else if err == nil {
   145  			t.Fatalf("error: expected = %v, got = <nil>", testCase.expectedErr)
   146  		} else if testCase.expectedErr.Error() != err.Error() {
   147  			t.Fatalf("error: expected = %v, got = %v", testCase.expectedErr, err)
   148  		}
   149  
   150  		if testCase.expectedIPList != nil && testCase.expectedIPList.Intersection(ipList).IsEmpty() {
   151  			t.Fatalf("host: expected = %v, got = %v", testCase.expectedIPList, ipList)
   152  		}
   153  	}
   154  }
   155  
   156  // Tests finalize api endpoints.
   157  func TestGetAPIEndpoints(t *testing.T) {
   158  	host, port := globalMinioHost, globalMinioAddr
   159  	defer func() {
   160  		globalMinioHost, globalMinioAddr = host, port
   161  	}()
   162  	testCases := []struct {
   163  		host, port     string
   164  		expectedResult string
   165  	}{
   166  		{"", "80", "http://127.0.0.1:80"},
   167  		{"127.0.0.1", "80", "http://127.0.0.1:80"},
   168  		{"localhost", "80", "http://localhost:80"},
   169  	}
   170  
   171  	for i, testCase := range testCases {
   172  		globalMinioHost, globalMinioPort = testCase.host, testCase.port
   173  		apiEndpoints := getAPIEndpoints()
   174  		apiEndpointSet := set.CreateStringSet(apiEndpoints...)
   175  		if !apiEndpointSet.Contains(testCase.expectedResult) {
   176  			t.Fatalf("test %d: expected: Found, got: Not Found", i+1)
   177  		}
   178  	}
   179  }
   180  
   181  // Ask the kernel for a free open port.
   182  func getFreePort() string {
   183  	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
   184  	if err != nil {
   185  		panic(err)
   186  	}
   187  
   188  	l, err := net.ListenTCP("tcp", addr)
   189  	if err != nil {
   190  		panic(err)
   191  	}
   192  	defer l.Close()
   193  	return fmt.Sprintf("%d", l.Addr().(*net.TCPAddr).Port)
   194  }
   195  
   196  // Tests for port availability logic written for server startup sequence.
   197  func TestCheckPortAvailability(t *testing.T) {
   198  	// Make a port is not available.
   199  	port := getFreePort()
   200  	listener, err := net.Listen("tcp", net.JoinHostPort("", port))
   201  	if err != nil {
   202  		t.Fatalf("Unable to listen on port %v", port)
   203  	}
   204  	defer listener.Close()
   205  
   206  	testCases := []struct {
   207  		host        string
   208  		port        string
   209  		expectedErr error
   210  	}{
   211  		{"", port, fmt.Errorf("listen tcp :%v: bind: address already in use", port)},
   212  		{"127.0.0.1", port, fmt.Errorf("listen tcp 127.0.0.1:%v: bind: address already in use", port)},
   213  		{"", getFreePort(), nil},
   214  	}
   215  
   216  	for _, testCase := range testCases {
   217  		// On MS Windows and Mac, skip checking error case due to https://github.com/golang/go/issues/7598
   218  		if (runtime.GOOS == globalWindowsOSName || runtime.GOOS == globalMacOSName || runtime.GOOS == "solaris") && testCase.expectedErr != nil {
   219  			continue
   220  		}
   221  
   222  		err := checkPortAvailability(testCase.host, testCase.port)
   223  		if testCase.expectedErr == nil {
   224  			if err != nil {
   225  				t.Fatalf("error: expected = <nil>, got = %v", err)
   226  			}
   227  		} else if err == nil {
   228  			t.Fatalf("error: expected = %v, got = <nil>", testCase.expectedErr)
   229  		} else if testCase.expectedErr.Error() != err.Error() {
   230  			t.Fatalf("error: expected = %v, got = %v", testCase.expectedErr, err)
   231  		}
   232  	}
   233  }
   234  
   235  func TestCheckLocalServerAddr(t *testing.T) {
   236  	testCases := []struct {
   237  		serverAddr  string
   238  		expectedErr error
   239  	}{
   240  		{":54321", nil},
   241  		{"localhost:54321", nil},
   242  		{"0.0.0.0:9000", nil},
   243  		{":0", nil},
   244  		{"localhost", nil},
   245  		{"", fmt.Errorf("invalid argument")},
   246  		{"example.org:54321", fmt.Errorf("host in server address should be this server")},
   247  		{":-10", fmt.Errorf("port must be between 0 to 65535")},
   248  	}
   249  
   250  	for _, testCase := range testCases {
   251  		testCase := testCase
   252  		t.Run("", func(t *testing.T) {
   253  			err := CheckLocalServerAddr(testCase.serverAddr)
   254  			if testCase.expectedErr == nil {
   255  				if err != nil {
   256  					t.Errorf("error: expected = <nil>, got = %v", err)
   257  				}
   258  			} else if err == nil {
   259  				t.Errorf("error: expected = %v, got = <nil>", testCase.expectedErr)
   260  			} else if testCase.expectedErr.Error() != err.Error() {
   261  				t.Errorf("error: expected = %v, got = %v", testCase.expectedErr, err)
   262  			}
   263  		})
   264  	}
   265  }
   266  
   267  func TestExtractHostPort(t *testing.T) {
   268  	testCases := []struct {
   269  		addr        string
   270  		host        string
   271  		port        string
   272  		expectedErr error
   273  	}{
   274  		{"", "", "", errors.New("unable to process empty address")},
   275  		{"localhost:9000", "localhost", "9000", nil},
   276  		{"http://:9000/", "", "9000", nil},
   277  		{"http://8.8.8.8:9000/", "8.8.8.8", "9000", nil},
   278  		{"https://facebook.com:9000/", "facebook.com", "9000", nil},
   279  	}
   280  
   281  	for i, testCase := range testCases {
   282  		host, port, err := extractHostPort(testCase.addr)
   283  		if testCase.expectedErr == nil && err != nil {
   284  			t.Fatalf("Test %d: should succeed but failed with err: %v", i+1, err)
   285  		}
   286  		if testCase.expectedErr != nil && err == nil {
   287  			t.Fatalf("Test %d:, should fail but succeeded.", i+1)
   288  		}
   289  		if err == nil {
   290  			if host != testCase.host {
   291  				t.Fatalf("Test %d: expected: %v, found: %v", i+1, testCase.host, host)
   292  			}
   293  			if port != testCase.port {
   294  				t.Fatalf("Test %d: expected: %v, found: %v", i+1, testCase.port, port)
   295  			}
   296  		}
   297  		if testCase.expectedErr != nil && err != nil {
   298  			if testCase.expectedErr.Error() != err.Error() {
   299  				t.Fatalf("Test %d: failed with different error, expected: '%v', found:'%v'.", i+1, testCase.expectedErr, err)
   300  			}
   301  		}
   302  	}
   303  }
   304  
   305  func TestSameLocalAddrs(t *testing.T) {
   306  	testCases := []struct {
   307  		addr1       string
   308  		addr2       string
   309  		sameAddr    bool
   310  		expectedErr error
   311  	}{
   312  		{"", "", false, errors.New("unable to process empty address")},
   313  		{":9000", ":9000", true, nil},
   314  		{"localhost:9000", ":9000", true, nil},
   315  		{"localhost:9000", "http://localhost:9000", true, nil},
   316  		{"http://localhost:9000", ":9000", true, nil},
   317  		{"http://localhost:9000", "http://localhost:9000", true, nil},
   318  		{"http://8.8.8.8:9000", "http://localhost:9000", false, nil},
   319  	}
   320  
   321  	for _, testCase := range testCases {
   322  		testCase := testCase
   323  		t.Run("", func(t *testing.T) {
   324  			sameAddr, err := sameLocalAddrs(testCase.addr1, testCase.addr2)
   325  			if testCase.expectedErr != nil && err == nil {
   326  				t.Errorf("should fail but succeeded")
   327  			}
   328  			if testCase.expectedErr == nil && err != nil {
   329  				t.Errorf("should succeed but failed with %v", err)
   330  			}
   331  			if err == nil {
   332  				if sameAddr != testCase.sameAddr {
   333  					t.Errorf("expected: %v, found: %v", testCase.sameAddr, sameAddr)
   334  				}
   335  			} else {
   336  				if err.Error() != testCase.expectedErr.Error() {
   337  					t.Errorf("failed with different error, expected: '%v', found:'%v'.",
   338  						testCase.expectedErr, err)
   339  				}
   340  			}
   341  		})
   342  	}
   343  }
   344  func TestIsHostIP(t *testing.T) {
   345  	testCases := []struct {
   346  		args           string
   347  		expectedResult bool
   348  	}{
   349  		{"localhost", false},
   350  		{"localhost:9000", false},
   351  		{"example.com", false},
   352  		{"http://192.168.1.0", false},
   353  		{"http://192.168.1.0:9000", false},
   354  		{"192.168.1.0", true},
   355  		{"[2001:3984:3989::20%eth0]:9000", true},
   356  	}
   357  
   358  	for _, testCase := range testCases {
   359  		ret := isHostIP(testCase.args)
   360  		if testCase.expectedResult != ret {
   361  			t.Fatalf("expected: %v , got: %v", testCase.expectedResult, ret)
   362  		}
   363  	}
   364  }