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 }