storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/api-headers.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2015, 2016, 2017 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cmd 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "encoding/xml" 23 "fmt" 24 "net/http" 25 "net/url" 26 "strconv" 27 "strings" 28 "time" 29 30 "storj.io/minio/cmd/crypto" 31 xhttp "storj.io/minio/cmd/http" 32 "storj.io/minio/pkg/bucket/lifecycle" 33 ) 34 35 // Returns a hexadecimal representation of time at the 36 // time response is sent to the client. 37 func mustGetRequestID(t time.Time) string { 38 return fmt.Sprintf("%X", t.UnixNano()) 39 } 40 41 // setEventStreamHeaders to allow proxies to avoid buffering proxy responses 42 func setEventStreamHeaders(w http.ResponseWriter) { 43 w.Header().Set(xhttp.ContentType, "text/event-stream") 44 w.Header().Set(xhttp.CacheControl, "no-cache") // nginx to turn off buffering 45 w.Header().Set("X-Accel-Buffering", "no") // nginx to turn off buffering 46 } 47 48 // Write http common headers 49 func setCommonHeaders(w http.ResponseWriter) { 50 // Set the "Server" http header. 51 w.Header().Set(xhttp.ServerInfo, "MinIO") 52 53 // Set `x-amz-bucket-region` only if region is set on the server 54 // by default minio uses an empty region. 55 if region := globalServerRegion; region != "" { 56 w.Header().Set(xhttp.AmzBucketRegion, region) 57 } 58 w.Header().Set(xhttp.AcceptRanges, "bytes") 59 60 // Remove sensitive information 61 crypto.RemoveSensitiveHeaders(w.Header()) 62 } 63 64 // Encodes the response headers into XML format. 65 func EncodeResponse(response interface{}) []byte { 66 var bytesBuffer bytes.Buffer 67 bytesBuffer.WriteString(xml.Header) 68 e := xml.NewEncoder(&bytesBuffer) 69 e.Encode(response) 70 return bytesBuffer.Bytes() 71 } 72 73 // Encodes the response headers into JSON format. 74 func encodeResponseJSON(response interface{}) []byte { 75 var bytesBuffer bytes.Buffer 76 e := json.NewEncoder(&bytesBuffer) 77 e.Encode(response) 78 return bytesBuffer.Bytes() 79 } 80 81 // Write parts count 82 func setPartsCountHeaders(w http.ResponseWriter, objInfo ObjectInfo) { 83 if strings.Contains(objInfo.ETag, "-") && len(objInfo.Parts) > 0 { 84 w.Header()[xhttp.AmzMpPartsCount] = []string{strconv.Itoa(len(objInfo.Parts))} 85 } 86 } 87 88 // Write object header 89 func setObjectHeaders(w http.ResponseWriter, objInfo ObjectInfo, rs *HTTPRangeSpec, opts ObjectOptions) (err error) { 90 // set common headers 91 setCommonHeaders(w) 92 93 // Set last modified time. 94 lastModified := objInfo.ModTime.UTC().Format(http.TimeFormat) 95 w.Header().Set(xhttp.LastModified, lastModified) 96 97 // Set Etag if available. 98 if objInfo.ETag != "" { 99 w.Header()[xhttp.ETag] = []string{"\"" + objInfo.ETag + "\""} 100 } 101 102 if objInfo.ContentType != "" { 103 w.Header().Set(xhttp.ContentType, objInfo.ContentType) 104 } 105 106 if objInfo.ContentEncoding != "" { 107 w.Header().Set(xhttp.ContentEncoding, objInfo.ContentEncoding) 108 } 109 110 if !objInfo.Expires.IsZero() { 111 w.Header().Set(xhttp.Expires, objInfo.Expires.UTC().Format(http.TimeFormat)) 112 } 113 114 if globalCacheConfig.Enabled { 115 w.Header().Set(xhttp.XCache, objInfo.CacheStatus.String()) 116 w.Header().Set(xhttp.XCacheLookup, objInfo.CacheLookupStatus.String()) 117 } 118 119 // Set tag count if object has tags 120 if len(objInfo.UserTags) > 0 { 121 tags, _ := url.ParseQuery(objInfo.UserTags) 122 if len(tags) > 0 { 123 w.Header()[xhttp.AmzTagCount] = []string{strconv.Itoa(len(tags))} 124 } 125 } 126 127 // Set all other user defined metadata. 128 for k, v := range objInfo.UserDefined { 129 if strings.HasPrefix(strings.ToLower(k), ReservedMetadataPrefixLower) { 130 // Do not need to send any internal metadata 131 // values to client. 132 continue 133 } 134 135 // https://github.com/google/security-research/security/advisories/GHSA-76wf-9vgp-pj7w 136 if equals(k, xhttp.AmzMetaUnencryptedContentLength, xhttp.AmzMetaUnencryptedContentMD5) { 137 continue 138 } 139 140 var isSet bool 141 for _, userMetadataPrefix := range userMetadataKeyPrefixes { 142 if !strings.HasPrefix(strings.ToLower(k), strings.ToLower(userMetadataPrefix)) { 143 continue 144 } 145 w.Header()[strings.ToLower(k)] = []string{v} 146 isSet = true 147 break 148 } 149 150 if !isSet { 151 w.Header().Set(k, v) 152 } 153 } 154 155 var start, rangeLen int64 156 totalObjectSize, err := objInfo.GetActualSize() 157 if err != nil { 158 return err 159 } 160 161 if rs == nil && opts.PartNumber > 0 { 162 rs = partNumberToRangeSpec(objInfo, opts.PartNumber) 163 } 164 165 // For providing ranged content 166 start, rangeLen, err = rs.GetOffsetLength(totalObjectSize) 167 if err != nil { 168 return err 169 } 170 171 // Set content length. 172 w.Header().Set(xhttp.ContentLength, strconv.FormatInt(rangeLen, 10)) 173 if rs != nil { 174 contentRange := fmt.Sprintf("bytes %d-%d/%d", start, start+rangeLen-1, totalObjectSize) 175 w.Header().Set(xhttp.ContentRange, contentRange) 176 } 177 178 // Set the relevant version ID as part of the response header. 179 if objInfo.VersionID != "" { 180 w.Header()[xhttp.AmzVersionID] = []string{objInfo.VersionID} 181 } 182 183 if objInfo.ReplicationStatus.String() != "" { 184 w.Header()[xhttp.AmzBucketReplicationStatus] = []string{objInfo.ReplicationStatus.String()} 185 } 186 187 if lc, err := globalLifecycleSys.Get(objInfo.Bucket); err == nil { 188 if opts.VersionID == "" { 189 if ruleID, expiryTime := lc.PredictExpiryTime(lifecycle.ObjectOpts{ 190 Name: objInfo.Name, 191 UserTags: objInfo.UserTags, 192 VersionID: objInfo.VersionID, 193 ModTime: objInfo.ModTime, 194 IsLatest: objInfo.IsLatest, 195 DeleteMarker: objInfo.DeleteMarker, 196 SuccessorModTime: objInfo.SuccessorModTime, 197 }); !expiryTime.IsZero() { 198 w.Header()[xhttp.AmzExpiration] = []string{ 199 fmt.Sprintf(`expiry-date="%s", rule-id="%s"`, expiryTime.Format(http.TimeFormat), ruleID), 200 } 201 } 202 } 203 if objInfo.TransitionStatus == lifecycle.TransitionComplete { 204 w.Header()[xhttp.AmzStorageClass] = []string{objInfo.StorageClass} 205 } 206 } 207 208 return nil 209 }