| 
									
										
										
										
											2015-02-15 16:48:15 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2015, 2016, 2017 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2015-02-15 16:48:15 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2015-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) { | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 	w.Header().Set(xhttp.ServerInfo, "MinIO/"+ReleaseTag) | 
					
						
							| 
									
										
										
										
											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
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | func setObjectHeaders(w http.ResponseWriter, objInfo ObjectInfo, rs *HTTPRangeSpec) (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
 | 
					
						
							|  |  |  | 	tags, _ := url.ParseQuery(objInfo.UserTags) | 
					
						
							|  |  |  | 	tagCount := len(tags) | 
					
						
							| 
									
										
										
										
											2020-05-26 07:51:32 +08:00
										 |  |  | 	if tagCount > 0 { | 
					
						
							|  |  |  | 		w.Header()[xhttp.AmzTagCount] = []string{strconv.Itoa(tagCount)} | 
					
						
							| 
									
										
										
										
											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
 | 
					
						
							|  |  |  | 		if strings.EqualFold(k, xhttp.AmzMetaUnencryptedContentLength) || strings.EqualFold(k, xhttp.AmzMetaUnencryptedContentMD5) { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-05-26 07:51:32 +08:00
										 |  |  | 		var isSet bool | 
					
						
							|  |  |  | 		for _, userMetadataPrefix := range userMetadataKeyPrefixes { | 
					
						
							|  |  |  | 			if !strings.HasPrefix(k, userMetadataPrefix) { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			w.Header()[strings.ToLower(k)] = []string{v} | 
					
						
							|  |  |  | 			isSet = true | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !isSet { | 
					
						
							|  |  |  | 			w.Header().Set(k, v) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-07-13 03:45:17 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-07 04:16:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-07 04:16:22 +08:00
										 |  |  | 	// for providing ranged content
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 	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} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	if objInfo.ReplicationStatus.String() != "" { | 
					
						
							|  |  |  | 		w.Header()[xhttp.AmzBucketReplicationStatus] = []string{objInfo.ReplicationStatus.String()} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if lc, err := globalLifecycleSys.Get(objInfo.Bucket); err == nil { | 
					
						
							|  |  |  | 		ruleID, expiryTime := lc.PredictExpiryTime(lifecycle.ObjectOpts{ | 
					
						
							|  |  |  | 			Name:         objInfo.Name, | 
					
						
							|  |  |  | 			UserTags:     objInfo.UserTags, | 
					
						
							|  |  |  | 			VersionID:    objInfo.VersionID, | 
					
						
							|  |  |  | 			ModTime:      objInfo.ModTime, | 
					
						
							|  |  |  | 			IsLatest:     objInfo.IsLatest, | 
					
						
							|  |  |  | 			DeleteMarker: objInfo.DeleteMarker, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		if !expiryTime.IsZero() { | 
					
						
							|  |  |  | 			w.Header()[xhttp.AmzExpiration] = []string{ | 
					
						
							|  |  |  | 				fmt.Sprintf(`expiry-date="%s", rule-id="%s"`, expiryTime.Format(http.TimeFormat), ruleID), | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-21 10:22:09 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2015-03-11 16:01:49 +08:00
										 |  |  | } |