github.com/minio/madmin-go/v2@v2.2.1/utils.go (about) 1 // 2 // Copyright (c) 2015-2022 MinIO, Inc. 3 // 4 // This file is part of MinIO Object Storage stack 5 // 6 // This program is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU Affero General Public License as 8 // published by the Free Software Foundation, either version 3 of the 9 // License, or (at your option) any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU Affero General Public License for more details. 15 // 16 // You should have received a copy of the GNU Affero General Public License 17 // along with this program. If not, see <http://www.gnu.org/licenses/>. 18 // 19 20 package madmin 21 22 import ( 23 "io" 24 "io/ioutil" 25 "net" 26 "net/http" 27 "net/url" 28 "strings" 29 30 "github.com/minio/minio-go/v7/pkg/s3utils" 31 ) 32 33 // AdminAPIVersion - admin api version used in the request. 34 const ( 35 AdminAPIVersion = "v3" 36 AdminAPIVersionV2 = "v2" 37 adminAPIPrefix = "/" + AdminAPIVersion 38 kmsAPIVersion = "v1" 39 kmsAPIPrefix = "/" + kmsAPIVersion 40 ) 41 42 // getEndpointURL - construct a new endpoint. 43 func getEndpointURL(endpoint string, secure bool) (*url.URL, error) { 44 if strings.Contains(endpoint, ":") { 45 host, _, err := net.SplitHostPort(endpoint) 46 if err != nil { 47 return nil, err 48 } 49 if !s3utils.IsValidIP(host) && !s3utils.IsValidDomain(host) { 50 msg := "Endpoint: " + endpoint + " does not follow ip address or domain name standards." 51 return nil, ErrInvalidArgument(msg) 52 } 53 } else { 54 if !s3utils.IsValidIP(endpoint) && !s3utils.IsValidDomain(endpoint) { 55 msg := "Endpoint: " + endpoint + " does not follow ip address or domain name standards." 56 return nil, ErrInvalidArgument(msg) 57 } 58 } 59 60 // If secure is false, use 'http' scheme. 61 scheme := "https" 62 if !secure { 63 scheme = "http" 64 } 65 66 // Strip the obvious :443 and :80 from the endpoint 67 // to avoid the signature mismatch error. 68 if secure && strings.HasSuffix(endpoint, ":443") { 69 endpoint = strings.TrimSuffix(endpoint, ":443") 70 } 71 if !secure && strings.HasSuffix(endpoint, ":80") { 72 endpoint = strings.TrimSuffix(endpoint, ":80") 73 } 74 75 // Construct a secured endpoint URL. 76 endpointURLStr := scheme + "://" + endpoint 77 endpointURL, err := url.Parse(endpointURLStr) 78 if err != nil { 79 return nil, err 80 } 81 82 // Validate incoming endpoint URL. 83 if err := isValidEndpointURL(endpointURL.String()); err != nil { 84 return nil, err 85 } 86 return endpointURL, nil 87 } 88 89 // Verify if input endpoint URL is valid. 90 func isValidEndpointURL(endpointURL string) error { 91 if endpointURL == "" { 92 return ErrInvalidArgument("Endpoint url cannot be empty.") 93 } 94 url, err := url.Parse(endpointURL) 95 if err != nil { 96 return ErrInvalidArgument("Endpoint url cannot be parsed.") 97 } 98 if url.Path != "/" && url.Path != "" { 99 return ErrInvalidArgument("Endpoint url cannot have fully qualified paths.") 100 } 101 return nil 102 } 103 104 // closeResponse close non nil response with any response Body. 105 // convenient wrapper to drain any remaining data on response body. 106 // 107 // Subsequently this allows golang http RoundTripper 108 // to re-use the same connection for future requests. 109 func closeResponse(resp *http.Response) { 110 // Callers should close resp.Body when done reading from it. 111 // If resp.Body is not closed, the Client's underlying RoundTripper 112 // (typically Transport) may not be able to re-use a persistent TCP 113 // connection to the server for a subsequent "keep-alive" request. 114 if resp != nil && resp.Body != nil { 115 // Drain any remaining Body and then close the connection. 116 // Without this closing connection would disallow re-using 117 // the same connection for future uses. 118 // - http://stackoverflow.com/a/17961593/4465767 119 io.Copy(ioutil.Discard, resp.Body) 120 resp.Body.Close() 121 } 122 }