storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/madmin/utils.go (about)

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