| 
									
										
										
										
											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-02-15 16:48:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2015-02-11 19:23:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2015-02-11 19:23:15 +08:00
										 |  |  | 	"encoding/xml" | 
					
						
							| 
									
										
										
										
											2017-01-11 08:43:48 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2015-02-11 19:23:15 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2015-02-11 19:23:15 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2020-03-13 03:37:27 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2017-01-11 08:43:48 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/minio/minio/cmd/crypto" | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 	xhttp "github.com/minio/minio/cmd/http" | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/bucket/lifecycle" | 
					
						
							| 
									
										
										
										
											2015-02-11 19:23:15 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-11 08:43:48 +08:00
										 |  |  | // Returns a hexadecimal representation of time at the
 | 
					
						
							|  |  |  | // time response is sent to the client.
 | 
					
						
							|  |  |  | func mustGetRequestID(t time.Time) string { | 
					
						
							|  |  |  | 	return fmt.Sprintf("%X", t.UnixNano()) | 
					
						
							| 
									
										
										
										
											2015-07-15 05:29:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:45:12 +08:00
										 |  |  | // setEventStreamHeaders to allow proxies to avoid buffering proxy responses
 | 
					
						
							|  |  |  | func setEventStreamHeaders(w http.ResponseWriter) { | 
					
						
							|  |  |  | 	w.Header().Set(xhttp.ContentType, "text/event-stream") | 
					
						
							|  |  |  | 	w.Header().Set(xhttp.CacheControl, "no-cache") // nginx to turn off buffering
 | 
					
						
							|  |  |  | 	w.Header().Set("X-Accel-Buffering", "no")      // nginx to turn off buffering
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-24 08:46:48 +08:00
										 |  |  | // Write http common headers
 | 
					
						
							| 
									
										
										
										
											2016-01-08 16:40:06 +08:00
										 |  |  | func setCommonHeaders(w http.ResponseWriter) { | 
					
						
							| 
									
										
										
										
											2020-11-20 01:16:02 +08:00
										 |  |  | 	// Set the "Server" http header.
 | 
					
						
							|  |  |  | 	w.Header().Set(xhttp.ServerInfo, "MinIO") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-24 07:05:40 +08:00
										 |  |  | 	// Set `x-amz-bucket-region` only if region is set on the server
 | 
					
						
							|  |  |  | 	// by default minio uses an empty region.
 | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 	if region := globalServerRegion; region != "" { | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 		w.Header().Set(xhttp.AmzBucketRegion, region) | 
					
						
							| 
									
										
										
										
											2017-06-24 07:05:40 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 	w.Header().Set(xhttp.AcceptRanges, "bytes") | 
					
						
							| 
									
										
										
										
											2018-09-26 03:39:46 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Remove sensitive information
 | 
					
						
							|  |  |  | 	crypto.RemoveSensitiveHeaders(w.Header()) | 
					
						
							| 
									
										
										
										
											2015-02-11 19:23:15 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-07 04:16:22 +08:00
										 |  |  | // Encodes the response headers into XML format.
 | 
					
						
							|  |  |  | func encodeResponse(response interface{}) []byte { | 
					
						
							| 
									
										
										
										
											2015-02-11 19:23:15 +08:00
										 |  |  | 	var bytesBuffer bytes.Buffer | 
					
						
							| 
									
										
										
										
											2015-12-10 07:38:40 +08:00
										 |  |  | 	bytesBuffer.WriteString(xml.Header) | 
					
						
							| 
									
										
										
										
											2015-10-04 15:27:49 +08:00
										 |  |  | 	e := xml.NewEncoder(&bytesBuffer) | 
					
						
							| 
									
										
										
										
											2015-09-10 06:11:37 +08:00
										 |  |  | 	e.Encode(response) | 
					
						
							| 
									
										
										
										
											2015-02-11 19:23:15 +08:00
										 |  |  | 	return bytesBuffer.Bytes() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | // Encodes the response headers into JSON format.
 | 
					
						
							|  |  |  | func encodeResponseJSON(response interface{}) []byte { | 
					
						
							|  |  |  | 	var bytesBuffer bytes.Buffer | 
					
						
							|  |  |  | 	e := json.NewEncoder(&bytesBuffer) | 
					
						
							|  |  |  | 	e.Encode(response) | 
					
						
							|  |  |  | 	return bytesBuffer.Bytes() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-11 00:22:15 +08:00
										 |  |  | // Write parts count
 | 
					
						
							|  |  |  | func setPartsCountHeaders(w http.ResponseWriter, objInfo ObjectInfo) { | 
					
						
							|  |  |  | 	if strings.Contains(objInfo.ETag, "-") && len(objInfo.Parts) > 0 { | 
					
						
							|  |  |  | 		w.Header()[xhttp.AmzMpPartsCount] = []string{strconv.Itoa(len(objInfo.Parts))} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-24 08:46:48 +08:00
										 |  |  | // Write object header
 | 
					
						
							| 
									
										
										
										
											2020-10-02 06:41:12 +08:00
										 |  |  | func setObjectHeaders(w http.ResponseWriter, objInfo ObjectInfo, rs *HTTPRangeSpec, opts ObjectOptions) (err error) { | 
					
						
							| 
									
										
										
										
											2015-07-29 10:33:56 +08:00
										 |  |  | 	// set common headers
 | 
					
						
							| 
									
										
										
										
											2016-03-07 04:16:22 +08:00
										 |  |  | 	setCommonHeaders(w) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-23 11:31:45 +08:00
										 |  |  | 	// Set last modified time.
 | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 	lastModified := objInfo.ModTime.UTC().Format(http.TimeFormat) | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 	w.Header().Set(xhttp.LastModified, lastModified) | 
					
						
							| 
									
										
										
										
											2016-03-07 04:16:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-23 11:31:45 +08:00
										 |  |  | 	// Set Etag if available.
 | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | 	if objInfo.ETag != "" { | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 		w.Header()[xhttp.ETag] = []string{"\"" + objInfo.ETag + "\""} | 
					
						
							| 
									
										
										
										
											2016-01-29 11:57:23 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-23 11:31:45 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-13 18:56:16 +08:00
										 |  |  | 	if objInfo.ContentType != "" { | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 		w.Header().Set(xhttp.ContentType, objInfo.ContentType) | 
					
						
							| 
									
										
										
										
											2017-10-13 18:56:16 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if objInfo.ContentEncoding != "" { | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 		w.Header().Set(xhttp.ContentEncoding, objInfo.ContentEncoding) | 
					
						
							| 
									
										
										
										
											2017-10-13 18:56:16 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-01 03:01:25 +08:00
										 |  |  | 	if !objInfo.Expires.IsZero() { | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 		w.Header().Set(xhttp.Expires, objInfo.Expires.UTC().Format(http.TimeFormat)) | 
					
						
							| 
									
										
										
										
											2019-03-01 03:01:25 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-21 13:01:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-11 12:21:13 +08:00
										 |  |  | 	if globalCacheConfig.Enabled { | 
					
						
							|  |  |  | 		w.Header().Set(xhttp.XCache, objInfo.CacheStatus.String()) | 
					
						
							|  |  |  | 		w.Header().Set(xhttp.XCacheLookup, objInfo.CacheLookupStatus.String()) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Set tag count if object has tags
 | 
					
						
							| 
									
										
										
										
											2020-11-03 07:15:12 +08:00
										 |  |  | 	if len(objInfo.UserTags) > 0 { | 
					
						
							|  |  |  | 		tags, _ := url.ParseQuery(objInfo.UserTags) | 
					
						
							|  |  |  | 		if len(tags) > 0 { | 
					
						
							|  |  |  | 			w.Header()[xhttp.AmzTagCount] = []string{strconv.Itoa(len(tags))} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-21 00:45:59 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-23 11:31:45 +08:00
										 |  |  | 	// Set all other user defined metadata.
 | 
					
						
							| 
									
										
										
										
											2016-07-13 03:45:17 +08:00
										 |  |  | 	for k, v := range objInfo.UserDefined { | 
					
						
							| 
									
										
										
										
											2020-05-29 05:36:38 +08:00
										 |  |  | 		if strings.HasPrefix(strings.ToLower(k), ReservedMetadataPrefixLower) { | 
					
						
							| 
									
										
										
										
											2018-03-02 08:15:53 +08:00
										 |  |  | 			// Do not need to send any internal metadata
 | 
					
						
							|  |  |  | 			// values to client.
 | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-11 23:29:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// https://github.com/google/security-research/security/advisories/GHSA-76wf-9vgp-pj7w
 | 
					
						
							| 
									
										
										
										
											2021-02-04 12:41:33 +08:00
										 |  |  | 		if equals(k, xhttp.AmzMetaUnencryptedContentLength, xhttp.AmzMetaUnencryptedContentMD5) { | 
					
						
							| 
									
										
										
										
											2020-08-11 23:29:29 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-02-04 12:41:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-26 07:51:32 +08:00
										 |  |  | 		var isSet bool | 
					
						
							|  |  |  | 		for _, userMetadataPrefix := range userMetadataKeyPrefixes { | 
					
						
							| 
									
										
										
										
											2021-02-04 12:41:33 +08:00
										 |  |  | 			if !strings.HasPrefix(strings.ToLower(k), strings.ToLower(userMetadataPrefix)) { | 
					
						
							| 
									
										
										
										
											2020-05-26 07:51:32 +08:00
										 |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			w.Header()[strings.ToLower(k)] = []string{v} | 
					
						
							|  |  |  | 			isSet = true | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-02-04 12:41:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-26 07:51:32 +08:00
										 |  |  | 		if !isSet { | 
					
						
							|  |  |  | 			w.Header().Set(k, v) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-07-13 03:45:17 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-07 04:16:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-02 06:41:12 +08:00
										 |  |  | 	var start, rangeLen int64 | 
					
						
							| 
									
										
										
										
											2020-05-25 02:19:17 +08:00
										 |  |  | 	totalObjectSize, err := objInfo.GetActualSize() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-08 05:37:10 +08:00
										 |  |  | 	if rs == nil && opts.PartNumber > 0 { | 
					
						
							|  |  |  | 		rs = partNumberToRangeSpec(objInfo, opts.PartNumber) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-09 05:12:42 +08:00
										 |  |  | 	// For providing ranged content
 | 
					
						
							|  |  |  | 	start, rangeLen, err = rs.GetOffsetLength(totalObjectSize) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2015-07-29 10:33:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Set content length.
 | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 	w.Header().Set(xhttp.ContentLength, strconv.FormatInt(rangeLen, 10)) | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 	if rs != nil { | 
					
						
							|  |  |  | 		contentRange := fmt.Sprintf("bytes %d-%d/%d", start, start+rangeLen-1, totalObjectSize) | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 		w.Header().Set(xhttp.ContentRange, contentRange) | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	// Set the relevant version ID as part of the response header.
 | 
					
						
							|  |  |  | 	if objInfo.VersionID != "" { | 
					
						
							|  |  |  | 		w.Header()[xhttp.AmzVersionID] = []string{objInfo.VersionID} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-10 05:19:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	if objInfo.ReplicationStatus.String() != "" { | 
					
						
							|  |  |  | 		w.Header()[xhttp.AmzBucketReplicationStatus] = []string{objInfo.ReplicationStatus.String()} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-10 05:19:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if lc, err := globalLifecycleSys.Get(objInfo.Bucket); err == nil { | 
					
						
							| 
									
										
										
										
											2021-03-10 05:19:08 +08:00
										 |  |  | 		if opts.VersionID == "" { | 
					
						
							|  |  |  | 			if ruleID, expiryTime := lc.PredictExpiryTime(lifecycle.ObjectOpts{ | 
					
						
							|  |  |  | 				Name:             objInfo.Name, | 
					
						
							|  |  |  | 				UserTags:         objInfo.UserTags, | 
					
						
							|  |  |  | 				VersionID:        objInfo.VersionID, | 
					
						
							|  |  |  | 				ModTime:          objInfo.ModTime, | 
					
						
							|  |  |  | 				IsLatest:         objInfo.IsLatest, | 
					
						
							|  |  |  | 				DeleteMarker:     objInfo.DeleteMarker, | 
					
						
							|  |  |  | 				SuccessorModTime: objInfo.SuccessorModTime, | 
					
						
							|  |  |  | 			}); !expiryTime.IsZero() { | 
					
						
							|  |  |  | 				w.Header()[xhttp.AmzExpiration] = []string{ | 
					
						
							|  |  |  | 					fmt.Sprintf(`expiry-date="%s", rule-id="%s"`, expiryTime.Format(http.TimeFormat), ruleID), | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-11-17 14:52:40 +08:00
										 |  |  | 		if objInfo.TransitionStatus == lifecycle.TransitionComplete { | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  | 			// Check if object is being restored. For more information on x-amz-restore header see
 | 
					
						
							|  |  |  | 			// https://docs.aws.amazon.com/AmazonS3/latest/API/API_HeadObject.html#API_HeadObject_ResponseSyntax
 | 
					
						
							|  |  |  | 			if onDisk := isRestoredObjectOnDisk(objInfo.UserDefined); !onDisk { | 
					
						
							|  |  |  | 				w.Header()[xhttp.AmzStorageClass] = []string{objInfo.TransitionTier} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ruleID, transitionTime := lc.PredictTransitionTime(lifecycle.ObjectOpts{ | 
					
						
							|  |  |  | 			Name:             objInfo.Name, | 
					
						
							|  |  |  | 			UserTags:         objInfo.UserTags, | 
					
						
							|  |  |  | 			VersionID:        objInfo.VersionID, | 
					
						
							|  |  |  | 			ModTime:          objInfo.ModTime, | 
					
						
							|  |  |  | 			IsLatest:         objInfo.IsLatest, | 
					
						
							|  |  |  | 			DeleteMarker:     objInfo.DeleteMarker, | 
					
						
							|  |  |  | 			TransitionStatus: objInfo.TransitionStatus, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		if !transitionTime.IsZero() { | 
					
						
							|  |  |  | 			// This header is a MinIO centric extension to show expected transition date in a similar spirit as x-amz-expiration
 | 
					
						
							|  |  |  | 			w.Header()[xhttp.MinIOTransition] = []string{ | 
					
						
							|  |  |  | 				fmt.Sprintf(`transition-date="%s", rule-id="%s"`, transitionTime.Format(http.TimeFormat), ruleID), | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-17 14:52:40 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2015-03-11 16:01:49 +08:00
										 |  |  | } |