github.com/minio/madmin-go@v1.7.5/utils.go (about)

     1  //
     2  // MinIO Object Storage (c) 2021 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 madmin
    18  
    19  import (
    20  	"io"
    21  	"io/ioutil"
    22  	"net"
    23  	"net/http"
    24  	"net/url"
    25  	"strings"
    26  
    27  	"github.com/minio/minio-go/v7/pkg/s3utils"
    28  )
    29  
    30  // AdminAPIVersion - admin api version used in the request.
    31  const (
    32  	AdminAPIVersion   = "v3"
    33  	AdminAPIVersionV2 = "v2"
    34  	adminAPIPrefix    = "/" + AdminAPIVersion
    35  	kmsAPIVersion     = "v1"
    36  	kmsAPIPrefix      = "/" + kmsAPIVersion
    37  )
    38  
    39  // getEndpointURL - construct a new endpoint.
    40  func getEndpointURL(endpoint string, secure bool) (*url.URL, error) {
    41  	if strings.Contains(endpoint, ":") {
    42  		host, _, err := net.SplitHostPort(endpoint)
    43  		if err != nil {
    44  			return nil, err
    45  		}
    46  		if !s3utils.IsValidIP(host) && !s3utils.IsValidDomain(host) {
    47  			msg := "Endpoint: " + endpoint + " does not follow ip address or domain name standards."
    48  			return nil, ErrInvalidArgument(msg)
    49  		}
    50  	} else {
    51  		if !s3utils.IsValidIP(endpoint) && !s3utils.IsValidDomain(endpoint) {
    52  			msg := "Endpoint: " + endpoint + " does not follow ip address or domain name standards."
    53  			return nil, ErrInvalidArgument(msg)
    54  		}
    55  	}
    56  
    57  	// If secure is false, use 'http' scheme.
    58  	scheme := "https"
    59  	if !secure {
    60  		scheme = "http"
    61  	}
    62  
    63  	// Strip the obvious :443 and :80 from the endpoint
    64  	// to avoid the signature mismatch error.
    65  	if secure && strings.HasSuffix(endpoint, ":443") {
    66  		endpoint = strings.TrimSuffix(endpoint, ":443")
    67  	}
    68  	if !secure && strings.HasSuffix(endpoint, ":80") {
    69  		endpoint = strings.TrimSuffix(endpoint, ":80")
    70  	}
    71  
    72  	// Construct a secured endpoint URL.
    73  	endpointURLStr := scheme + "://" + endpoint
    74  	endpointURL, err := url.Parse(endpointURLStr)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	// Validate incoming endpoint URL.
    80  	if err := isValidEndpointURL(endpointURL.String()); err != nil {
    81  		return nil, err
    82  	}
    83  	return endpointURL, nil
    84  }
    85  
    86  // Verify if input endpoint URL is valid.
    87  func isValidEndpointURL(endpointURL string) error {
    88  	if endpointURL == "" {
    89  		return ErrInvalidArgument("Endpoint url cannot be empty.")
    90  	}
    91  	url, err := url.Parse(endpointURL)
    92  	if err != nil {
    93  		return ErrInvalidArgument("Endpoint url cannot be parsed.")
    94  	}
    95  	if url.Path != "/" && url.Path != "" {
    96  		return ErrInvalidArgument("Endpoint url cannot have fully qualified paths.")
    97  	}
    98  	return nil
    99  }
   100  
   101  // closeResponse close non nil response with any response Body.
   102  // convenient wrapper to drain any remaining data on response body.
   103  //
   104  // Subsequently this allows golang http RoundTripper
   105  // to re-use the same connection for future requests.
   106  func closeResponse(resp *http.Response) {
   107  	// Callers should close resp.Body when done reading from it.
   108  	// If resp.Body is not closed, the Client's underlying RoundTripper
   109  	// (typically Transport) may not be able to re-use a persistent TCP
   110  	// connection to the server for a subsequent "keep-alive" request.
   111  	if resp != nil && resp.Body != nil {
   112  		// Drain any remaining Body and then close the connection.
   113  		// Without this closing connection would disallow re-using
   114  		// the same connection for future uses.
   115  		//  - http://stackoverflow.com/a/17961593/4465767
   116  		io.Copy(ioutil.Discard, resp.Body)
   117  		resp.Body.Close()
   118  	}
   119  }