| 
									
										
										
										
											2016-04-29 11:01:11 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2015, 2016, 2017 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2016-04-29 11:01:11 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2016-04-29 11:01:11 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-08-02 05:19:11 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2016-04-29 11:01:11 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2018-08-02 05:19:11 +08:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2016-07-24 13:51:12 +08:00
										 |  |  | 	"mime/multipart" | 
					
						
							| 
									
										
										
										
											2017-11-15 08:56:24 +08:00
										 |  |  | 	"net" | 
					
						
							| 
									
										
										
										
											2016-07-19 12:20:17 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2016-07-23 11:31:45 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 	xhttp "github.com/minio/minio/cmd/http" | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2018-11-30 09:35:11 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/auth" | 
					
						
							| 
									
										
										
										
											2018-07-03 05:40:18 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/handlers" | 
					
						
							| 
									
										
										
										
											2016-04-29 11:01:11 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-04 05:50:09 +08:00
										 |  |  | // Parses location constraint from the incoming reader.
 | 
					
						
							|  |  |  | func parseLocationConstraint(r *http.Request) (location string, s3Error APIErrorCode) { | 
					
						
							| 
									
										
										
										
											2016-07-19 12:20:17 +08:00
										 |  |  | 	// If the request has no body with content-length set to 0,
 | 
					
						
							|  |  |  | 	// we do not have to validate location constraint. Bucket will
 | 
					
						
							|  |  |  | 	// be created at default region.
 | 
					
						
							|  |  |  | 	locationConstraint := createBucketLocationConfiguration{} | 
					
						
							| 
									
										
										
										
											2016-09-30 06:51:00 +08:00
										 |  |  | 	err := xmlDecoder(r.Body, &locationConstraint, r.ContentLength) | 
					
						
							| 
									
										
										
										
											2019-01-31 23:19:09 +08:00
										 |  |  | 	if err != nil && r.ContentLength != 0 { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		logger.LogIf(context.Background(), err) | 
					
						
							| 
									
										
										
										
											2017-04-04 05:50:09 +08:00
										 |  |  | 		// Treat all other failures as XML parsing errors.
 | 
					
						
							|  |  |  | 		return "", ErrMalformedXML | 
					
						
							|  |  |  | 	} // else for both err as nil or io.EOF
 | 
					
						
							|  |  |  | 	location = locationConstraint.Location | 
					
						
							|  |  |  | 	if location == "" { | 
					
						
							| 
									
										
										
										
											2017-11-30 05:12:47 +08:00
										 |  |  | 		location = globalServerConfig.GetRegion() | 
					
						
							| 
									
										
										
										
											2016-04-29 11:01:11 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-04 05:50:09 +08:00
										 |  |  | 	return location, ErrNone | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Validates input location is same as configured region
 | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  | // of MinIO server.
 | 
					
						
							| 
									
										
										
										
											2017-04-04 05:50:09 +08:00
										 |  |  | func isValidLocation(location string) bool { | 
					
						
							| 
									
										
										
										
											2017-11-30 05:12:47 +08:00
										 |  |  | 	return globalServerConfig.GetRegion() == "" || globalServerConfig.GetRegion() == location | 
					
						
							| 
									
										
										
										
											2016-04-29 11:01:11 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-07-23 11:31:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Supported headers that needs to be extracted.
 | 
					
						
							|  |  |  | var supportedHeaders = []string{ | 
					
						
							|  |  |  | 	"content-type", | 
					
						
							|  |  |  | 	"cache-control", | 
					
						
							| 
									
										
										
										
											2018-03-14 17:57:32 +08:00
										 |  |  | 	"content-language", | 
					
						
							| 
									
										
										
										
											2016-07-23 11:31:45 +08:00
										 |  |  | 	"content-encoding", | 
					
						
							|  |  |  | 	"content-disposition", | 
					
						
							| 
									
										
										
										
											2019-10-07 13:50:24 +08:00
										 |  |  | 	xhttp.AmzStorageClass, | 
					
						
							| 
									
										
										
										
											2018-03-29 05:14:06 +08:00
										 |  |  | 	"expires", | 
					
						
							| 
									
										
										
										
											2016-07-23 11:31:45 +08:00
										 |  |  | 	// Add more supported headers here.
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | // isMetadataDirectiveValid - check if metadata-directive is valid.
 | 
					
						
							|  |  |  | func isMetadataDirectiveValid(h http.Header) bool { | 
					
						
							| 
									
										
										
										
											2019-10-07 13:50:24 +08:00
										 |  |  | 	_, ok := h[http.CanonicalHeaderKey(xhttp.AmzMetadataDirective)] | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | 	if ok { | 
					
						
							|  |  |  | 		// Check atleast set metadata-directive is valid.
 | 
					
						
							|  |  |  | 		return (isMetadataCopy(h) || isMetadataReplace(h)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// By default if x-amz-metadata-directive is not we
 | 
					
						
							|  |  |  | 	// treat it as 'COPY' this function returns true.
 | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Check if the metadata COPY is requested.
 | 
					
						
							|  |  |  | func isMetadataCopy(h http.Header) bool { | 
					
						
							| 
									
										
										
										
											2019-10-07 13:50:24 +08:00
										 |  |  | 	return h.Get(xhttp.AmzMetadataDirective) == "COPY" | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Check if the metadata REPLACE is requested.
 | 
					
						
							|  |  |  | func isMetadataReplace(h http.Header) bool { | 
					
						
							| 
									
										
										
										
											2019-10-07 13:50:24 +08:00
										 |  |  | 	return h.Get(xhttp.AmzMetadataDirective) == "REPLACE" | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Splits an incoming path into bucket and object components.
 | 
					
						
							|  |  |  | func path2BucketAndObject(path string) (bucket, object string) { | 
					
						
							|  |  |  | 	// Skip the first element if it is '/', split the rest.
 | 
					
						
							| 
									
										
										
										
											2019-08-07 03:08:58 +08:00
										 |  |  | 	path = strings.TrimPrefix(path, SlashSeparator) | 
					
						
							|  |  |  | 	pathComponents := strings.SplitN(path, SlashSeparator, 2) | 
					
						
							| 
									
										
										
										
											2016-12-27 08:29:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Save the bucket and object extracted from path.
 | 
					
						
							|  |  |  | 	switch len(pathComponents) { | 
					
						
							|  |  |  | 	case 1: | 
					
						
							|  |  |  | 		bucket = pathComponents[0] | 
					
						
							|  |  |  | 	case 2: | 
					
						
							|  |  |  | 		bucket = pathComponents[0] | 
					
						
							|  |  |  | 		object = pathComponents[1] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return bucket, object | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-23 07:53:35 +08:00
										 |  |  | // userMetadataKeyPrefixes contains the prefixes of used-defined metadata keys.
 | 
					
						
							|  |  |  | // All values stored with a key starting with one of the following prefixes
 | 
					
						
							|  |  |  | // must be extracted from the header.
 | 
					
						
							|  |  |  | var userMetadataKeyPrefixes = []string{ | 
					
						
							|  |  |  | 	"X-Amz-Meta-", | 
					
						
							|  |  |  | 	"X-Minio-Meta-", | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-11 11:27:10 +08:00
										 |  |  | // extractMetadata extracts metadata from HTTP header and HTTP queryString.
 | 
					
						
							|  |  |  | func extractMetadata(ctx context.Context, r *http.Request) (metadata map[string]string, err error) { | 
					
						
							|  |  |  | 	query := r.URL.Query() | 
					
						
							|  |  |  | 	header := r.Header | 
					
						
							|  |  |  | 	metadata = make(map[string]string) | 
					
						
							|  |  |  | 	// Extract all query values.
 | 
					
						
							|  |  |  | 	err = extractMetadataFromMap(ctx, query, metadata) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Extract all header values.
 | 
					
						
							|  |  |  | 	err = extractMetadataFromMap(ctx, header, metadata) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-12-22 19:28:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-20 06:31:45 +08:00
										 |  |  | 	// Set content-type to default value if it is not set.
 | 
					
						
							|  |  |  | 	if _, ok := metadata["content-type"]; !ok { | 
					
						
							|  |  |  | 		metadata["content-type"] = "application/octet-stream" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-11 11:27:10 +08:00
										 |  |  | 	// Success.
 | 
					
						
							|  |  |  | 	return metadata, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // extractMetadata extracts metadata from map values.
 | 
					
						
							|  |  |  | func extractMetadataFromMap(ctx context.Context, v map[string][]string, m map[string]string) error { | 
					
						
							|  |  |  | 	if v == nil { | 
					
						
							|  |  |  | 		logger.LogIf(ctx, errInvalidArgument) | 
					
						
							|  |  |  | 		return errInvalidArgument | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-12-22 19:28:13 +08:00
										 |  |  | 	// Save all supported headers.
 | 
					
						
							| 
									
										
										
										
											2016-07-23 11:31:45 +08:00
										 |  |  | 	for _, supportedHeader := range supportedHeaders { | 
					
						
							| 
									
										
										
										
											2018-07-11 11:27:10 +08:00
										 |  |  | 		if value, ok := v[http.CanonicalHeaderKey(supportedHeader)]; ok { | 
					
						
							|  |  |  | 			m[supportedHeader] = value[0] | 
					
						
							|  |  |  | 		} else if value, ok := v[supportedHeader]; ok { | 
					
						
							|  |  |  | 			m[supportedHeader] = value[0] | 
					
						
							| 
									
										
										
										
											2016-07-23 11:31:45 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-11 11:27:10 +08:00
										 |  |  | 	for key := range v { | 
					
						
							| 
									
										
										
										
											2017-08-23 07:53:35 +08:00
										 |  |  | 		for _, prefix := range userMetadataKeyPrefixes { | 
					
						
							| 
									
										
										
										
											2018-07-11 11:27:10 +08:00
										 |  |  | 			if !strings.HasPrefix(strings.ToLower(key), strings.ToLower(prefix)) { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			value, ok := v[key] | 
					
						
							|  |  |  | 			if ok { | 
					
						
							| 
									
										
										
										
											2018-07-13 00:40:14 +08:00
										 |  |  | 				m[key] = strings.Join(value, ",") | 
					
						
							| 
									
										
										
										
											2017-08-23 07:53:35 +08:00
										 |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-07-23 11:31:45 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-11 11:27:10 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-12-20 08:14:04 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | // The Query string for the redirect URL the client is
 | 
					
						
							|  |  |  | // redirected on successful upload.
 | 
					
						
							|  |  |  | func getRedirectPostRawQuery(objInfo ObjectInfo) string { | 
					
						
							|  |  |  | 	redirectValues := make(url.Values) | 
					
						
							|  |  |  | 	redirectValues.Set("bucket", objInfo.Bucket) | 
					
						
							|  |  |  | 	redirectValues.Set("key", objInfo.Name) | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | 	redirectValues.Set("etag", "\""+objInfo.ETag+"\"") | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 	return redirectValues.Encode() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-30 09:35:11 +08:00
										 |  |  | // Returns access credentials in the request Authorization header.
 | 
					
						
							|  |  |  | func getReqAccessCred(r *http.Request, region string) (cred auth.Credentials) { | 
					
						
							| 
									
										
										
										
											2019-02-28 09:46:55 +08:00
										 |  |  | 	cred, _, _ = getReqAccessKeyV4(r, region, serviceS3) | 
					
						
							| 
									
										
										
										
											2018-11-07 22:40:03 +08:00
										 |  |  | 	if cred.AccessKey == "" { | 
					
						
							|  |  |  | 		cred, _, _ = getReqAccessKeyV2(r) | 
					
						
							| 
									
										
										
										
											2018-11-03 09:40:08 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-12-19 21:13:47 +08:00
										 |  |  | 	if cred.AccessKey == "" { | 
					
						
							|  |  |  | 		claims, owner, _ := webRequestAuthenticate(r) | 
					
						
							|  |  |  | 		if owner { | 
					
						
							|  |  |  | 			return globalServerConfig.GetCredential() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		cred, _ = globalIAMSys.GetUser(claims.Subject) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-11-30 09:35:11 +08:00
										 |  |  | 	return cred | 
					
						
							| 
									
										
										
										
											2018-11-03 09:40:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | // Extract request params to be sent with event notifiation.
 | 
					
						
							|  |  |  | func extractReqParams(r *http.Request) map[string]string { | 
					
						
							|  |  |  | 	if r == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2016-12-20 08:14:04 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-03 09:40:08 +08:00
										 |  |  | 	region := globalServerConfig.GetRegion() | 
					
						
							| 
									
										
										
										
											2018-11-30 09:35:11 +08:00
										 |  |  | 	cred := getReqAccessCred(r, region) | 
					
						
							| 
									
										
										
										
											2018-12-19 21:13:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 	// Success.
 | 
					
						
							|  |  |  | 	return map[string]string{ | 
					
						
							| 
									
										
										
										
											2018-11-03 09:40:08 +08:00
										 |  |  | 		"region":          region, | 
					
						
							| 
									
										
										
										
											2018-11-30 09:35:11 +08:00
										 |  |  | 		"accessKey":       cred.AccessKey, | 
					
						
							| 
									
										
										
										
											2018-07-03 05:40:18 +08:00
										 |  |  | 		"sourceIPAddress": handlers.GetSourceIP(r), | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 		// Add more fields here.
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-24 05:40:54 +08:00
										 |  |  | // Extract response elements to be sent with event notifiation.
 | 
					
						
							|  |  |  | func extractRespElements(w http.ResponseWriter) map[string]string { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return map[string]string{ | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 		"requestId":      w.Header().Get(xhttp.AmzRequestID), | 
					
						
							|  |  |  | 		"content-length": w.Header().Get(xhttp.ContentLength), | 
					
						
							| 
									
										
										
										
											2018-08-24 05:40:54 +08:00
										 |  |  | 		// Add more fields here.
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-28 08:02:04 +08:00
										 |  |  | // Trims away `aws-chunked` from the content-encoding header if present.
 | 
					
						
							|  |  |  | // Streaming signature clients can have custom content-encoding such as
 | 
					
						
							|  |  |  | // `aws-chunked,gzip` here we need to only save `gzip`.
 | 
					
						
							|  |  |  | // For more refer http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html
 | 
					
						
							|  |  |  | func trimAwsChunkedContentEncoding(contentEnc string) (trimmedContentEnc string) { | 
					
						
							|  |  |  | 	if contentEnc == "" { | 
					
						
							|  |  |  | 		return contentEnc | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var newEncs []string | 
					
						
							|  |  |  | 	for _, enc := range strings.Split(contentEnc, ",") { | 
					
						
							|  |  |  | 		if enc != streamingContentEncoding { | 
					
						
							|  |  |  | 			newEncs = append(newEncs, enc) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return strings.Join(newEncs, ",") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | // Validate form field size for s3 specification requirement.
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | func validateFormFieldSize(ctx context.Context, formValues http.Header) error { | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 	// Iterate over form values
 | 
					
						
							|  |  |  | 	for k := range formValues { | 
					
						
							|  |  |  | 		// Check if value's field exceeds S3 limit
 | 
					
						
							|  |  |  | 		if int64(len(formValues.Get(k))) > maxFormFieldSize { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 			logger.LogIf(ctx, errSizeUnexpected) | 
					
						
							|  |  |  | 			return errSizeUnexpected | 
					
						
							| 
									
										
										
										
											2016-12-20 08:14:04 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Success.
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-07-23 11:31:45 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-07-24 13:51:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-29 03:02:22 +08:00
										 |  |  | // Extract form fields and file data from a HTTP POST Policy
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | func extractPostPolicyFormValues(ctx context.Context, form *multipart.Form) (filePart io.ReadCloser, fileName string, fileSize int64, formValues http.Header, err error) { | 
					
						
							| 
									
										
										
										
											2016-07-24 13:51:12 +08:00
										 |  |  | 	/// HTML Form values
 | 
					
						
							| 
									
										
										
										
											2016-07-29 03:02:22 +08:00
										 |  |  | 	fileName = "" | 
					
						
							| 
									
										
										
										
											2017-02-03 02:45:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 	// Canonicalize the form values into http.Header.
 | 
					
						
							|  |  |  | 	formValues = make(http.Header) | 
					
						
							| 
									
										
										
										
											2017-02-03 02:45:00 +08:00
										 |  |  | 	for k, v := range form.Value { | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 		formValues[http.CanonicalHeaderKey(k)] = v | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Validate form values.
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	if err = validateFormFieldSize(ctx, formValues); err != nil { | 
					
						
							| 
									
										
										
										
											2017-03-14 05:41:13 +08:00
										 |  |  | 		return nil, "", 0, nil, err | 
					
						
							| 
									
										
										
										
											2017-02-03 02:45:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-02 05:19:11 +08:00
										 |  |  | 	// this means that filename="" was not specified for file key and Go has
 | 
					
						
							|  |  |  | 	// an ugly way of handling this situation. Refer here
 | 
					
						
							|  |  |  | 	// https://golang.org/src/mime/multipart/formdata.go#L61
 | 
					
						
							|  |  |  | 	if len(form.File) == 0 { | 
					
						
							|  |  |  | 		var b = &bytes.Buffer{} | 
					
						
							|  |  |  | 		for _, v := range formValues["File"] { | 
					
						
							|  |  |  | 			b.WriteString(v) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		fileSize = int64(b.Len()) | 
					
						
							|  |  |  | 		filePart = ioutil.NopCloser(b) | 
					
						
							|  |  |  | 		return filePart, fileName, fileSize, formValues, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-03 02:45:00 +08:00
										 |  |  | 	// Iterator until we find a valid File field and break
 | 
					
						
							|  |  |  | 	for k, v := range form.File { | 
					
						
							|  |  |  | 		canonicalFormName := http.CanonicalHeaderKey(k) | 
					
						
							|  |  |  | 		if canonicalFormName == "File" { | 
					
						
							|  |  |  | 			if len(v) == 0 { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 				logger.LogIf(ctx, errInvalidArgument) | 
					
						
							|  |  |  | 				return nil, "", 0, nil, errInvalidArgument | 
					
						
							| 
									
										
										
										
											2016-07-24 13:51:12 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-02-03 02:45:00 +08:00
										 |  |  | 			// Fetch fileHeader which has the uploaded file information
 | 
					
						
							|  |  |  | 			fileHeader := v[0] | 
					
						
							|  |  |  | 			// Set filename
 | 
					
						
							|  |  |  | 			fileName = fileHeader.Filename | 
					
						
							|  |  |  | 			// Open the uploaded part
 | 
					
						
							|  |  |  | 			filePart, err = fileHeader.Open() | 
					
						
							| 
									
										
										
										
											2017-02-10 04:37:32 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 				logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 				return nil, "", 0, nil, err | 
					
						
							| 
									
										
										
										
											2017-02-10 04:37:32 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-02-03 02:45:00 +08:00
										 |  |  | 			// Compute file size
 | 
					
						
							|  |  |  | 			fileSize, err = filePart.(io.Seeker).Seek(0, 2) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 				logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 				return nil, "", 0, nil, err | 
					
						
							| 
									
										
										
										
											2017-02-03 02:45:00 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			// Reset Seek to the beginning
 | 
					
						
							|  |  |  | 			_, err = filePart.(io.Seeker).Seek(0, 0) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 				logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 				return nil, "", 0, nil, err | 
					
						
							| 
									
										
										
										
											2017-02-03 02:45:00 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			// File found and ready for reading
 | 
					
						
							|  |  |  | 			break | 
					
						
							| 
									
										
										
										
											2016-07-24 13:51:12 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-02-03 02:45:00 +08:00
										 |  |  | 	return filePart, fileName, fileSize, formValues, nil | 
					
						
							| 
									
										
										
										
											2016-07-24 13:51:12 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Log headers and body.
 | 
					
						
							|  |  |  | func httpTraceAll(f http.HandlerFunc) http.HandlerFunc { | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	return func(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2019-06-27 13:41:12 +08:00
										 |  |  | 		if !globalHTTPTrace.HasSubscribers() { | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 			f.ServeHTTP(w, r) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		trace := Trace(f, true, w, r) | 
					
						
							| 
									
										
										
										
											2019-06-27 13:41:12 +08:00
										 |  |  | 		globalHTTPTrace.Publish(trace) | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Log only the headers.
 | 
					
						
							|  |  |  | func httpTraceHdrs(f http.HandlerFunc) http.HandlerFunc { | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	return func(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2019-06-27 13:41:12 +08:00
										 |  |  | 		if !globalHTTPTrace.HasSubscribers() { | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 			f.ServeHTTP(w, r) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		trace := Trace(f, false, w, r) | 
					
						
							| 
									
										
										
										
											2019-06-27 13:41:12 +08:00
										 |  |  | 		globalHTTPTrace.Publish(trace) | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-11-15 08:56:24 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Returns "/bucketName/objectName" for path-style or virtual-host-style requests.
 | 
					
						
							| 
									
										
										
										
											2019-02-23 11:18:01 +08:00
										 |  |  | func getResource(path string, host string, domains []string) (string, error) { | 
					
						
							|  |  |  | 	if len(domains) == 0 { | 
					
						
							| 
									
										
										
										
											2017-11-15 08:56:24 +08:00
										 |  |  | 		return path, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// If virtual-host-style is enabled construct the "resource" properly.
 | 
					
						
							|  |  |  | 	if strings.Contains(host, ":") { | 
					
						
							|  |  |  | 		// In bucket.mydomain.com:9000, strip out :9000
 | 
					
						
							|  |  |  | 		var err error | 
					
						
							|  |  |  | 		if host, _, err = net.SplitHostPort(host); err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 			reqInfo := (&logger.ReqInfo{}).AppendTags("host", host) | 
					
						
							|  |  |  | 			reqInfo.AppendTags("path", path) | 
					
						
							|  |  |  | 			ctx := logger.SetReqInfo(context.Background(), reqInfo) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2017-11-15 08:56:24 +08:00
										 |  |  | 			return "", err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-23 11:18:01 +08:00
										 |  |  | 	for _, domain := range domains { | 
					
						
							|  |  |  | 		if !strings.HasSuffix(host, "."+domain) { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		bucket := strings.TrimSuffix(host, "."+domain) | 
					
						
							| 
									
										
										
										
											2019-08-07 03:08:58 +08:00
										 |  |  | 		return SlashSeparator + pathJoin(bucket, path), nil | 
					
						
							| 
									
										
										
										
											2017-11-15 08:56:24 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-23 11:18:01 +08:00
										 |  |  | 	return path, nil | 
					
						
							| 
									
										
										
										
											2017-11-15 08:56:24 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // If none of the http routes match respond with MethodNotAllowed
 | 
					
						
							|  |  |  | func notFoundHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 	writeErrorResponse(context.Background(), w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL, guessIsBrowserReq(r)) | 
					
						
							| 
									
										
										
										
											2017-11-15 08:56:24 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-07-06 11:41:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-03 15:08:12 +08:00
										 |  |  | // If the API version does not match with current version.
 | 
					
						
							|  |  |  | func versionMismatchHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	path := r.URL.Path | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case strings.HasPrefix(path, minioReservedBucketPath+"/peer/"): | 
					
						
							|  |  |  | 		writeVersionMismatchResponse(r.Context(), w, errorCodes.ToAPIErr(ErrInvalidAPIVersion), r.URL, false) | 
					
						
							|  |  |  | 	case strings.HasPrefix(path, minioReservedBucketPath+"/storage/"): | 
					
						
							|  |  |  | 		writeVersionMismatchResponse(r.Context(), w, errorCodes.ToAPIErr(ErrInvalidAPIVersion), r.URL, false) | 
					
						
							|  |  |  | 	case strings.HasPrefix(path, minioReservedBucketPath+"/lock/"): | 
					
						
							|  |  |  | 		writeVersionMismatchResponse(r.Context(), w, errorCodes.ToAPIErr(ErrInvalidAPIVersion), r.URL, false) | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		writeVersionMismatchResponse(r.Context(), w, errorCodes.ToAPIErr(ErrInvalidAPIVersion), r.URL, true) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-06 11:41:35 +08:00
										 |  |  | // gets host name for current node
 | 
					
						
							| 
									
										
										
										
											2019-07-19 00:58:37 +08:00
										 |  |  | func getHostName(r *http.Request) (hostName string) { | 
					
						
							| 
									
										
										
										
											2019-07-06 11:41:35 +08:00
										 |  |  | 	if globalIsDistXL { | 
					
						
							| 
									
										
										
										
											2019-07-19 00:58:37 +08:00
										 |  |  | 		hostName = GetLocalPeer(globalEndpoints) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		hostName = r.Host | 
					
						
							| 
									
										
										
										
											2019-07-06 11:41:35 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } |