| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2015-04-29 17:19:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2021-04-17 09:58:26 +08:00
										 |  |  | 	"net" | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  | 	"path" | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2021-12-01 16:38:32 +08:00
										 |  |  | 	"syscall" | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2016-09-02 11:13:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-08 09:41:43 +08:00
										 |  |  | 	"github.com/coreos/go-oidc" | 
					
						
							| 
									
										
											  
											
												directio: Check if buffers are set. (#13440)
Check if directio buffers have actually been fetched and prevent errors on double Close. Return error on Read after Close.
Fixes
```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0xf8582f]
goroutine 210 [running]:
github.com/minio/minio/internal/ioutil.(*ODirectReader).Read(0xc0054f8320, {0xc0014560b0, 0xa8, 0x44d012})
	github.com/minio/minio/internal/ioutil/odirect_reader.go:88 +0x10f
io.ReadAtLeast({0x428c5c0, 0xc0054f8320}, {0xc0014560b0, 0xa8, 0xa8}, 0xa8)
	io/io.go:328 +0x9a
io.ReadFull(...)
	io/io.go:347
github.com/minio/minio/internal/ioutil.ReadFile({0xc001bf60e0, 0x6})
	github.com/minio/minio/internal/ioutil/read_file.go:48 +0x19b
github.com/minio/minio/cmd.(*FSObjects).scanBucket.func1({{0xc00444e1e0, 0x4d}, 0x0, {0xc0040cf240, 0xe}, {0xc0040cf24f, 0x18}, {0xc0040cf268, 0x18}, 0x0, ...})
	github.com/minio/minio/cmd/fs-v1.go:366 +0x1ea
github.com/minio/minio/cmd.(*folderScanner).scanFolder.func1({0xc00474a6a8, 0xc0065d6793}, 0x0)
	github.com/minio/minio/cmd/data-scanner.go:494 +0xb15
github.com/minio/minio/cmd.readDirFn({0xc002803e80, 0x34}, 0xc000670270)
	github.com/minio/minio/cmd/os-readdir_unix.go:172 +0x638
github.com/minio/minio/cmd.(*folderScanner).scanFolder(0xc002deeb40, {0x42dc9d0, 0xc00068cbc0}, {{0xc001c6e2d0, 0x27}, 0xc0023db8e0, 0x1}, 0xc0001c7ab0)
	github.com/minio/minio/cmd/data-scanner.go:427 +0xa8f
github.com/minio/minio/cmd.(*folderScanner).scanFolder.func2({{0xc001c6e2d0, 0x27}, 0xc0023db8e0, 0x27})
	github.com/minio/minio/cmd/data-scanner.go:549 +0xd0
github.com/minio/minio/cmd.(*folderScanner).scanFolder(0xc002deeb40, {0x42dc9d0, 0xc00068cbc0}, {{0xc0013fa9e0, 0xe}, 0x0, 0x1}, 0xc000670dd8)
	github.com/minio/minio/cmd/data-scanner.go:623 +0x205d
github.com/minio/minio/cmd.scanDataFolder({_, _}, {_, _}, {{{0xc0013fa9e0, 0xe}, 0x802, {0x210f15d2, 0xed8f903b8, 0x5bc0e80}, ...}, ...}, ...)
	github.com/minio/minio/cmd/data-scanner.go:333 +0xc51
github.com/minio/minio/cmd.(*FSObjects).scanBucket(_, {_, _}, {_, _}, {{{0xc0013fa9e0, 0xe}, 0x802, {0x210f15d2, 0xed8f903b8, ...}, ...}, ...})
	github.com/minio/minio/cmd/fs-v1.go:364 +0x305
github.com/minio/minio/cmd.(*FSObjects).NSScanner(0x42dc9d0, {0x42dc9d0, 0xc00068cbc0}, 0x0, 0xc003bcfda0, 0x802)
	github.com/minio/minio/cmd/fs-v1.go:307 +0xa16
github.com/minio/minio/cmd.runDataScanner({0x42dc9d0, 0xc00068cbc0}, {0x436a6c0, 0xc000bfcf50})
	github.com/minio/minio/cmd/data-scanner.go:150 +0x749
created by github.com/minio/minio/cmd.initDataScanner
	github.com/minio/minio/cmd/data-scanner.go:73 +0xb0
```
											
										 
											2021-10-15 01:19:17 +08:00
										 |  |  | 	"github.com/dustin/go-humanize" | 
					
						
							| 
									
										
										
										
											2022-02-17 04:00:10 +08:00
										 |  |  | 	"github.com/felixge/fgprof" | 
					
						
							| 
									
										
										
										
											2020-08-11 00:02:29 +08:00
										 |  |  | 	"github.com/gorilla/mux" | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 	"github.com/minio/madmin-go" | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  | 	miniogopolicy "github.com/minio/minio-go/v7/pkg/policy" | 
					
						
							| 
									
										
										
										
											2021-11-30 01:06:56 +08:00
										 |  |  | 	"github.com/minio/minio/internal/config" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/config/api" | 
					
						
							|  |  |  | 	xtls "github.com/minio/minio/internal/config/identity/tls" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/fips" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/handlers" | 
					
						
							|  |  |  | 	xhttp "github.com/minio/minio/internal/http" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/logger" | 
					
						
							| 
									
										
										
										
											2021-06-29 14:58:08 +08:00
										 |  |  | 	"github.com/minio/minio/internal/logger/message/audit" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/rest" | 
					
						
							| 
									
										
										
										
											2021-05-29 06:17:01 +08:00
										 |  |  | 	"github.com/minio/pkg/certs" | 
					
						
							| 
									
										
										
										
											2021-11-30 01:06:56 +08:00
										 |  |  | 	"github.com/minio/pkg/env" | 
					
						
							| 
									
										
										
										
											2022-01-08 09:41:43 +08:00
										 |  |  | 	"golang.org/x/oauth2" | 
					
						
							| 
									
										
										
										
											2015-04-23 07:28:13 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | const ( | 
					
						
							|  |  |  | 	slashSeparator = "/" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  | // BucketAccessPolicy - Collection of canned bucket policy at a given prefix.
 | 
					
						
							|  |  |  | type BucketAccessPolicy struct { | 
					
						
							|  |  |  | 	Bucket string                     `json:"bucket"` | 
					
						
							|  |  |  | 	Prefix string                     `json:"prefix"` | 
					
						
							|  |  |  | 	Policy miniogopolicy.BucketPolicy `json:"policy"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 getWriteQuorum(drive int) int { | 
					
						
							| 
									
										
										
										
											2021-01-17 04:08:02 +08:00
										 |  |  | 	parity := getDefaultParityBlocks(drive) | 
					
						
							|  |  |  | 	quorum := drive - parity | 
					
						
							|  |  |  | 	if quorum == parity { | 
					
						
							| 
									
										
										
										
											2020-08-28 03:55:32 +08:00
										 |  |  | 		quorum++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return quorum | 
					
						
							| 
									
										
										
										
											2020-04-01 00:32:16 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-11 02:37:22 +08:00
										 |  |  | // cloneMSS will clone a map[string]string.
 | 
					
						
							|  |  |  | // If input is nil an empty map is returned, not nil.
 | 
					
						
							|  |  |  | func cloneMSS(v map[string]string) map[string]string { | 
					
						
							|  |  |  | 	r := make(map[string]string, len(v)) | 
					
						
							|  |  |  | 	for k, v := range v { | 
					
						
							|  |  |  | 		r[k] = v | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return r | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-17 01:28:29 +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.
 | 
					
						
							| 
									
										
										
										
											2021-07-24 04:16:53 +08:00
										 |  |  | 	records map[string][]byte | 
					
						
							|  |  |  | 	stopFn  func() ([]byte, error) | 
					
						
							|  |  |  | 	ext     string | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 04:16:53 +08:00
										 |  |  | // record will record the profile and store it as the base.
 | 
					
						
							|  |  |  | func (p *profilerWrapper) record(profileType string, debug int, recordName string) { | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 	var buf bytes.Buffer | 
					
						
							| 
									
										
										
										
											2021-07-24 04:16:53 +08:00
										 |  |  | 	if p.records == nil { | 
					
						
							|  |  |  | 		p.records = make(map[string][]byte) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	err := pprof.Lookup(profileType).WriteTo(&buf, debug) | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-24 04:16:53 +08:00
										 |  |  | 	p.records[recordName] = buf.Bytes() | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 04:16:53 +08:00
										 |  |  | // Records returns the recorded profiling if any.
 | 
					
						
							|  |  |  | func (p profilerWrapper) Records() map[string][]byte { | 
					
						
							|  |  |  | 	return p.records | 
					
						
							| 
									
										
										
										
											2020-01-22 07:49:25 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-07-24 04:16:53 +08:00
										 |  |  | 		for name, buf := range prof.Records() { | 
					
						
							|  |  |  | 			if len(buf) > 0 { | 
					
						
							|  |  |  | 				dst[typ+"-"+name+"."+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) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-02-17 04:00:10 +08:00
										 |  |  | 		// TODO(klauspost): Replace with madmin.ProfilerCPUIO on next update.
 | 
					
						
							|  |  |  | 	case "cpuio": | 
					
						
							|  |  |  | 		// at 10k or more goroutines fgprof is likely to become
 | 
					
						
							|  |  |  | 		// unable to maintain its sampling rate and to significantly
 | 
					
						
							|  |  |  | 		// degrade the performance of your application
 | 
					
						
							|  |  |  | 		// https://github.com/felixge/fgprof#fgprof
 | 
					
						
							|  |  |  | 		if n := runtime.NumGoroutine(); n > 10000 && !globalIsCICD { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("unable to perform CPU IO profile with %d goroutines", n) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		dirPath, err := ioutil.TempDir("", "profile") | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		fn := filepath.Join(dirPath, "cpuio.out") | 
					
						
							|  |  |  | 		f, err := os.Create(fn) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		stop := fgprof.Start(f, fgprof.FormatPprof) | 
					
						
							|  |  |  | 		err = pprof.StartCPUProfile(f) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		prof.stopFn = func() ([]byte, error) { | 
					
						
							|  |  |  | 			err := stop() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			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() | 
					
						
							| 
									
										
										
										
											2021-07-24 04:16:53 +08:00
										 |  |  | 		prof.record("heap", 0, "before") | 
					
						
							| 
									
										
										
										
											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: | 
					
						
							| 
									
										
										
										
											2021-02-28 01:22:14 +08:00
										 |  |  | 		runtime.SetBlockProfileRate(100) | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 		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: | 
					
						
							| 
									
										
										
										
											2021-07-24 04:16:53 +08:00
										 |  |  | 		prof.record("mutex", 0, "before") | 
					
						
							| 
									
										
										
										
											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: | 
					
						
							| 
									
										
										
										
											2021-07-24 04:16:53 +08:00
										 |  |  | 		prof.record("threadcreate", 0, "before") | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2021-07-24 04:16:53 +08:00
										 |  |  | 		prof.record("goroutine", 1, "before") | 
					
						
							|  |  |  | 		prof.record("goroutine", 2, "before,debug=2") | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2021-07-24 04:16:53 +08:00
										 |  |  | 	// Return recorded profiles, each profile associated with a distinct generic name.
 | 
					
						
							|  |  |  | 	Records() map[string][]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.
 | 
					
						
							| 
									
										
										
										
											2022-01-07 05:08:21 +08:00
										 |  |  | var ( | 
					
						
							|  |  |  | 	globalProfiler   map[string]minioProfiler | 
					
						
							|  |  |  | 	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.
 | 
					
						
							| 
									
										
										
										
											2021-11-17 01:28:29 +08:00
										 |  |  | 	rawURI := strings.ReplaceAll(r.RequestURI, "%", "%%") | 
					
						
							| 
									
										
										
										
											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-12-22 13:42:38 +08:00
										 |  |  | func newInternodeHTTPTransport(tlsConfig *tls.Config, dialTimeout time.Duration) func() http.RoundTripper { | 
					
						
							| 
									
										
										
										
											2020-08-05 05:55:53 +08:00
										 |  |  | 	// For more details about various values used here refer
 | 
					
						
							|  |  |  | 	// https://golang.org/pkg/net/http/#Transport documentation
 | 
					
						
							|  |  |  | 	tr := &http.Transport{ | 
					
						
							|  |  |  | 		Proxy:                 http.ProxyFromEnvironment, | 
					
						
							| 
									
										
										
										
											2020-10-17 05:49:05 +08:00
										 |  |  | 		DialContext:           xhttp.DialContextWithDNSCache(globalDNSCache, xhttp.NewInternodeDialContext(dialTimeout)), | 
					
						
							| 
									
										
										
										
											2020-10-13 05:19:46 +08:00
										 |  |  | 		MaxIdleConnsPerHost:   1024, | 
					
						
							| 
									
										
											  
											
												[feat]: change erasure coding default block size from 10MiB to 1MiB (#11721)
major performance improvements in range GETs to avoid large
read amplification when ranges are tiny and random
```
-------------------
Operation: GET
Operations: 142014 -> 339421
Duration: 4m50s -> 4m56s
* Average: +139.41% (+1177.3 MiB/s) throughput, +139.11% (+658.4) obj/s
* Fastest: +125.24% (+1207.4 MiB/s) throughput, +132.32% (+612.9) obj/s
* 50% Median: +139.06% (+1175.7 MiB/s) throughput, +133.46% (+660.9) obj/s
* Slowest: +203.40% (+1267.9 MiB/s) throughput, +198.59% (+753.5) obj/s
```
TTFB from 10MiB BlockSize
```
* First Access TTFB: Avg: 81ms, Median: 61ms, Best: 20ms, Worst: 2.056s
```
TTFB from 1MiB BlockSize
```
* First Access TTFB: Avg: 22ms, Median: 21ms, Best: 8ms, Worst: 91ms
```
Full object reads however do see a slight change which won't be
noticeable in real world, so not doing any comparisons
TTFB still had improvements with full object reads with 1MiB
```
* First Access TTFB: Avg: 68ms, Median: 35ms, Best: 11ms, Worst: 1.16s
```
v/s
TTFB with 10MiB
```
* First Access TTFB: Avg: 388ms, Median: 98ms, Best: 20ms, Worst: 4.156s
```
This change should affect all new uploads, previous uploads should
continue to work with business as usual. But dramatic improvements can
be seen with these changes.
											
										 
											2021-03-07 06:09:34 +08:00
										 |  |  | 		WriteBufferSize:       32 << 10, // 32KiB moving up from 4KiB default
 | 
					
						
							|  |  |  | 		ReadBufferSize:        32 << 10, // 32KiB moving up from 4KiB default
 | 
					
						
							| 
									
										
										
										
											2020-10-13 05:19:46 +08:00
										 |  |  | 		IdleConnTimeout:       15 * time.Second, | 
					
						
							| 
									
										
										
										
											2021-04-24 01:18:18 +08:00
										 |  |  | 		ResponseHeaderTimeout: 15 * time.Minute, // Set conservative timeouts for MinIO internode.
 | 
					
						
							| 
									
										
										
										
											2020-09-25 11:10:07 +08:00
										 |  |  | 		TLSHandshakeTimeout:   15 * time.Second, | 
					
						
							|  |  |  | 		ExpectContinueTimeout: 15 * time.Second, | 
					
						
							| 
									
										
										
										
											2020-08-05 05:55:53 +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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-12 07:53:04 +08:00
										 |  |  | 	// https://github.com/golang/go/issues/23559
 | 
					
						
							|  |  |  | 	// https://github.com/golang/go/issues/42534
 | 
					
						
							|  |  |  | 	// https://github.com/golang/go/issues/43989
 | 
					
						
							|  |  |  | 	// https://github.com/golang/go/issues/33425
 | 
					
						
							|  |  |  | 	// https://github.com/golang/go/issues/29246
 | 
					
						
							|  |  |  | 	// if tlsConfig != nil {
 | 
					
						
							|  |  |  | 	// 	trhttp2, _ := http2.ConfigureTransports(tr)
 | 
					
						
							|  |  |  | 	// 	if trhttp2 != nil {
 | 
					
						
							|  |  |  | 	// 		// ReadIdleTimeout is the timeout after which a health check using ping
 | 
					
						
							|  |  |  | 	// 		// frame will be carried out if no frame is received on the
 | 
					
						
							|  |  |  | 	// 		// connection. 5 minutes is sufficient time for any idle connection.
 | 
					
						
							|  |  |  | 	// 		trhttp2.ReadIdleTimeout = 5 * time.Minute
 | 
					
						
							|  |  |  | 	// 		// PingTimeout is the timeout after which the connection will be closed
 | 
					
						
							|  |  |  | 	// 		// if a response to Ping is not received.
 | 
					
						
							|  |  |  | 	// 		trhttp2.PingTimeout = dialTimeout
 | 
					
						
							|  |  |  | 	// 		// DisableCompression, if true, prevents the Transport from
 | 
					
						
							|  |  |  | 	// 		// requesting compression with an "Accept-Encoding: gzip"
 | 
					
						
							|  |  |  | 	// 		trhttp2.DisableCompression = true
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	// }
 | 
					
						
							| 
									
										
										
										
											2020-08-11 00:02:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-22 13:42:38 +08:00
										 |  |  | 	return func() http.RoundTripper { | 
					
						
							| 
									
										
										
										
											2020-08-05 05:55:53 +08:00
										 |  |  | 		return tr | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-13 05:19:46 +08:00
										 |  |  | // Used by only proxied requests, specifically only supports HTTP/1.1
 | 
					
						
							|  |  |  | func newCustomHTTPProxyTransport(tlsConfig *tls.Config, dialTimeout time.Duration) func() *http.Transport { | 
					
						
							| 
									
										
										
										
											2020-10-09 03:32:32 +08:00
										 |  |  | 	// For more details about various values used here refer
 | 
					
						
							|  |  |  | 	// https://golang.org/pkg/net/http/#Transport documentation
 | 
					
						
							|  |  |  | 	tr := &http.Transport{ | 
					
						
							|  |  |  | 		Proxy:                 http.ProxyFromEnvironment, | 
					
						
							| 
									
										
										
										
											2020-10-17 05:49:05 +08:00
										 |  |  | 		DialContext:           xhttp.DialContextWithDNSCache(globalDNSCache, xhttp.NewInternodeDialContext(dialTimeout)), | 
					
						
							| 
									
										
										
										
											2020-10-13 05:19:46 +08:00
										 |  |  | 		MaxIdleConnsPerHost:   1024, | 
					
						
							| 
									
										
										
										
											2021-07-25 13:03:38 +08:00
										 |  |  | 		MaxConnsPerHost:       1024, | 
					
						
							| 
									
										
											  
											
												[feat]: change erasure coding default block size from 10MiB to 1MiB (#11721)
major performance improvements in range GETs to avoid large
read amplification when ranges are tiny and random
```
-------------------
Operation: GET
Operations: 142014 -> 339421
Duration: 4m50s -> 4m56s
* Average: +139.41% (+1177.3 MiB/s) throughput, +139.11% (+658.4) obj/s
* Fastest: +125.24% (+1207.4 MiB/s) throughput, +132.32% (+612.9) obj/s
* 50% Median: +139.06% (+1175.7 MiB/s) throughput, +133.46% (+660.9) obj/s
* Slowest: +203.40% (+1267.9 MiB/s) throughput, +198.59% (+753.5) obj/s
```
TTFB from 10MiB BlockSize
```
* First Access TTFB: Avg: 81ms, Median: 61ms, Best: 20ms, Worst: 2.056s
```
TTFB from 1MiB BlockSize
```
* First Access TTFB: Avg: 22ms, Median: 21ms, Best: 8ms, Worst: 91ms
```
Full object reads however do see a slight change which won't be
noticeable in real world, so not doing any comparisons
TTFB still had improvements with full object reads with 1MiB
```
* First Access TTFB: Avg: 68ms, Median: 35ms, Best: 11ms, Worst: 1.16s
```
v/s
TTFB with 10MiB
```
* First Access TTFB: Avg: 388ms, Median: 98ms, Best: 20ms, Worst: 4.156s
```
This change should affect all new uploads, previous uploads should
continue to work with business as usual. But dramatic improvements can
be seen with these changes.
											
										 
											2021-03-07 06:09:34 +08:00
										 |  |  | 		WriteBufferSize:       16 << 10, // 16KiB moving up from 4KiB default
 | 
					
						
							|  |  |  | 		ReadBufferSize:        16 << 10, // 16KiB moving up from 4KiB default
 | 
					
						
							| 
									
										
										
										
											2020-10-13 05:19:46 +08:00
										 |  |  | 		IdleConnTimeout:       15 * time.Second, | 
					
						
							|  |  |  | 		ResponseHeaderTimeout: 30 * time.Minute, // Set larger timeouts for proxied requests.
 | 
					
						
							| 
									
										
										
										
											2020-10-09 03:32:32 +08:00
										 |  |  | 		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, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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-10-17 05:49:05 +08:00
										 |  |  | 		DialContext:           xhttp.DialContextWithDNSCache(globalDNSCache, xhttp.NewInternodeDialContext(dialTimeout)), | 
					
						
							| 
									
										
										
										
											2020-10-13 05:19:46 +08:00
										 |  |  | 		MaxIdleConnsPerHost:   1024, | 
					
						
							| 
									
										
											  
											
												[feat]: change erasure coding default block size from 10MiB to 1MiB (#11721)
major performance improvements in range GETs to avoid large
read amplification when ranges are tiny and random
```
-------------------
Operation: GET
Operations: 142014 -> 339421
Duration: 4m50s -> 4m56s
* Average: +139.41% (+1177.3 MiB/s) throughput, +139.11% (+658.4) obj/s
* Fastest: +125.24% (+1207.4 MiB/s) throughput, +132.32% (+612.9) obj/s
* 50% Median: +139.06% (+1175.7 MiB/s) throughput, +133.46% (+660.9) obj/s
* Slowest: +203.40% (+1267.9 MiB/s) throughput, +198.59% (+753.5) obj/s
```
TTFB from 10MiB BlockSize
```
* First Access TTFB: Avg: 81ms, Median: 61ms, Best: 20ms, Worst: 2.056s
```
TTFB from 1MiB BlockSize
```
* First Access TTFB: Avg: 22ms, Median: 21ms, Best: 8ms, Worst: 91ms
```
Full object reads however do see a slight change which won't be
noticeable in real world, so not doing any comparisons
TTFB still had improvements with full object reads with 1MiB
```
* First Access TTFB: Avg: 68ms, Median: 35ms, Best: 11ms, Worst: 1.16s
```
v/s
TTFB with 10MiB
```
* First Access TTFB: Avg: 388ms, Median: 98ms, Best: 20ms, Worst: 4.156s
```
This change should affect all new uploads, previous uploads should
continue to work with business as usual. But dramatic improvements can
be seen with these changes.
											
										 
											2021-03-07 06:09:34 +08:00
										 |  |  | 		WriteBufferSize:       16 << 10, // 16KiB moving up from 4KiB default
 | 
					
						
							|  |  |  | 		ReadBufferSize:        16 << 10, // 16KiB moving up from 4KiB default
 | 
					
						
							| 
									
										
										
										
											2020-10-13 05:19:46 +08:00
										 |  |  | 		IdleConnTimeout:       15 * time.Second, | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-12 07:53:04 +08:00
										 |  |  | 	// https://github.com/golang/go/issues/23559
 | 
					
						
							|  |  |  | 	// https://github.com/golang/go/issues/42534
 | 
					
						
							|  |  |  | 	// https://github.com/golang/go/issues/43989
 | 
					
						
							|  |  |  | 	// https://github.com/golang/go/issues/33425
 | 
					
						
							|  |  |  | 	// https://github.com/golang/go/issues/29246
 | 
					
						
							|  |  |  | 	// if tlsConfig != nil {
 | 
					
						
							|  |  |  | 	// 	trhttp2, _ := http2.ConfigureTransports(tr)
 | 
					
						
							|  |  |  | 	// 	if trhttp2 != nil {
 | 
					
						
							|  |  |  | 	// 		// ReadIdleTimeout is the timeout after which a health check using ping
 | 
					
						
							|  |  |  | 	// 		// frame will be carried out if no frame is received on the
 | 
					
						
							|  |  |  | 	// 		// connection. 5 minutes is sufficient time for any idle connection.
 | 
					
						
							|  |  |  | 	// 		trhttp2.ReadIdleTimeout = 5 * time.Minute
 | 
					
						
							|  |  |  | 	// 		// PingTimeout is the timeout after which the connection will be closed
 | 
					
						
							|  |  |  | 	// 		// if a response to Ping is not received.
 | 
					
						
							|  |  |  | 	// 		trhttp2.PingTimeout = dialTimeout
 | 
					
						
							|  |  |  | 	// 		// DisableCompression, if true, prevents the Transport from
 | 
					
						
							|  |  |  | 	// 		// requesting compression with an "Accept-Encoding: gzip"
 | 
					
						
							|  |  |  | 	// 		trhttp2.DisableCompression = true
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	// }
 | 
					
						
							| 
									
										
										
										
											2020-08-11 00:02:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	return func() *http.Transport { | 
					
						
							|  |  |  | 		return tr | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-02 01:19:13 +08:00
										 |  |  | // NewGatewayHTTPTransportWithClientCerts returns a new http configuration
 | 
					
						
							|  |  |  | // used while communicating with the cloud backends.
 | 
					
						
							|  |  |  | func NewGatewayHTTPTransportWithClientCerts(clientCert, clientKey string) *http.Transport { | 
					
						
							|  |  |  | 	transport := newGatewayHTTPTransport(1 * time.Minute) | 
					
						
							|  |  |  | 	if clientCert != "" && clientKey != "" { | 
					
						
							|  |  |  | 		ctx, cancel := context.WithTimeout(context.Background(), time.Second) | 
					
						
							|  |  |  | 		defer cancel() | 
					
						
							|  |  |  | 		c, err := certs.NewManager(ctx, clientCert, clientKey, tls.LoadX509KeyPair) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			logger.LogIf(ctx, fmt.Errorf("failed to load client key and cert, please check your endpoint configuration: %s", | 
					
						
							|  |  |  | 				err.Error())) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if c != nil { | 
					
						
							| 
									
										
										
										
											2022-03-05 12:34:39 +08:00
										 |  |  | 			c.UpdateReloadDuration(10 * time.Second) | 
					
						
							| 
									
										
										
										
											2021-12-01 16:38:32 +08:00
										 |  |  | 			c.ReloadOnSignal(syscall.SIGHUP) // allow reloads upon SIGHUP
 | 
					
						
							| 
									
										
										
										
											2021-03-02 01:19:13 +08:00
										 |  |  | 			transport.TLSClientConfig.GetClientCertificate = c.GetClientCertificate | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return transport | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											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{ | 
					
						
							| 
									
										
										
										
											2022-01-07 03:34:02 +08:00
										 |  |  | 		RootCAs:            globalRootCAs, | 
					
						
							|  |  |  | 		ClientSessionCache: tls.NewLRUClientSessionCache(tlsClientSessionCacheSize), | 
					
						
							| 
									
										
										
										
											2020-04-18 02:20:56 +08:00
										 |  |  | 	}, defaultDialTimeout)() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-13 05:19:46 +08:00
										 |  |  | 	// Customize response header timeout for gateway transport.
 | 
					
						
							| 
									
										
										
										
											2020-07-08 03:19:57 +08:00
										 |  |  | 	tr.ResponseHeaderTimeout = timeout | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-17 09:58:26 +08:00
										 |  |  | // NewRemoteTargetHTTPTransport returns a new http configuration
 | 
					
						
							|  |  |  | // used while communicating with the remote replication targets.
 | 
					
						
							|  |  |  | func NewRemoteTargetHTTPTransport() *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: (&net.Dialer{ | 
					
						
							|  |  |  | 			Timeout:   15 * time.Second, | 
					
						
							|  |  |  | 			KeepAlive: 30 * time.Second, | 
					
						
							|  |  |  | 		}).DialContext, | 
					
						
							|  |  |  | 		MaxIdleConnsPerHost:   1024, | 
					
						
							|  |  |  | 		WriteBufferSize:       16 << 10, // 16KiB moving up from 4KiB default
 | 
					
						
							|  |  |  | 		ReadBufferSize:        16 << 10, // 16KiB moving up from 4KiB default
 | 
					
						
							|  |  |  | 		IdleConnTimeout:       15 * time.Second, | 
					
						
							|  |  |  | 		TLSHandshakeTimeout:   5 * time.Second, | 
					
						
							|  |  |  | 		ExpectContinueTimeout: 5 * time.Second, | 
					
						
							|  |  |  | 		TLSClientConfig: &tls.Config{ | 
					
						
							| 
									
										
										
										
											2022-01-07 03:34:02 +08:00
										 |  |  | 			RootCAs:            globalRootCAs, | 
					
						
							|  |  |  | 			ClientSessionCache: tls.NewLRUClientSessionCache(tlsClientSessionCacheSize), | 
					
						
							| 
									
										
										
										
											2021-04-17 09:58:26 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		// Go net/http automatically unzip if content-type is
 | 
					
						
							|  |  |  | 		// gzip disable this feature, as we are always interested
 | 
					
						
							|  |  |  | 		// in raw stream.
 | 
					
						
							|  |  |  | 		DisableCompression: true, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return tr | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-16 07:32:13 +08:00
										 |  |  | // pathClean is like path.Clean but does not return "." for
 | 
					
						
							|  |  |  | // empty inputs, instead returns "empty" as is.
 | 
					
						
							|  |  |  | func pathClean(p string) string { | 
					
						
							|  |  |  | 	cp := path.Clean(p) | 
					
						
							|  |  |  | 	if cp == "." { | 
					
						
							|  |  |  | 		return "" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return cp | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-12 05:57:03 +08:00
										 |  |  | func trimLeadingSlash(ep string) string { | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  | 	if len(ep) > 0 && ep[0] == '/' { | 
					
						
							|  |  |  | 		// Path ends with '/' preserve it
 | 
					
						
							| 
									
										
										
										
											2021-03-12 05:57:03 +08:00
										 |  |  | 		if ep[len(ep)-1] == '/' && len(ep) > 1 { | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  | 			ep = path.Clean(ep) | 
					
						
							|  |  |  | 			ep += slashSeparator | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			ep = path.Clean(ep) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ep = ep[1:] | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-12 05:57:03 +08:00
										 |  |  | 	return ep | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // unescapeGeneric is similar to url.PathUnescape or url.QueryUnescape
 | 
					
						
							|  |  |  | // depending on input, additionally also handles situations such as
 | 
					
						
							|  |  |  | // `//` are normalized as `/`, also removes any `/` prefix before
 | 
					
						
							|  |  |  | // returning.
 | 
					
						
							|  |  |  | func unescapeGeneric(p string, escapeFn func(string) (string, error)) (string, error) { | 
					
						
							|  |  |  | 	ep, err := escapeFn(p) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return trimLeadingSlash(ep), nil | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // unescapePath is similar to unescapeGeneric but for specifically
 | 
					
						
							|  |  |  | // path unescaping.
 | 
					
						
							|  |  |  | func unescapePath(p string) (string, error) { | 
					
						
							|  |  |  | 	return unescapeGeneric(p, url.PathUnescape) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // similar to unescapeGeneric but never returns any error if the unescaping
 | 
					
						
							|  |  |  | // fails, returns the input as is in such occasion, not meant to be
 | 
					
						
							|  |  |  | // used where strict validation is expected.
 | 
					
						
							|  |  |  | func likelyUnescapeGeneric(p string, escapeFn func(string) (string, error)) string { | 
					
						
							|  |  |  | 	ep, err := unescapeGeneric(p, escapeFn) | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  | 		return p | 
					
						
							| 
									
										
										
										
											2020-02-12 11:38:02 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  | 	return ep | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-03 17:28:52 +08:00
										 |  |  | func updateReqContext(ctx context.Context, objects ...ObjectV) context.Context { | 
					
						
							|  |  |  | 	req := logger.GetReqInfo(ctx) | 
					
						
							|  |  |  | 	if req != nil { | 
					
						
							|  |  |  | 		req.Objects = make([]logger.ObjectVersion, 0, len(objects)) | 
					
						
							|  |  |  | 		for _, ov := range objects { | 
					
						
							|  |  |  | 			req.Objects = append(req.Objects, logger.ObjectVersion{ | 
					
						
							|  |  |  | 				ObjectName: ov.ObjectName, | 
					
						
							|  |  |  | 				VersionID:  ov.VersionID, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return logger.SetReqInfo(ctx, req) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ctx | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-10 04:58:22 +08:00
										 |  |  | // Returns context with ReqInfo details set in the context.
 | 
					
						
							|  |  |  | func newContext(r *http.Request, w http.ResponseWriter, api string) context.Context { | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	bucket := vars["bucket"] | 
					
						
							|  |  |  | 	object := likelyUnescapeGeneric(vars["object"], url.PathUnescape) | 
					
						
							|  |  |  | 	prefix := likelyUnescapeGeneric(vars["prefix"], url.QueryUnescape) | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							| 
									
										
										
										
											2022-01-03 17:28:52 +08:00
										 |  |  | 		VersionID:    strings.TrimSpace(r.Form.Get(xhttp.VersionID)), | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-29 03:44:48 +08:00
										 |  |  | // Suffix returns the longest common suffix of the provided strings
 | 
					
						
							|  |  |  | func lcpSuffix(strs []string) string { | 
					
						
							|  |  |  | 	return lcp(strs, false) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func lcp(strs []string, pre bool) string { | 
					
						
							|  |  |  | 	// short-circuit empty list
 | 
					
						
							|  |  |  | 	if len(strs) == 0 { | 
					
						
							| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | 		return "" | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-01-29 03:44:48 +08:00
										 |  |  | 	xfix := strs[0] | 
					
						
							|  |  |  | 	// short-circuit single-element list
 | 
					
						
							|  |  |  | 	if len(strs) == 1 { | 
					
						
							|  |  |  | 		return xfix | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// compare first to rest
 | 
					
						
							|  |  |  | 	for _, str := range strs[1:] { | 
					
						
							|  |  |  | 		xfixl := len(xfix) | 
					
						
							|  |  |  | 		strl := len(str) | 
					
						
							|  |  |  | 		// short-circuit empty strings
 | 
					
						
							|  |  |  | 		if xfixl == 0 || strl == 0 { | 
					
						
							|  |  |  | 			return "" | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// maximum possible length
 | 
					
						
							|  |  |  | 		maxl := xfixl | 
					
						
							|  |  |  | 		if strl < maxl { | 
					
						
							|  |  |  | 			maxl = strl | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// compare letters
 | 
					
						
							|  |  |  | 		if pre { | 
					
						
							|  |  |  | 			// prefix, iterate left to right
 | 
					
						
							|  |  |  | 			for i := 0; i < maxl; i++ { | 
					
						
							|  |  |  | 				if xfix[i] != str[i] { | 
					
						
							|  |  |  | 					xfix = xfix[:i] | 
					
						
							|  |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			// suffix, iterate right to left
 | 
					
						
							|  |  |  | 			for i := 0; i < maxl; i++ { | 
					
						
							|  |  |  | 				xi := xfixl - i - 1 | 
					
						
							|  |  |  | 				si := strl - i - 1 | 
					
						
							|  |  |  | 				if xfix[xi] != str[si] { | 
					
						
							|  |  |  | 					xfix = xfix[xi+1:] | 
					
						
							|  |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-01-29 03:44:48 +08:00
										 |  |  | 	return xfix | 
					
						
							| 
									
										
										
										
											2019-08-10 01:02:41 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2021-02-13 10:17:52 +08:00
										 |  |  | 	mu         sync.RWMutex | 
					
						
							| 
									
										
										
										
											2020-05-27 03:52:24 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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) { | 
					
						
							| 
									
										
										
										
											2021-02-13 10:17:52 +08:00
										 |  |  | 	v := t.get() | 
					
						
							|  |  |  | 	if v != nil { | 
					
						
							|  |  |  | 		return v, nil | 
					
						
							| 
									
										
										
										
											2020-05-27 03:52:24 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-02-13 10:17:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-27 03:52:24 +08:00
										 |  |  | 	v, err := t.Update() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return v, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-02-13 10:17:52 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	t.update(v) | 
					
						
							| 
									
										
										
										
											2020-05-27 03:52:24 +08:00
										 |  |  | 	return v, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-13 10:17:52 +08:00
										 |  |  | func (t *timedValue) get() (v interface{}) { | 
					
						
							|  |  |  | 	ttl := t.TTL | 
					
						
							|  |  |  | 	if ttl <= 0 { | 
					
						
							|  |  |  | 		ttl = time.Second | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	t.mu.RLock() | 
					
						
							|  |  |  | 	defer t.mu.RUnlock() | 
					
						
							|  |  |  | 	v = t.value | 
					
						
							|  |  |  | 	if time.Since(t.lastUpdate) < ttl { | 
					
						
							|  |  |  | 		return v | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (t *timedValue) update(v interface{}) { | 
					
						
							| 
									
										
										
										
											2020-05-27 03:52:24 +08:00
										 |  |  | 	t.mu.Lock() | 
					
						
							| 
									
										
										
										
											2021-02-13 10:17:52 +08:00
										 |  |  | 	defer t.mu.Unlock() | 
					
						
							|  |  |  | 	t.value = v | 
					
						
							|  |  |  | 	t.lastUpdate = time.Now() | 
					
						
							| 
									
										
										
										
											2020-05-27 03:52:24 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-09-19 23:39:41 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // On MinIO a directory object is stored as a regular object with "__XLDIR__" suffix.
 | 
					
						
							|  |  |  | // For ex. "prefix/" is stored as "prefix__XLDIR__"
 | 
					
						
							|  |  |  | func encodeDirObject(object string) string { | 
					
						
							|  |  |  | 	if HasSuffix(object, slashSeparator) { | 
					
						
							|  |  |  | 		return strings.TrimSuffix(object, slashSeparator) + globalDirSuffix | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return object | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Reverse process of encodeDirObject()
 | 
					
						
							|  |  |  | func decodeDirObject(object string) string { | 
					
						
							|  |  |  | 	if HasSuffix(object, globalDirSuffix) { | 
					
						
							|  |  |  | 		return strings.TrimSuffix(object, globalDirSuffix) + slashSeparator | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return object | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-03-02 04:31:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // This is used by metrics to show the number of failed RPC calls
 | 
					
						
							|  |  |  | // between internodes
 | 
					
						
							|  |  |  | func loadAndResetRPCNetworkErrsCounter() uint64 { | 
					
						
							|  |  |  | 	defer rest.ResetNetworkErrsCounter() | 
					
						
							|  |  |  | 	return rest.GetNetworkErrsCounter() | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-06-25 09:29:30 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Helper method to return total number of nodes in cluster
 | 
					
						
							|  |  |  | func totalNodeCount() uint64 { | 
					
						
							|  |  |  | 	peers, _ := globalEndpoints.peers() | 
					
						
							|  |  |  | 	totalNodesCount := uint64(len(peers)) | 
					
						
							|  |  |  | 	if totalNodesCount == 0 { | 
					
						
							|  |  |  | 		totalNodesCount = 1 // For standalone erasure coding
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return totalNodesCount | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-06-29 14:58:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // AuditLogOptions takes options for audit logging subsystem activity
 | 
					
						
							|  |  |  | type AuditLogOptions struct { | 
					
						
							|  |  |  | 	Trigger   string | 
					
						
							|  |  |  | 	APIName   string | 
					
						
							|  |  |  | 	Status    string | 
					
						
							|  |  |  | 	VersionID string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // sends audit logs for internal subsystem activity
 | 
					
						
							|  |  |  | func auditLogInternal(ctx context.Context, bucket, object string, opts AuditLogOptions) { | 
					
						
							|  |  |  | 	entry := audit.NewEntry(globalDeploymentID) | 
					
						
							|  |  |  | 	entry.Trigger = opts.Trigger | 
					
						
							|  |  |  | 	entry.API.Name = opts.APIName | 
					
						
							|  |  |  | 	entry.API.Bucket = bucket | 
					
						
							|  |  |  | 	entry.API.Object = object | 
					
						
							|  |  |  | 	if opts.VersionID != "" { | 
					
						
							|  |  |  | 		entry.ReqQuery = make(map[string]string) | 
					
						
							|  |  |  | 		entry.ReqQuery[xhttp.VersionID] = opts.VersionID | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	entry.API.Status = opts.Status | 
					
						
							|  |  |  | 	ctx = logger.SetAuditEntry(ctx, &entry) | 
					
						
							|  |  |  | 	logger.AuditLog(ctx, nil, nil, nil) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-11 08:43:34 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-30 01:06:56 +08:00
										 |  |  | func newTLSConfig(getCert certs.GetCertificateFunc) *tls.Config { | 
					
						
							|  |  |  | 	if getCert == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tlsConfig := &tls.Config{ | 
					
						
							|  |  |  | 		PreferServerCipherSuites: true, | 
					
						
							|  |  |  | 		MinVersion:               tls.VersionTLS12, | 
					
						
							|  |  |  | 		NextProtos:               []string{"http/1.1", "h2"}, | 
					
						
							|  |  |  | 		GetCertificate:           getCert, | 
					
						
							| 
									
										
										
										
											2022-01-07 03:34:02 +08:00
										 |  |  | 		ClientSessionCache:       tls.NewLRUClientSessionCache(tlsClientSessionCacheSize), | 
					
						
							| 
									
										
										
										
											2021-11-30 01:06:56 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tlsClientIdentity := env.Get(xtls.EnvIdentityTLSEnabled, "") == config.EnableOn | 
					
						
							|  |  |  | 	if tlsClientIdentity { | 
					
						
							|  |  |  | 		tlsConfig.ClientAuth = tls.RequestClientCert | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	secureCiphers := env.Get(api.EnvAPISecureCiphers, config.EnableOn) == config.EnableOn | 
					
						
							|  |  |  | 	if secureCiphers || fips.Enabled { | 
					
						
							|  |  |  | 		// Hardened ciphers
 | 
					
						
							|  |  |  | 		tlsConfig.CipherSuites = fips.CipherSuitesTLS() | 
					
						
							|  |  |  | 		tlsConfig.CurvePreferences = fips.EllipticCurvesTLS() | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		// Default ciphers while excluding those with security issues
 | 
					
						
							|  |  |  | 		for _, cipher := range tls.CipherSuites() { | 
					
						
							|  |  |  | 			tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, cipher.ID) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return tlsConfig | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-01-08 09:41:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /////////// Types and functions for OpenID IAM testing
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // OpenIDClientAppParams - contains openID client application params, used in
 | 
					
						
							|  |  |  | // testing.
 | 
					
						
							|  |  |  | type OpenIDClientAppParams struct { | 
					
						
							|  |  |  | 	ClientID, ClientSecret, ProviderURL, RedirectURL string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MockOpenIDTestUserInteraction - tries to login to dex using provided credentials.
 | 
					
						
							|  |  |  | // It performs the user's browser interaction to login and retrieves the auth
 | 
					
						
							|  |  |  | // code from dex and exchanges it for a JWT.
 | 
					
						
							|  |  |  | func MockOpenIDTestUserInteraction(ctx context.Context, pro OpenIDClientAppParams, username, password string) (string, error) { | 
					
						
							|  |  |  | 	ctx, cancel := context.WithTimeout(ctx, 10*time.Second) | 
					
						
							|  |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	provider, err := oidc.NewProvider(ctx, pro.ProviderURL) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", fmt.Errorf("unable to create provider: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Configure an OpenID Connect aware OAuth2 client.
 | 
					
						
							|  |  |  | 	oauth2Config := oauth2.Config{ | 
					
						
							|  |  |  | 		ClientID:     pro.ClientID, | 
					
						
							|  |  |  | 		ClientSecret: pro.ClientSecret, | 
					
						
							|  |  |  | 		RedirectURL:  pro.RedirectURL, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Discovery returns the OAuth2 endpoints.
 | 
					
						
							|  |  |  | 		Endpoint: provider.Endpoint(), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// "openid" is a required scope for OpenID Connect flows.
 | 
					
						
							|  |  |  | 		Scopes: []string{oidc.ScopeOpenID, "groups"}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	state := fmt.Sprintf("x%dx", time.Now().Unix()) | 
					
						
							|  |  |  | 	authCodeURL := oauth2Config.AuthCodeURL(state) | 
					
						
							|  |  |  | 	// fmt.Printf("authcodeurl: %s\n", authCodeURL)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var lastReq *http.Request | 
					
						
							|  |  |  | 	checkRedirect := func(req *http.Request, via []*http.Request) error { | 
					
						
							|  |  |  | 		// fmt.Printf("CheckRedirect:\n")
 | 
					
						
							|  |  |  | 		// fmt.Printf("Upcoming: %s %s\n", req.Method, req.URL.String())
 | 
					
						
							|  |  |  | 		// for i, c := range via {
 | 
					
						
							|  |  |  | 		// 	fmt.Printf("Sofar %d: %s %s\n", i, c.Method, c.URL.String())
 | 
					
						
							|  |  |  | 		// }
 | 
					
						
							|  |  |  | 		// Save the last request in a redirect chain.
 | 
					
						
							|  |  |  | 		lastReq = req | 
					
						
							|  |  |  | 		// We do not follow redirect back to client application.
 | 
					
						
							|  |  |  | 		if req.URL.Path == "/oauth_callback" { | 
					
						
							|  |  |  | 			return http.ErrUseLastResponse | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dexClient := http.Client{ | 
					
						
							|  |  |  | 		CheckRedirect: checkRedirect, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	u, err := url.Parse(authCodeURL) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", fmt.Errorf("url parse err: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Start the user auth flow. This page would present the login with
 | 
					
						
							|  |  |  | 	// email or LDAP option.
 | 
					
						
							|  |  |  | 	req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", fmt.Errorf("new request err: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	_, err = dexClient.Do(req) | 
					
						
							|  |  |  | 	// fmt.Printf("Do: %#v %#v\n", resp, err)
 | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", fmt.Errorf("auth url request err: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Modify u to choose the ldap option
 | 
					
						
							|  |  |  | 	u.Path += "/ldap" | 
					
						
							|  |  |  | 	// fmt.Println(u)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Pick the LDAP login option. This would return a form page after
 | 
					
						
							|  |  |  | 	// following some redirects. `lastReq` would be the URL of the form
 | 
					
						
							|  |  |  | 	// page, where we need to POST (submit) the form.
 | 
					
						
							|  |  |  | 	req, err = http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", fmt.Errorf("new request err (/ldap): %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	_, err = dexClient.Do(req) | 
					
						
							|  |  |  | 	// fmt.Printf("Fetch LDAP login page: %#v %#v\n", resp, err)
 | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", fmt.Errorf("request err: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// {
 | 
					
						
							|  |  |  | 	// 	bodyBuf, err := ioutil.ReadAll(resp.Body)
 | 
					
						
							|  |  |  | 	// 	if err != nil {
 | 
					
						
							|  |  |  | 	// 		return "", fmt.Errorf("Error reading body: %v", err)
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	// 	fmt.Printf("bodyBuf (for LDAP login page): %s\n", string(bodyBuf))
 | 
					
						
							|  |  |  | 	// }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Fill the login form with our test creds:
 | 
					
						
							|  |  |  | 	// fmt.Printf("login form url: %s\n", lastReq.URL.String())
 | 
					
						
							|  |  |  | 	formData := url.Values{} | 
					
						
							|  |  |  | 	formData.Set("login", username) | 
					
						
							|  |  |  | 	formData.Set("password", password) | 
					
						
							|  |  |  | 	req, err = http.NewRequestWithContext(ctx, http.MethodPost, lastReq.URL.String(), strings.NewReader(formData.Encode())) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", fmt.Errorf("new request err (/login): %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	req.Header.Set("Content-Type", "application/x-www-form-urlencoded") | 
					
						
							|  |  |  | 	_, err = dexClient.Do(req) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", fmt.Errorf("post form err: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// fmt.Printf("resp: %#v %#v\n", resp.StatusCode, resp.Header)
 | 
					
						
							|  |  |  | 	// bodyBuf, err := ioutil.ReadAll(resp.Body)
 | 
					
						
							|  |  |  | 	// if err != nil {
 | 
					
						
							|  |  |  | 	// 	return "", fmt.Errorf("Error reading body: %v", err)
 | 
					
						
							|  |  |  | 	// }
 | 
					
						
							|  |  |  | 	// fmt.Printf("resp body: %s\n", string(bodyBuf))
 | 
					
						
							|  |  |  | 	// fmt.Printf("lastReq: %#v\n", lastReq.URL.String())
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// On form submission, the last redirect response contains the auth
 | 
					
						
							|  |  |  | 	// code, which we now have in `lastReq`. Exchange it for a JWT id_token.
 | 
					
						
							|  |  |  | 	q := lastReq.URL.Query() | 
					
						
							|  |  |  | 	// fmt.Printf("lastReq.URL: %#v q: %#v\n", lastReq.URL, q)
 | 
					
						
							|  |  |  | 	code := q.Get("code") | 
					
						
							|  |  |  | 	oauth2Token, err := oauth2Config.Exchange(ctx, code) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", fmt.Errorf("unable to exchange code for id token: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rawIDToken, ok := oauth2Token.Extra("id_token").(string) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return "", fmt.Errorf("id_token not found!") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// fmt.Printf("TOKEN: %s\n", rawIDToken)
 | 
					
						
							|  |  |  | 	return rawIDToken, nil | 
					
						
							|  |  |  | } |