| 
									
										
										
										
											2015-04-29 17:19:51 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2020-08-05 05:55:53 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2015-2020 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2015-04-29 17:19:51 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2015-04-23 07:28:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2017-05-31 15:11:06 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-03-15 03:01:47 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2017-12-06 09:58:09 +08:00
										 |  |  | 	"crypto/tls" | 
					
						
							| 
									
										
										
										
											2015-04-23 07:28:13 +08:00
										 |  |  | 	"encoding/base64" | 
					
						
							| 
									
										
										
										
											2017-04-08 05:37:32 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2016-04-21 08:35:38 +08:00
										 |  |  | 	"encoding/xml" | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2016-08-26 15:11:53 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2016-04-21 08:35:38 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2018-09-06 07:47:14 +08:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2016-09-10 00:38:07 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2017-10-13 18:01:15 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2017-12-29 01:32:48 +08:00
										 |  |  | 	"reflect" | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 	"runtime" | 
					
						
							|  |  |  | 	"runtime/pprof" | 
					
						
							|  |  |  | 	"runtime/trace" | 
					
						
							| 
									
										
										
										
											2016-04-21 08:35:38 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2016-09-02 11:13:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-11 00:02:29 +08:00
										 |  |  | 	humanize "github.com/dustin/go-humanize" | 
					
						
							|  |  |  | 	"github.com/gorilla/mux" | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 	xhttp "github.com/minio/minio/cmd/http" | 
					
						
							| 
									
										
										
										
											2018-03-15 09:36:54 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2018-07-03 05:40:18 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/handlers" | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/madmin" | 
					
						
							| 
									
										
										
										
											2020-08-11 00:02:29 +08:00
										 |  |  | 	"golang.org/x/net/http2" | 
					
						
							| 
									
										
										
										
											2015-04-23 07:28:13 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | const ( | 
					
						
							|  |  |  | 	slashSeparator = "/" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-11 00:36:37 +08:00
										 |  |  | // IsErrIgnored returns whether given error is ignored or not.
 | 
					
						
							|  |  |  | func IsErrIgnored(err error, ignoredErrs ...error) bool { | 
					
						
							|  |  |  | 	return IsErr(err, ignoredErrs...) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IsErr returns whether given error is exact error.
 | 
					
						
							|  |  |  | func IsErr(err error, errs ...error) bool { | 
					
						
							|  |  |  | 	for _, exactErr := range errs { | 
					
						
							| 
									
										
										
										
											2019-09-12 01:21:43 +08:00
										 |  |  | 		if errors.Is(err, exactErr) { | 
					
						
							| 
									
										
										
										
											2018-04-11 00:36:37 +08:00
										 |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-27 09:21:54 +08:00
										 |  |  | func request2BucketObjectName(r *http.Request) (bucketName, objectName string) { | 
					
						
							|  |  |  | 	path, err := getResource(r.URL.Path, r.Host, globalDomainNames) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-04-10 00:30:02 +08:00
										 |  |  | 		logger.CriticalIf(GlobalContext, err) | 
					
						
							| 
									
										
										
										
											2019-06-27 09:21:54 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-22 06:07:49 +08:00
										 |  |  | 	return path2BucketObject(path) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-01-11 03:01:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-22 06:07:49 +08:00
										 |  |  | // path2BucketObjectWithBasePath returns bucket and prefix, if any,
 | 
					
						
							|  |  |  | // of a 'path'. basePath is trimmed from the front of the 'path'.
 | 
					
						
							|  |  |  | func path2BucketObjectWithBasePath(basePath, path string) (bucket, prefix string) { | 
					
						
							|  |  |  | 	path = strings.TrimPrefix(path, basePath) | 
					
						
							|  |  |  | 	path = strings.TrimPrefix(path, SlashSeparator) | 
					
						
							|  |  |  | 	m := strings.Index(path, SlashSeparator) | 
					
						
							|  |  |  | 	if m < 0 { | 
					
						
							|  |  |  | 		return path, "" | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-22 06:07:49 +08:00
										 |  |  | 	return path[:m], path[m+len(SlashSeparator):] | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-01-11 03:01:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-22 06:07:49 +08:00
										 |  |  | func path2BucketObject(s string) (bucket, prefix string) { | 
					
						
							|  |  |  | 	return path2BucketObjectWithBasePath("", s) | 
					
						
							| 
									
										
										
										
											2017-01-11 03:01:23 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 00:32:16 +08:00
										 |  |  | func getDefaultParityBlocks(drive int) int { | 
					
						
							|  |  |  | 	return drive / 2 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func getDefaultDataBlocks(drive int) int { | 
					
						
							|  |  |  | 	return drive - getDefaultParityBlocks(drive) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func getReadQuorum(drive int) int { | 
					
						
							|  |  |  | 	return getDefaultDataBlocks(drive) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func getWriteQuorum(drive int) int { | 
					
						
							|  |  |  | 	return getDefaultDataBlocks(drive) + 1 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  | // URI scheme constants.
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	httpScheme  = "http" | 
					
						
							|  |  |  | 	httpsScheme = "https" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-14 04:09:50 +08:00
										 |  |  | // nopCharsetConverter is a dummy charset convert which just copies input to output,
 | 
					
						
							|  |  |  | // it is used to ignore custom encoding charset in S3 XML body.
 | 
					
						
							|  |  |  | func nopCharsetConverter(label string, input io.Reader) (io.Reader, error) { | 
					
						
							|  |  |  | 	return input, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-21 08:35:38 +08:00
										 |  |  | // xmlDecoder provide decoded value in xml.
 | 
					
						
							| 
									
										
										
										
											2016-07-19 12:20:17 +08:00
										 |  |  | func xmlDecoder(body io.Reader, v interface{}, size int64) error { | 
					
						
							|  |  |  | 	var lbody io.Reader | 
					
						
							|  |  |  | 	if size > 0 { | 
					
						
							|  |  |  | 		lbody = io.LimitReader(body, size) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		lbody = body | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	d := xml.NewDecoder(lbody) | 
					
						
							| 
									
										
										
										
											2018-12-14 04:09:50 +08:00
										 |  |  | 	// Ignore any encoding set in the XML body
 | 
					
						
							|  |  |  | 	d.CharsetReader = nopCharsetConverter | 
					
						
							| 
									
										
										
										
											2016-04-21 08:35:38 +08:00
										 |  |  | 	return d.Decode(v) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-13 08:08:15 +08:00
										 |  |  | // checkValidMD5 - verify if valid md5, returns md5 in bytes.
 | 
					
						
							| 
									
										
										
										
											2018-03-17 02:22:34 +08:00
										 |  |  | func checkValidMD5(h http.Header) ([]byte, error) { | 
					
						
							| 
									
										
										
										
											2020-02-16 14:07:52 +08:00
										 |  |  | 	md5B64, ok := h[xhttp.ContentMD5] | 
					
						
							| 
									
										
										
										
											2018-03-17 02:22:34 +08:00
										 |  |  | 	if ok { | 
					
						
							|  |  |  | 		if md5B64[0] == "" { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("Content-Md5 header set to empty value") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-02-16 14:07:52 +08:00
										 |  |  | 		return base64.StdEncoding.Strict().DecodeString(md5B64[0]) | 
					
						
							| 
									
										
										
										
											2018-03-17 02:22:34 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return []byte{}, nil | 
					
						
							| 
									
										
										
										
											2015-04-23 07:28:13 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-04-29 17:19:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-04 23:04:12 +08:00
										 |  |  | // hasContentMD5 returns true if Content-MD5 header is set.
 | 
					
						
							|  |  |  | func hasContentMD5(h http.Header) bool { | 
					
						
							|  |  |  | 	_, ok := h[xhttp.ContentMD5] | 
					
						
							|  |  |  | 	return ok | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-30 01:51:59 +08:00
										 |  |  | /// http://docs.aws.amazon.com/AmazonS3/latest/dev/UploadingObjects.html
 | 
					
						
							| 
									
										
										
										
											2015-04-29 17:19:51 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2017-11-28 15:29:02 +08:00
										 |  |  | 	// Maximum object size per PUT request is 5TB.
 | 
					
						
							| 
									
										
										
										
											2017-03-04 02:14:17 +08:00
										 |  |  | 	// This is a divergence from S3 limit on purpose to support
 | 
					
						
							|  |  |  | 	// use cases where users are going to upload large files
 | 
					
						
							|  |  |  | 	// using 'curl' and presigned URL.
 | 
					
						
							| 
									
										
										
										
											2017-11-28 15:29:02 +08:00
										 |  |  | 	globalMaxObjectSize = 5 * humanize.TiByte | 
					
						
							| 
									
										
										
										
											2017-03-04 02:14:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Minimum Part size for multipart upload is 5MiB
 | 
					
						
							|  |  |  | 	globalMinPartSize = 5 * humanize.MiByte | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Maximum Part size for multipart upload is 5GiB
 | 
					
						
							|  |  |  | 	globalMaxPartSize = 5 * humanize.GiByte | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Maximum Part ID for multipart upload is 10000
 | 
					
						
							|  |  |  | 	// (Acceptable values range from 1 to 10000 inclusive)
 | 
					
						
							|  |  |  | 	globalMaxPartID = 10000 | 
					
						
							| 
									
										
										
										
											2018-07-13 05:12:40 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-05 05:55:53 +08:00
										 |  |  | 	// Default values used while communicating for gateway communication
 | 
					
						
							| 
									
										
										
										
											2020-04-18 02:20:56 +08:00
										 |  |  | 	defaultDialTimeout = 5 * time.Second | 
					
						
							| 
									
										
										
										
											2015-04-29 17:19:51 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // isMaxObjectSize - verify if max object size
 | 
					
						
							| 
									
										
										
										
											2015-12-28 15:00:36 +08:00
										 |  |  | func isMaxObjectSize(size int64) bool { | 
					
						
							| 
									
										
										
										
											2017-03-04 02:14:17 +08:00
										 |  |  | 	return size > globalMaxObjectSize | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // // Check if part size is more than maximum allowed size.
 | 
					
						
							|  |  |  | func isMaxAllowedPartSize(size int64) bool { | 
					
						
							|  |  |  | 	return size > globalMaxPartSize | 
					
						
							| 
									
										
										
										
											2015-04-29 17:19:51 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-02-05 19:09:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-09 03:06:05 +08:00
										 |  |  | // Check if part size is more than or equal to minimum allowed size.
 | 
					
						
							|  |  |  | func isMinAllowedPartSize(size int64) bool { | 
					
						
							| 
									
										
										
										
											2017-03-04 02:14:17 +08:00
										 |  |  | 	return size >= globalMinPartSize | 
					
						
							| 
									
										
										
										
											2016-05-09 03:06:05 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-24 16:52:47 +08:00
										 |  |  | // isMaxPartNumber - Check if part ID is greater than the maximum allowed ID.
 | 
					
						
							|  |  |  | func isMaxPartID(partID int) bool { | 
					
						
							| 
									
										
										
										
											2017-03-04 02:14:17 +08:00
										 |  |  | 	return partID > globalMaxPartID | 
					
						
							| 
									
										
										
										
											2016-05-24 16:52:47 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-29 01:32:48 +08:00
										 |  |  | func contains(slice interface{}, elem interface{}) bool { | 
					
						
							|  |  |  | 	v := reflect.ValueOf(slice) | 
					
						
							|  |  |  | 	if v.Kind() == reflect.Slice { | 
					
						
							|  |  |  | 		for i := 0; i < v.Len(); i++ { | 
					
						
							|  |  |  | 			if v.Index(i).Interface() == elem { | 
					
						
							|  |  |  | 				return true | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-02-05 19:09:31 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-08-06 04:48:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | // profilerWrapper is created becauses pkg/profiler doesn't
 | 
					
						
							|  |  |  | // provide any API to calculate the profiler file path in the
 | 
					
						
							|  |  |  | // disk since the name of this latter is randomly generated.
 | 
					
						
							|  |  |  | type profilerWrapper struct { | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 	// Profile recorded at start of benchmark.
 | 
					
						
							|  |  |  | 	base   []byte | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 	stopFn func() ([]byte, error) | 
					
						
							| 
									
										
										
										
											2020-03-04 22:58:12 +08:00
										 |  |  | 	ext    string | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | // recordBase will record the profile and store it as the base.
 | 
					
						
							| 
									
										
										
										
											2020-03-04 22:58:12 +08:00
										 |  |  | func (p *profilerWrapper) recordBase(name string, debug int) { | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 	var buf bytes.Buffer | 
					
						
							|  |  |  | 	p.base = nil | 
					
						
							| 
									
										
										
										
											2020-03-04 22:58:12 +08:00
										 |  |  | 	err := pprof.Lookup(name).WriteTo(&buf, debug) | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	p.base = buf.Bytes() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Base returns the recorded base if any.
 | 
					
						
							|  |  |  | func (p profilerWrapper) Base() []byte { | 
					
						
							|  |  |  | 	return p.base | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Stop the currently running benchmark.
 | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | func (p profilerWrapper) Stop() ([]byte, error) { | 
					
						
							|  |  |  | 	return p.stopFn() | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-04 22:58:12 +08:00
										 |  |  | // Extension returns the extension without dot prefix.
 | 
					
						
							|  |  |  | func (p profilerWrapper) Extension() string { | 
					
						
							|  |  |  | 	return p.ext | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | // Returns current profile data, returns error if there is no active
 | 
					
						
							|  |  |  | // profiling in progress. Stops an active profile.
 | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | func getProfileData() (map[string][]byte, error) { | 
					
						
							|  |  |  | 	globalProfilerMu.Lock() | 
					
						
							|  |  |  | 	defer globalProfilerMu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(globalProfiler) == 0 { | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 		return nil, errors.New("profiler not enabled") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 	dst := make(map[string][]byte, len(globalProfiler)) | 
					
						
							|  |  |  | 	for typ, prof := range globalProfiler { | 
					
						
							|  |  |  | 		// Stop the profiler
 | 
					
						
							|  |  |  | 		var err error | 
					
						
							|  |  |  | 		buf, err := prof.Stop() | 
					
						
							|  |  |  | 		delete(globalProfiler, typ) | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							| 
									
										
										
										
											2020-03-04 22:58:12 +08:00
										 |  |  | 			dst[typ+"."+prof.Extension()] = buf | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 		buf = prof.Base() | 
					
						
							|  |  |  | 		if len(buf) > 0 { | 
					
						
							| 
									
										
										
										
											2020-03-04 22:58:12 +08:00
										 |  |  | 			dst[typ+"-before"+"."+prof.Extension()] = buf | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 	return dst, nil | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | func setDefaultProfilerRates() { | 
					
						
							|  |  |  | 	runtime.MemProfileRate = 4096      // 512K -> 4K - Must be constant throughout application lifetime.
 | 
					
						
							|  |  |  | 	runtime.SetMutexProfileFraction(0) // Disable until needed
 | 
					
						
							|  |  |  | 	runtime.SetBlockProfileRate(0)     // Disable until needed
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-02 11:13:11 +08:00
										 |  |  | // Starts a profiler returns nil if profiler is not enabled, caller needs to handle this.
 | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | func startProfiler(profilerType string) (minioProfiler, error) { | 
					
						
							|  |  |  | 	var prof profilerWrapper | 
					
						
							| 
									
										
										
										
											2020-03-04 22:58:12 +08:00
										 |  |  | 	prof.ext = "pprof" | 
					
						
							| 
									
										
										
										
											2018-10-16 02:13:19 +08:00
										 |  |  | 	// Enable profiler and set the name of the file that pkg/pprof
 | 
					
						
							|  |  |  | 	// library creates to store profiling data.
 | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 	switch madmin.ProfilerType(profilerType) { | 
					
						
							|  |  |  | 	case madmin.ProfilerCPU: | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 		dirPath, err := ioutil.TempDir("", "profile") | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		fn := filepath.Join(dirPath, "cpu.out") | 
					
						
							|  |  |  | 		f, err := os.Create(fn) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		err = pprof.StartCPUProfile(f) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		prof.stopFn = func() ([]byte, error) { | 
					
						
							|  |  |  | 			pprof.StopCPUProfile() | 
					
						
							|  |  |  | 			err := f.Close() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			defer os.RemoveAll(dirPath) | 
					
						
							|  |  |  | 			return ioutil.ReadFile(fn) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 	case madmin.ProfilerMEM: | 
					
						
							|  |  |  | 		runtime.GC() | 
					
						
							| 
									
										
										
										
											2020-03-04 22:58:12 +08:00
										 |  |  | 		prof.recordBase("heap", 0) | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 		prof.stopFn = func() ([]byte, error) { | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 			runtime.GC() | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 			var buf bytes.Buffer | 
					
						
							|  |  |  | 			err := pprof.Lookup("heap").WriteTo(&buf, 0) | 
					
						
							|  |  |  | 			return buf.Bytes(), err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 	case madmin.ProfilerBlock: | 
					
						
							| 
									
										
										
										
											2020-03-04 22:58:12 +08:00
										 |  |  | 		prof.recordBase("block", 0) | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 		runtime.SetBlockProfileRate(1) | 
					
						
							|  |  |  | 		prof.stopFn = func() ([]byte, error) { | 
					
						
							|  |  |  | 			var buf bytes.Buffer | 
					
						
							|  |  |  | 			err := pprof.Lookup("block").WriteTo(&buf, 0) | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 			runtime.SetBlockProfileRate(0) | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 			return buf.Bytes(), err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 	case madmin.ProfilerMutex: | 
					
						
							| 
									
										
										
										
											2020-03-04 22:58:12 +08:00
										 |  |  | 		prof.recordBase("mutex", 0) | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 		runtime.SetMutexProfileFraction(1) | 
					
						
							|  |  |  | 		prof.stopFn = func() ([]byte, error) { | 
					
						
							|  |  |  | 			var buf bytes.Buffer | 
					
						
							|  |  |  | 			err := pprof.Lookup("mutex").WriteTo(&buf, 0) | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 			runtime.SetMutexProfileFraction(0) | 
					
						
							|  |  |  | 			return buf.Bytes(), err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case madmin.ProfilerThreads: | 
					
						
							| 
									
										
										
										
											2020-03-04 22:58:12 +08:00
										 |  |  | 		prof.recordBase("threadcreate", 0) | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 		prof.stopFn = func() ([]byte, error) { | 
					
						
							|  |  |  | 			var buf bytes.Buffer | 
					
						
							|  |  |  | 			err := pprof.Lookup("threadcreate").WriteTo(&buf, 0) | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 			return buf.Bytes(), err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-03-04 22:58:12 +08:00
										 |  |  | 	case madmin.ProfilerGoroutines: | 
					
						
							|  |  |  | 		prof.ext = "txt" | 
					
						
							| 
									
										
										
										
											2020-03-07 05:22:47 +08:00
										 |  |  | 		prof.recordBase("goroutine", 1) | 
					
						
							| 
									
										
										
										
											2020-03-04 22:58:12 +08:00
										 |  |  | 		prof.stopFn = func() ([]byte, error) { | 
					
						
							|  |  |  | 			var buf bytes.Buffer | 
					
						
							| 
									
										
										
										
											2020-03-07 05:22:47 +08:00
										 |  |  | 			err := pprof.Lookup("goroutine").WriteTo(&buf, 1) | 
					
						
							| 
									
										
										
										
											2020-03-04 22:58:12 +08:00
										 |  |  | 			return buf.Bytes(), err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 	case madmin.ProfilerTrace: | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 		dirPath, err := ioutil.TempDir("", "profile") | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		fn := filepath.Join(dirPath, "trace.out") | 
					
						
							|  |  |  | 		f, err := os.Create(fn) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		err = trace.Start(f) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-03-04 22:58:12 +08:00
										 |  |  | 		prof.ext = "trace" | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 		prof.stopFn = func() ([]byte, error) { | 
					
						
							|  |  |  | 			trace.Stop() | 
					
						
							|  |  |  | 			err := f.Close() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			defer os.RemoveAll(dirPath) | 
					
						
							|  |  |  | 			return ioutil.ReadFile(fn) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-09-02 11:13:11 +08:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | 		return nil, errors.New("profiler type unknown") | 
					
						
							| 
									
										
										
										
											2016-09-02 11:13:11 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 	return prof, nil | 
					
						
							| 
									
										
										
										
											2016-09-02 11:13:11 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-13 20:59:36 +08:00
										 |  |  | // minioProfiler - minio profiler interface.
 | 
					
						
							|  |  |  | type minioProfiler interface { | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 	// Return base profile. 'nil' if none.
 | 
					
						
							|  |  |  | 	Base() []byte | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | 	// Stop the profiler
 | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 	Stop() ([]byte, error) | 
					
						
							| 
									
										
										
										
											2020-03-04 22:58:12 +08:00
										 |  |  | 	// Return extension of profile
 | 
					
						
							|  |  |  | 	Extension() string | 
					
						
							| 
									
										
										
										
											2016-09-02 11:13:11 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-13 20:59:36 +08:00
										 |  |  | // Global profiler to be used by service go-routine.
 | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | var globalProfiler map[string]minioProfiler | 
					
						
							|  |  |  | var globalProfilerMu sync.Mutex | 
					
						
							| 
									
										
										
										
											2019-02-13 20:59:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-20 01:17:46 +08:00
										 |  |  | // dump the request into a string in JSON format.
 | 
					
						
							|  |  |  | func dumpRequest(r *http.Request) string { | 
					
						
							| 
									
										
										
										
											2019-09-12 01:21:43 +08:00
										 |  |  | 	header := r.Header.Clone() | 
					
						
							| 
									
										
										
										
											2016-09-20 01:17:46 +08:00
										 |  |  | 	header.Set("Host", r.Host) | 
					
						
							| 
									
										
										
										
											2017-05-31 15:11:06 +08:00
										 |  |  | 	// Replace all '%' to '%%' so that printer format parser
 | 
					
						
							|  |  |  | 	// to ignore URL encoded values.
 | 
					
						
							|  |  |  | 	rawURI := strings.Replace(r.RequestURI, "%", "%%", -1) | 
					
						
							| 
									
										
										
										
											2016-09-20 01:17:46 +08:00
										 |  |  | 	req := struct { | 
					
						
							| 
									
										
										
										
											2017-05-31 15:11:06 +08:00
										 |  |  | 		Method     string      `json:"method"` | 
					
						
							|  |  |  | 		RequestURI string      `json:"reqURI"` | 
					
						
							|  |  |  | 		Header     http.Header `json:"header"` | 
					
						
							|  |  |  | 	}{r.Method, rawURI, header} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var buffer bytes.Buffer | 
					
						
							|  |  |  | 	enc := json.NewEncoder(&buffer) | 
					
						
							|  |  |  | 	enc.SetEscapeHTML(false) | 
					
						
							|  |  |  | 	if err := enc.Encode(&req); err != nil { | 
					
						
							| 
									
										
										
										
											2017-04-08 05:37:32 +08:00
										 |  |  | 		// Upon error just return Go-syntax representation of the value
 | 
					
						
							|  |  |  | 		return fmt.Sprintf("%#v", req) | 
					
						
							| 
									
										
										
										
											2016-09-20 01:17:46 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-05-31 15:11:06 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Formatted string.
 | 
					
						
							| 
									
										
										
										
											2019-02-13 20:59:36 +08:00
										 |  |  | 	return strings.TrimSpace(buffer.String()) | 
					
						
							| 
									
										
										
										
											2016-09-20 01:17:46 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-02-28 06:59:53 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-03 06:21:30 +08:00
										 |  |  | // isFile - returns whether given path is a file or not.
 | 
					
						
							|  |  |  | func isFile(path string) bool { | 
					
						
							| 
									
										
										
										
											2017-10-13 18:01:15 +08:00
										 |  |  | 	if fi, err := os.Stat(path); err == nil { | 
					
						
							| 
									
										
										
										
											2017-03-03 06:21:30 +08:00
										 |  |  | 		return fi.Mode().IsRegular() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-03-16 07:30:34 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  | // UTCNow - returns current UTC time.
 | 
					
						
							|  |  |  | func UTCNow() time.Time { | 
					
						
							|  |  |  | 	return time.Now().UTC() | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-10-27 01:17:07 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-06 09:58:09 +08:00
										 |  |  | // GenETag - generate UUID based ETag
 | 
					
						
							|  |  |  | func GenETag() string { | 
					
						
							|  |  |  | 	return ToS3ETag(getMD5Hash([]byte(mustGetUUID()))) | 
					
						
							| 
									
										
										
										
											2017-10-27 01:17:07 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-06 09:58:09 +08:00
										 |  |  | // ToS3ETag - return checksum to ETag
 | 
					
						
							|  |  |  | func ToS3ETag(etag string) string { | 
					
						
							| 
									
										
										
										
											2017-10-27 01:17:07 +08:00
										 |  |  | 	etag = canonicalizeETag(etag) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !strings.HasSuffix(etag, "-1") { | 
					
						
							|  |  |  | 		// Tools like s3cmd uses ETag as checksum of data to validate.
 | 
					
						
							|  |  |  | 		// Append "-1" to indicate ETag is not a checksum.
 | 
					
						
							|  |  |  | 		etag += "-1" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return etag | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-12-06 09:58:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-05 05:55:53 +08:00
										 |  |  | func newInternodeHTTPTransport(tlsConfig *tls.Config, dialTimeout time.Duration) func() *http.Transport { | 
					
						
							|  |  |  | 	// For more details about various values used here refer
 | 
					
						
							|  |  |  | 	// https://golang.org/pkg/net/http/#Transport documentation
 | 
					
						
							|  |  |  | 	tr := &http.Transport{ | 
					
						
							|  |  |  | 		Proxy:                 http.ProxyFromEnvironment, | 
					
						
							|  |  |  | 		DialContext:           xhttp.NewInternodeDialContext(dialTimeout), | 
					
						
							|  |  |  | 		MaxIdleConnsPerHost:   16, | 
					
						
							|  |  |  | 		MaxIdleConns:          16, | 
					
						
							|  |  |  | 		IdleConnTimeout:       1 * time.Minute, | 
					
						
							|  |  |  | 		ResponseHeaderTimeout: 3 * time.Minute, // Set conservative timeouts for MinIO internode.
 | 
					
						
							|  |  |  | 		TLSHandshakeTimeout:   10 * time.Second, | 
					
						
							|  |  |  | 		ExpectContinueTimeout: 10 * time.Second, | 
					
						
							|  |  |  | 		TLSClientConfig:       tlsConfig, | 
					
						
							|  |  |  | 		// Go net/http automatically unzip if content-type is
 | 
					
						
							|  |  |  | 		// gzip disable this feature, as we are always interested
 | 
					
						
							|  |  |  | 		// in raw stream.
 | 
					
						
							|  |  |  | 		DisableCompression: true, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-11 00:02:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if tlsConfig != nil { | 
					
						
							|  |  |  | 		http2.ConfigureTransport(tr) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-05 05:55:53 +08:00
										 |  |  | 	return func() *http.Transport { | 
					
						
							|  |  |  | 		return tr | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-18 02:20:56 +08:00
										 |  |  | func newCustomHTTPTransport(tlsConfig *tls.Config, dialTimeout time.Duration) func() *http.Transport { | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	// For more details about various values used here refer
 | 
					
						
							|  |  |  | 	// https://golang.org/pkg/net/http/#Transport documentation
 | 
					
						
							|  |  |  | 	tr := &http.Transport{ | 
					
						
							| 
									
										
										
										
											2020-07-11 03:08:14 +08:00
										 |  |  | 		Proxy:                 http.ProxyFromEnvironment, | 
					
						
							| 
									
										
										
										
											2020-07-04 01:03:41 +08:00
										 |  |  | 		DialContext:           xhttp.NewCustomDialContext(dialTimeout), | 
					
						
							| 
									
										
										
										
											2020-04-18 02:20:56 +08:00
										 |  |  | 		MaxIdleConnsPerHost:   16, | 
					
						
							|  |  |  | 		MaxIdleConns:          16, | 
					
						
							|  |  |  | 		IdleConnTimeout:       1 * time.Minute, | 
					
						
							| 
									
										
										
										
											2020-03-22 13:10:13 +08:00
										 |  |  | 		ResponseHeaderTimeout: 3 * time.Minute, // Set conservative timeouts for MinIO internode.
 | 
					
						
							| 
									
										
										
										
											2020-01-10 18:35:06 +08:00
										 |  |  | 		TLSHandshakeTimeout:   10 * time.Second, | 
					
						
							| 
									
										
										
										
											2020-02-01 11:01:55 +08:00
										 |  |  | 		ExpectContinueTimeout: 10 * time.Second, | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 		TLSClientConfig:       tlsConfig, | 
					
						
							|  |  |  | 		// Go net/http automatically unzip if content-type is
 | 
					
						
							|  |  |  | 		// gzip disable this feature, as we are always interested
 | 
					
						
							|  |  |  | 		// in raw stream.
 | 
					
						
							|  |  |  | 		DisableCompression: true, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-11 00:02:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if tlsConfig != nil { | 
					
						
							|  |  |  | 		http2.ConfigureTransport(tr) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	return func() *http.Transport { | 
					
						
							|  |  |  | 		return tr | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-22 13:10:13 +08:00
										 |  |  | // NewGatewayHTTPTransport returns a new http configuration
 | 
					
						
							| 
									
										
										
										
											2017-12-06 09:58:09 +08:00
										 |  |  | // used while communicating with the cloud backends.
 | 
					
						
							| 
									
										
										
										
											2018-02-21 04:23:37 +08:00
										 |  |  | // This sets the value for MaxIdleConnsPerHost from 2 (go default)
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | // to 256.
 | 
					
						
							| 
									
										
										
										
											2020-03-22 13:10:13 +08:00
										 |  |  | func NewGatewayHTTPTransport() *http.Transport { | 
					
						
							| 
									
										
										
										
											2020-07-08 03:19:57 +08:00
										 |  |  | 	return newGatewayHTTPTransport(1 * time.Minute) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newGatewayHTTPTransport(timeout time.Duration) *http.Transport { | 
					
						
							| 
									
										
										
										
											2020-03-22 13:10:13 +08:00
										 |  |  | 	tr := newCustomHTTPTransport(&tls.Config{ | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 		RootCAs: globalRootCAs, | 
					
						
							| 
									
										
										
										
											2020-04-18 02:20:56 +08:00
										 |  |  | 	}, defaultDialTimeout)() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Allow more requests to be in flight.
 | 
					
						
							| 
									
										
										
										
											2020-07-08 03:19:57 +08:00
										 |  |  | 	tr.ResponseHeaderTimeout = timeout | 
					
						
							| 
									
										
										
										
											2020-04-18 02:20:56 +08:00
										 |  |  | 	tr.MaxConnsPerHost = 256 | 
					
						
							|  |  |  | 	tr.MaxIdleConnsPerHost = 16 | 
					
						
							|  |  |  | 	tr.MaxIdleConns = 256 | 
					
						
							| 
									
										
										
										
											2020-03-22 13:10:13 +08:00
										 |  |  | 	return tr | 
					
						
							| 
									
										
										
										
											2017-12-06 09:58:09 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-09 06:30:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Load the json (typically from disk file).
 | 
					
						
							| 
									
										
										
										
											2018-02-07 07:37:48 +08:00
										 |  |  | func jsonLoad(r io.ReadSeeker, data interface{}) error { | 
					
						
							| 
									
										
										
										
											2018-01-09 06:30:55 +08:00
										 |  |  | 	if _, err := r.Seek(0, io.SeekStart); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return json.NewDecoder(r).Decode(data) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-02-07 07:37:48 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Save to disk file in json format.
 | 
					
						
							|  |  |  | func jsonSave(f interface { | 
					
						
							|  |  |  | 	io.WriteSeeker | 
					
						
							|  |  |  | 	Truncate(int64) error | 
					
						
							|  |  |  | }, data interface{}) error { | 
					
						
							|  |  |  | 	b, err := json.Marshal(data) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err = f.Truncate(0); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if _, err = f.Seek(0, io.SeekStart); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	_, err = f.Write(b) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-03-05 06:16:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // ceilFrac takes a numerator and denominator representing a fraction
 | 
					
						
							|  |  |  | // and returns its ceiling. If denominator is 0, it returns 0 instead
 | 
					
						
							|  |  |  | // of crashing.
 | 
					
						
							|  |  |  | func ceilFrac(numerator, denominator int64) (ceil int64) { | 
					
						
							|  |  |  | 	if denominator == 0 { | 
					
						
							|  |  |  | 		// do nothing on invalid input
 | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Make denominator positive
 | 
					
						
							|  |  |  | 	if denominator < 0 { | 
					
						
							|  |  |  | 		numerator = -numerator | 
					
						
							|  |  |  | 		denominator = -denominator | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ceil = numerator / denominator | 
					
						
							|  |  |  | 	if numerator > 0 && numerator%denominator != 0 { | 
					
						
							|  |  |  | 		ceil++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-03-15 03:01:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-15 09:36:54 +08:00
										 |  |  | // Returns context with ReqInfo details set in the context.
 | 
					
						
							| 
									
										
										
										
											2018-07-21 09:46:32 +08:00
										 |  |  | func newContext(r *http.Request, w http.ResponseWriter, api string) context.Context { | 
					
						
							| 
									
										
										
										
											2018-03-15 03:01:47 +08:00
										 |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	bucket := vars["bucket"] | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  | 	object, err := url.PathUnescape(vars["object"]) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		object = vars["object"] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	prefix, err := url.QueryUnescape(vars["prefix"]) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		prefix = vars["prefix"] | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-15 03:01:47 +08:00
										 |  |  | 	if prefix != "" { | 
					
						
							|  |  |  | 		object = prefix | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-03 05:40:18 +08:00
										 |  |  | 	reqInfo := &logger.ReqInfo{ | 
					
						
							| 
									
										
										
										
											2019-07-02 03:22:01 +08:00
										 |  |  | 		DeploymentID: globalDeploymentID, | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 		RequestID:    w.Header().Get(xhttp.AmzRequestID), | 
					
						
							| 
									
										
										
										
											2018-11-20 06:47:03 +08:00
										 |  |  | 		RemoteHost:   handlers.GetSourceIP(r), | 
					
						
							| 
									
										
										
										
											2019-07-19 00:58:37 +08:00
										 |  |  | 		Host:         getHostName(r), | 
					
						
							| 
									
										
										
										
											2018-11-20 06:47:03 +08:00
										 |  |  | 		UserAgent:    r.UserAgent(), | 
					
						
							|  |  |  | 		API:          api, | 
					
						
							|  |  |  | 		BucketName:   bucket, | 
					
						
							|  |  |  | 		ObjectName:   object, | 
					
						
							| 
									
										
										
										
											2018-07-03 05:40:18 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-06-29 13:09:17 +08:00
										 |  |  | 	return logger.SetReqInfo(r.Context(), reqInfo) | 
					
						
							| 
									
										
										
										
											2018-03-15 03:01:47 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-03-29 05:14:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // Used for registering with rest handlers (have a look at registerStorageRESTHandlers for usage example)
 | 
					
						
							|  |  |  | // If it is passed ["aaaa", "bbbb"], it returns ["aaaa", "{aaaa:.*}", "bbbb", "{bbbb:.*}"]
 | 
					
						
							|  |  |  | func restQueries(keys ...string) []string { | 
					
						
							|  |  |  | 	var accumulator []string | 
					
						
							|  |  |  | 	for _, key := range keys { | 
					
						
							|  |  |  | 		accumulator = append(accumulator, key, "{"+key+":.*}") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return accumulator | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | // lcp finds the longest common prefix of the input strings.
 | 
					
						
							|  |  |  | // It compares by bytes instead of runes (Unicode code points).
 | 
					
						
							|  |  |  | // It's up to the caller to do Unicode normalization if desired
 | 
					
						
							|  |  |  | // (e.g. see golang.org/x/text/unicode/norm).
 | 
					
						
							|  |  |  | func lcp(l []string) string { | 
					
						
							|  |  |  | 	// Special cases first
 | 
					
						
							|  |  |  | 	switch len(l) { | 
					
						
							|  |  |  | 	case 0: | 
					
						
							|  |  |  | 		return "" | 
					
						
							|  |  |  | 	case 1: | 
					
						
							|  |  |  | 		return l[0] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// LCP of min and max (lexigraphically)
 | 
					
						
							|  |  |  | 	// is the LCP of the whole set.
 | 
					
						
							|  |  |  | 	min, max := l[0], l[0] | 
					
						
							|  |  |  | 	for _, s := range l[1:] { | 
					
						
							|  |  |  | 		switch { | 
					
						
							|  |  |  | 		case s < min: | 
					
						
							|  |  |  | 			min = s | 
					
						
							|  |  |  | 		case s > max: | 
					
						
							|  |  |  | 			max = s | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for i := 0; i < len(min) && i < len(max); i++ { | 
					
						
							|  |  |  | 		if min[i] != max[i] { | 
					
						
							|  |  |  | 			return min[:i] | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// In the case where lengths are not equal but all bytes
 | 
					
						
							|  |  |  | 	// are equal, min is the answer ("foo" < "foobar").
 | 
					
						
							|  |  |  | 	return min | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-08-15 02:43:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Returns the mode in which MinIO is running
 | 
					
						
							|  |  |  | func getMinioMode() string { | 
					
						
							|  |  |  | 	mode := globalMinioModeFS | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if globalIsDistErasure { | 
					
						
							|  |  |  | 		mode = globalMinioModeDistErasure | 
					
						
							|  |  |  | 	} else if globalIsErasure { | 
					
						
							|  |  |  | 		mode = globalMinioModeErasure | 
					
						
							| 
									
										
										
										
											2019-08-15 02:43:43 +08:00
										 |  |  | 	} else if globalIsGateway { | 
					
						
							|  |  |  | 		mode = globalMinioModeGatewayPrefix + globalGatewayName | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return mode | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-24 05:17:18 +08:00
										 |  |  | func iamPolicyClaimNameOpenID() string { | 
					
						
							| 
									
										
										
										
											2020-01-09 09:21:58 +08:00
										 |  |  | 	return globalOpenIDConfig.ClaimPrefix + globalOpenIDConfig.ClaimName | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-24 05:17:18 +08:00
										 |  |  | func iamPolicyClaimNameSA() string { | 
					
						
							|  |  |  | 	return "sa-policy" | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-05-27 03:52:24 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // timedValue contains a synchronized value that is considered valid
 | 
					
						
							|  |  |  | // for a specific amount of time.
 | 
					
						
							|  |  |  | // An Update function must be set to provide an updated value when needed.
 | 
					
						
							|  |  |  | type timedValue struct { | 
					
						
							|  |  |  | 	// Update must return an updated value.
 | 
					
						
							|  |  |  | 	// If an error is returned the cached value is not set.
 | 
					
						
							|  |  |  | 	// Only one caller will call this function at any time, others will be blocking.
 | 
					
						
							|  |  |  | 	// The returned value can no longer be modified once returned.
 | 
					
						
							|  |  |  | 	// Should be set before calling Get().
 | 
					
						
							|  |  |  | 	Update func() (interface{}, error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// TTL for a cached value.
 | 
					
						
							|  |  |  | 	// If not set 1 second TTL is assumed.
 | 
					
						
							|  |  |  | 	// Should be set before calling Get().
 | 
					
						
							|  |  |  | 	TTL time.Duration | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Once can be used to initialize values for lazy initialization.
 | 
					
						
							|  |  |  | 	// Should be set before calling Get().
 | 
					
						
							|  |  |  | 	Once sync.Once | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Managed values.
 | 
					
						
							|  |  |  | 	value      interface{} | 
					
						
							|  |  |  | 	lastUpdate time.Time | 
					
						
							|  |  |  | 	mu         sync.Mutex | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Get will return a cached value or fetch a new one.
 | 
					
						
							|  |  |  | // If the Update function returns an error the value is forwarded as is and not cached.
 | 
					
						
							|  |  |  | func (t *timedValue) Get() (interface{}, error) { | 
					
						
							|  |  |  | 	t.mu.Lock() | 
					
						
							|  |  |  | 	defer t.mu.Unlock() | 
					
						
							|  |  |  | 	if t.TTL <= 0 { | 
					
						
							|  |  |  | 		t.TTL = time.Second | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if t.value != nil { | 
					
						
							|  |  |  | 		if time.Since(t.lastUpdate) < t.TTL { | 
					
						
							|  |  |  | 			v := t.value | 
					
						
							|  |  |  | 			return v, nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		t.value = nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	v, err := t.Update() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return v, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	t.value = v | 
					
						
							|  |  |  | 	t.lastUpdate = time.Now() | 
					
						
							|  |  |  | 	return v, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Invalidate the value in the cache.
 | 
					
						
							|  |  |  | func (t *timedValue) Invalidate() { | 
					
						
							|  |  |  | 	t.mu.Lock() | 
					
						
							|  |  |  | 	t.value = nil | 
					
						
							|  |  |  | 	t.mu.Unlock() | 
					
						
							|  |  |  | } |