storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/http/listener_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 http
    18  
    19  import (
    20  	"crypto/tls"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"net"
    25  	"strconv"
    26  	"strings"
    27  	"sync/atomic"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/minio/minio-go/v7/pkg/set"
    32  )
    33  
    34  var serverPort uint32 = 60000
    35  
    36  var getCert = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
    37  	certificate, err := getTLSCert()
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  	return &certificate, nil
    42  }
    43  
    44  func getTLSCert() (tls.Certificate, error) {
    45  	keyPEMBlock := []byte(`-----BEGIN RSA PRIVATE KEY-----
    46  MIIEpAIBAAKCAQEApEkbPrT6wzcWK1W5atQiGptvuBsRdf8MCg4u6SN10QbslA5k
    47  6BYRdZfFeRpwAwYyzkumug6+eBJatDZEd7+0FF86yxB7eMTSiHKRZ5Mi5ZyCFsez
    48  dndknGBeK6I80s1jd5ZsLLuMKErvbNwSbfX+X6d2mBeYW8Scv9N+qYnNrHHHohvX
    49  oxy1gZ18EhhogQhrD22zaqg/jtmOT8ImUiXzB1mKInt2LlSkoRYuBzepkDJrsE1L
    50  /cyYZbtcO/ASDj+/qQAuQ66v9pNyJkIQ7bDOUyxaT5Hx9XvbqI1OqUVAdGLLi+eZ
    51  IFguFyYd0lemwdN/IDvxftzegTO3cO0D28d1UQIDAQABAoIBAB42x8j3lerTNcOQ
    52  h4JLM157WcedSs/NsVQkGaKM//0KbfYo04wPivR6jjngj9suh6eDKE2tqoAAuCfO
    53  lzcCzca1YOW5yUuDv0iS8YT//XoHF7HC1pGiEaHk40zZEKCgX3u98XUkpPlAFtqJ
    54  euY4SKkk7l24cS/ncACjj/b0PhxJoT/CncuaaJKqmCc+vdL4wj1UcrSNPZqRjDR/
    55  sh5DO0LblB0XrqVjpNxqxM60/IkbftB8YTnyGgtO2tbTPr8KdQ8DhHQniOp+WEPV
    56  u/iXt0LLM7u62LzadkGab2NDWS3agnmdvw2ADtv5Tt8fZ7WnPqiOpNyD5Bv1a3/h
    57  YBw5HsUCgYEA0Sfv6BiSAFEby2KusRoq5UeUjp/SfL7vwpO1KvXeiYkPBh2XYVq2
    58  azMnOw7Rz5ixFhtUtto2XhYdyvvr3dZu1fNHtxWo9ITBivqTGGRNwfiaQa58Bugo
    59  gy7vCdIE/f6xE5LYIovBnES2vs/ZayMyhTX84SCWd0pTY0kdDA8ePGsCgYEAyRSA
    60  OTzX43KUR1G/trpuM6VBc0W6YUNYzGRa1TcUxBP4K7DfKMpPGg6ulqypfoHmu8QD
    61  L+z+iQmG9ySSuvScIW6u8LgkrTwZga8y2eb/A2FAVYY/bnelef1aMkis+bBX2OQ4
    62  QAg2uq+pkhpW1k5NSS9lVCPkj4e5Ur9RCm9fRDMCgYAf3CSIR03eLHy+Y37WzXSh
    63  TmELxL6sb+1Xx2Y+cAuBCda3CMTpeIb3F2ivb1d4dvrqsikaXW0Qse/B3tQUC7kA
    64  cDmJYwxEiwBsajUD7yuFE5hzzt9nse+R5BFXfp1yD1zr7V9tC7rnUfRAZqrozgjB
    65  D/NAW9VvwGupYRbCon7plwKBgQCRPfeoYGRoa9ji8w+Rg3QaZeGyy8jmfGjlqg9a
    66  NyEOyIXXuThYFFmyrqw5NZpwQJBTTDApK/xnK7SLS6WY2Rr1oydFxRzo7KJX5B7M
    67  +md1H4gCvqeOuWmThgbij1AyQsgRaDehOM2fZ0cKu2/B+Gkm1c9RSWPMsPKR7JMz
    68  AGNFtQKBgQCRCFIdGJHnvz35vJfLoihifCejBWtZbAnZoBHpF3xMCtV755J96tUf
    69  k1Tv9hz6WfSkOSlwLq6eGZY2dCENJRW1ft1UelpFvCjbfrfLvoFFLs3gu0lfqXHi
    70  CS6fjhn9Ahvz10yD6fd4ixRUjoJvULzI0Sxc1O95SYVF1lIAuVr9Hw==
    71  -----END RSA PRIVATE KEY-----`)
    72  	certPEMBlock := []byte(`-----BEGIN CERTIFICATE-----
    73  MIIDXTCCAkWgAwIBAgIJAKlqK5HKlo9MMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
    74  BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
    75  aWRnaXRzIFB0eSBMdGQwHhcNMTcwNjE5MTA0MzEyWhcNMjcwNjE3MTA0MzEyWjBF
    76  MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
    77  ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
    78  CgKCAQEApEkbPrT6wzcWK1W5atQiGptvuBsRdf8MCg4u6SN10QbslA5k6BYRdZfF
    79  eRpwAwYyzkumug6+eBJatDZEd7+0FF86yxB7eMTSiHKRZ5Mi5ZyCFsezdndknGBe
    80  K6I80s1jd5ZsLLuMKErvbNwSbfX+X6d2mBeYW8Scv9N+qYnNrHHHohvXoxy1gZ18
    81  EhhogQhrD22zaqg/jtmOT8ImUiXzB1mKInt2LlSkoRYuBzepkDJrsE1L/cyYZbtc
    82  O/ASDj+/qQAuQ66v9pNyJkIQ7bDOUyxaT5Hx9XvbqI1OqUVAdGLLi+eZIFguFyYd
    83  0lemwdN/IDvxftzegTO3cO0D28d1UQIDAQABo1AwTjAdBgNVHQ4EFgQUqMVdMIA1
    84  68Dv+iwGugAaEGUSd0IwHwYDVR0jBBgwFoAUqMVdMIA168Dv+iwGugAaEGUSd0Iw
    85  DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAjQVoqRv2HlE5PJIX/qk5
    86  oMOKZlHTyJP+s2HzOOVt+eCE/jNdfC7+8R/HcPldQs7p9GqH2F6hQ9aOtDhJVEaU
    87  pjxCi4qKeZ1kWwqv8UMBXW92eHGysBvE2Gmm/B1JFl8S2GR5fBmheZVnYW893MoI
    88  gp+bOoCcIuMJRqCra4vJgrOsQjgRElQvd2OlP8qQzInf/fRqO/AnZPwMkGr3+KZ0
    89  BKEOXtmSZaPs3xEsnvJd8wrTgA0NQK7v48E+gHSXzQtaHmOLqisRXlUOu2r1gNCJ
    90  rr3DRiUP6V/10CZ/ImeSJ72k69VuTw9vq2HzB4x6pqxF2X7JQSLUCS2wfNN13N0d
    91  9A==
    92  -----END CERTIFICATE-----`)
    93  
    94  	return tls.X509KeyPair(certPEMBlock, keyPEMBlock)
    95  }
    96  
    97  func getNextPort() string {
    98  	return strconv.Itoa(int(atomic.AddUint32(&serverPort, 1)))
    99  }
   100  
   101  func getNonLoopBackIP(t *testing.T) string {
   102  	localIP4 := set.NewStringSet()
   103  	addrs, err := net.InterfaceAddrs()
   104  	if err != nil {
   105  		t.Fatalf("%s.  Unable to get IP addresses of this host.", err)
   106  	}
   107  
   108  	for _, addr := range addrs {
   109  		var ip net.IP
   110  		switch v := addr.(type) {
   111  		case *net.IPNet:
   112  			ip = v.IP
   113  		case *net.IPAddr:
   114  			ip = v.IP
   115  		}
   116  
   117  		if ip.To4() != nil {
   118  			localIP4.Add(ip.String())
   119  		}
   120  	}
   121  
   122  	// Filter ipList by IPs those do not start with '127.'.
   123  	nonLoopBackIPs := localIP4.FuncMatch(func(ip string, matchString string) bool {
   124  		return !strings.HasPrefix(ip, "127.")
   125  	}, "")
   126  	if len(nonLoopBackIPs) == 0 {
   127  		t.Fatalf("No non-loop back IP address found for this host")
   128  	}
   129  	nonLoopBackIP := nonLoopBackIPs.ToSlice()[0]
   130  	return nonLoopBackIP
   131  }
   132  
   133  func TestNewHTTPListener(t *testing.T) {
   134  	testCases := []struct {
   135  		serverAddrs         []string
   136  		tcpKeepAliveTimeout time.Duration
   137  		readTimeout         time.Duration
   138  		writeTimeout        time.Duration
   139  		expectedErr         bool
   140  	}{
   141  		{[]string{"93.184.216.34:65432"}, time.Duration(0), time.Duration(0), time.Duration(0), true},
   142  		{[]string{"example.org:65432"}, time.Duration(0), time.Duration(0), time.Duration(0), true},
   143  		{[]string{"unknown-host"}, time.Duration(0), time.Duration(0), time.Duration(0), true},
   144  		{[]string{"unknown-host:65432"}, time.Duration(0), time.Duration(0), time.Duration(0), true},
   145  		{[]string{"localhost:65432", "93.184.216.34:65432"}, time.Duration(0), time.Duration(0), time.Duration(0), true},
   146  		{[]string{"localhost:65432", "unknown-host:65432"}, time.Duration(0), time.Duration(0), time.Duration(0), true},
   147  		{[]string{"localhost:0"}, time.Duration(0), time.Duration(0), time.Duration(0), false},
   148  		{[]string{"localhost:0"}, time.Duration(0), time.Duration(0), time.Duration(0), false},
   149  	}
   150  
   151  	for _, testCase := range testCases {
   152  		listener, err := newHTTPListener(
   153  			testCase.serverAddrs,
   154  		)
   155  
   156  		if !testCase.expectedErr {
   157  			if err != nil {
   158  				t.Fatalf("error: expected = <nil>, got = %v", err)
   159  			}
   160  		} else if err == nil {
   161  			t.Fatalf("error: expected = %v, got = <nil>", testCase.expectedErr)
   162  		}
   163  
   164  		if err == nil {
   165  			listener.Close()
   166  		}
   167  	}
   168  }
   169  
   170  func TestHTTPListenerStartClose(t *testing.T) {
   171  	nonLoopBackIP := getNonLoopBackIP(t)
   172  
   173  	testCases := []struct {
   174  		serverAddrs []string
   175  	}{
   176  		{[]string{"localhost:0"}},
   177  		{[]string{nonLoopBackIP + ":0"}},
   178  		{[]string{"127.0.0.1:0", nonLoopBackIP + ":0"}},
   179  		{[]string{"localhost:0"}},
   180  		{[]string{nonLoopBackIP + ":0"}},
   181  		{[]string{"127.0.0.1:0", nonLoopBackIP + ":0"}},
   182  	}
   183  
   184  	for i, testCase := range testCases {
   185  		listener, err := newHTTPListener(
   186  			testCase.serverAddrs,
   187  		)
   188  		if err != nil {
   189  			if strings.Contains(err.Error(), "The requested address is not valid in its context") {
   190  				// Ignore if IP is unbindable.
   191  				continue
   192  			}
   193  			t.Fatalf("Test %d: error: expected = <nil>, got = %v", i+1, err)
   194  		}
   195  
   196  		for _, serverAddr := range listener.Addrs() {
   197  			conn, err := net.Dial("tcp", serverAddr.String())
   198  			if err != nil {
   199  				t.Fatalf("Test %d: error: expected = <nil>, got = %v", i+1, err)
   200  			}
   201  			conn.Close()
   202  		}
   203  
   204  		listener.Close()
   205  	}
   206  }
   207  
   208  func TestHTTPListenerAddr(t *testing.T) {
   209  	nonLoopBackIP := getNonLoopBackIP(t)
   210  	var casePorts []string
   211  	for i := 0; i < 6; i++ {
   212  		casePorts = append(casePorts, getNextPort())
   213  	}
   214  
   215  	testCases := []struct {
   216  		serverAddrs  []string
   217  		expectedAddr string
   218  	}{
   219  		{[]string{"localhost:" + casePorts[0]}, "127.0.0.1:" + casePorts[0]},
   220  		{[]string{nonLoopBackIP + ":" + casePorts[1]}, nonLoopBackIP + ":" + casePorts[1]},
   221  		{[]string{"127.0.0.1:" + casePorts[2], nonLoopBackIP + ":" + casePorts[2]}, "0.0.0.0:" + casePorts[2]},
   222  		{[]string{"localhost:" + casePorts[3]}, "127.0.0.1:" + casePorts[3]},
   223  		{[]string{nonLoopBackIP + ":" + casePorts[4]}, nonLoopBackIP + ":" + casePorts[4]},
   224  		{[]string{"127.0.0.1:" + casePorts[5], nonLoopBackIP + ":" + casePorts[5]}, "0.0.0.0:" + casePorts[5]},
   225  	}
   226  
   227  	for i, testCase := range testCases {
   228  		listener, err := newHTTPListener(
   229  			testCase.serverAddrs,
   230  		)
   231  		if err != nil {
   232  			if strings.Contains(err.Error(), "The requested address is not valid in its context") {
   233  				// Ignore if IP is unbindable.
   234  				continue
   235  			}
   236  			t.Fatalf("Test %d: error: expected = <nil>, got = %v", i+1, err)
   237  		}
   238  
   239  		addr := listener.Addr()
   240  		if addr.String() != testCase.expectedAddr {
   241  			t.Fatalf("Test %d: addr: expected = %v, got = %v", i+1, testCase.expectedAddr, addr)
   242  		}
   243  
   244  		listener.Close()
   245  	}
   246  }
   247  
   248  func TestHTTPListenerAddrs(t *testing.T) {
   249  	nonLoopBackIP := getNonLoopBackIP(t)
   250  	var casePorts []string
   251  	for i := 0; i < 6; i++ {
   252  		casePorts = append(casePorts, getNextPort())
   253  	}
   254  
   255  	testCases := []struct {
   256  		serverAddrs   []string
   257  		expectedAddrs set.StringSet
   258  	}{
   259  		{[]string{"localhost:" + casePorts[0]}, set.CreateStringSet("127.0.0.1:" + casePorts[0])},
   260  		{[]string{nonLoopBackIP + ":" + casePorts[1]}, set.CreateStringSet(nonLoopBackIP + ":" + casePorts[1])},
   261  		{[]string{"127.0.0.1:" + casePorts[2], nonLoopBackIP + ":" + casePorts[2]}, set.CreateStringSet("127.0.0.1:"+casePorts[2], nonLoopBackIP+":"+casePorts[2])},
   262  		{[]string{"localhost:" + casePorts[3]}, set.CreateStringSet("127.0.0.1:" + casePorts[3])},
   263  		{[]string{nonLoopBackIP + ":" + casePorts[4]}, set.CreateStringSet(nonLoopBackIP + ":" + casePorts[4])},
   264  		{[]string{"127.0.0.1:" + casePorts[5], nonLoopBackIP + ":" + casePorts[5]}, set.CreateStringSet("127.0.0.1:"+casePorts[5], nonLoopBackIP+":"+casePorts[5])},
   265  	}
   266  
   267  	for i, testCase := range testCases {
   268  		listener, err := newHTTPListener(
   269  			testCase.serverAddrs,
   270  		)
   271  		if err != nil {
   272  			if strings.Contains(err.Error(), "The requested address is not valid in its context") {
   273  				// Ignore if IP is unbindable.
   274  				continue
   275  			}
   276  			t.Fatalf("Test %d: error: expected = <nil>, got = %v", i+1, err)
   277  		}
   278  
   279  		addrs := listener.Addrs()
   280  		addrSet := set.NewStringSet()
   281  		for _, addr := range addrs {
   282  			addrSet.Add(addr.String())
   283  		}
   284  
   285  		if !addrSet.Equals(testCase.expectedAddrs) {
   286  			t.Fatalf("Test %d: addr: expected = %v, got = %v", i+1, testCase.expectedAddrs, addrs)
   287  		}
   288  
   289  		listener.Close()
   290  	}
   291  }
   292  
   293  type myTimeoutErr struct {
   294  	timeout bool
   295  }
   296  
   297  func (m *myTimeoutErr) Error() string { return fmt.Sprintf("myTimeoutErr: %v", m.timeout) }
   298  func (m *myTimeoutErr) Timeout() bool { return m.timeout }
   299  
   300  // Test for ignoreErr helper function
   301  func TestIgnoreErr(t *testing.T) {
   302  	testCases := []struct {
   303  		err  error
   304  		want bool
   305  	}{
   306  		{
   307  			err:  io.EOF,
   308  			want: true,
   309  		},
   310  		{
   311  			err:  &net.OpError{Err: &myTimeoutErr{timeout: true}},
   312  			want: true,
   313  		},
   314  		{
   315  			err:  errors.New("EOF"),
   316  			want: true,
   317  		},
   318  		{
   319  			err:  &net.OpError{Err: &myTimeoutErr{timeout: false}},
   320  			want: false,
   321  		},
   322  		{
   323  			err:  io.ErrUnexpectedEOF,
   324  			want: false,
   325  		},
   326  		{
   327  			err:  nil,
   328  			want: false,
   329  		},
   330  	}
   331  
   332  	for i, tc := range testCases {
   333  		if actual := isRoutineNetErr(tc.err); actual != tc.want {
   334  			t.Errorf("Test case %d: Expected %v but got %v for %v", i+1,
   335  				tc.want, actual, tc.err)
   336  		}
   337  	}
   338  }