api: extract http headers with some supported header list. (#2268)

This commit is contained in:
Harshavardhana 2016-07-22 20:31:45 -07:00 committed by GitHub
parent 55cb55675c
commit f85d94288d
5 changed files with 91 additions and 56 deletions

View File

@ -62,20 +62,19 @@ func setObjectHeaders(w http.ResponseWriter, objInfo ObjectInfo, contentRange *h
// set common headers
setCommonHeaders(w)
// set object-related metadata headers
// Set content length.
w.Header().Set("Content-Length", strconv.FormatInt(objInfo.Size, 10))
// Set last modified time.
lastModified := objInfo.ModTime.UTC().Format(http.TimeFormat)
w.Header().Set("Last-Modified", lastModified)
if objInfo.ContentType != "" {
w.Header().Set("Content-Type", objInfo.ContentType)
}
// Set Etag if available.
if objInfo.MD5Sum != "" {
w.Header().Set("ETag", "\""+objInfo.MD5Sum+"\"")
}
if objInfo.ContentEncoding != "" {
w.Header().Set("Content-Encoding", objInfo.ContentEncoding)
}
w.Header().Set("Content-Length", strconv.FormatInt(objInfo.Size, 10))
// Set all other user defined metadata.
for k, v := range objInfo.UserDefined {
w.Header().Set(k, v)
}

View File

@ -314,12 +314,15 @@ func (fs fsObjects) GetObjectInfo(bucket, object string) (ObjectInfo, error) {
return ObjectInfo{}, toObjectErr(err, bucket, object)
}
if len(fsMeta.Meta) == 0 {
fsMeta.Meta = make(map[string]string)
}
// Guess content-type from the extension if possible.
contentType := fsMeta.Meta["content-type"]
if contentType == "" {
if fsMeta.Meta["content-type"] == "" {
if objectExt := filepath.Ext(object); objectExt != "" {
if content, ok := mimedb.DB[strings.ToLower(strings.TrimPrefix(objectExt, "."))]; ok {
contentType = content.ContentType
fsMeta.Meta["content-type"] = content.ContentType
}
}
}
@ -332,7 +335,7 @@ func (fs fsObjects) GetObjectInfo(bucket, object string) (ObjectInfo, error) {
Size: fi.Size,
IsDir: fi.Mode.IsDir(),
MD5Sum: fsMeta.Meta["md5Sum"],
ContentType: contentType,
ContentType: fsMeta.Meta["content-type"],
ContentEncoding: fsMeta.Meta["content-encoding"],
UserDefined: fsMeta.Meta,
}, nil

View File

@ -19,6 +19,7 @@ package main
import (
"io"
"net/http"
"strings"
)
// Validates location constraint in PutBucket request body.
@ -58,3 +59,39 @@ func isValidLocationConstraint(r *http.Request) (s3Error APIErrorCode) {
}
return s3Error
}
// Supported headers that needs to be extracted.
var supportedHeaders = []string{
"content-type",
"cache-control",
"content-encoding",
"content-disposition",
// Add more supported headers here.
}
// extractMetadataFromHeader extracts metadata from HTTP header.
func extractMetadataFromHeader(header http.Header) map[string]string {
metadata := make(map[string]string)
// Save standard supported headers.
for _, supportedHeader := range supportedHeaders {
canonicalHeader := http.CanonicalHeaderKey(supportedHeader)
// HTTP headers are case insensitive, look for both canonical
// and non canonical entries.
if _, ok := header[canonicalHeader]; ok {
metadata[supportedHeader] = header.Get(canonicalHeader)
} else if _, ok := header[supportedHeader]; ok {
metadata[supportedHeader] = header.Get(supportedHeader)
}
}
// Go through all other headers for any additional headers that needs to be saved.
for key := range header {
cKey := http.CanonicalHeaderKey(key)
if strings.HasPrefix(cKey, "X-Amz-Meta-") {
metadata[cKey] = header.Get(cKey)
} else if strings.HasPrefix(key, "X-Minio-Meta-") {
metadata[cKey] = header.Get(cKey)
}
}
// Return.
return metadata
}

View File

@ -21,6 +21,7 @@ import (
"encoding/xml"
"io/ioutil"
"net/http"
"reflect"
"testing"
)
@ -84,3 +85,36 @@ func TestIsValidLocationContraint(t *testing.T) {
}
}
}
// Tests validate metadata extraction from http headers.
func TestExtractMetadataHeaders(t *testing.T) {
testCases := []struct {
header http.Header
metadata map[string]string
}{
// Validate if there a known 'content-type'.
{
header: http.Header{
"Content-Type": []string{"image/png"},
},
metadata: map[string]string{
"content-type": "image/png",
},
},
// Validate if there are no keys to extract.
{
header: http.Header{
"test-1": []string{"123"},
},
metadata: map[string]string{},
},
}
// Validate if the extracting headers.
for i, testCase := range testCases {
metadata := extractMetadataFromHeader(testCase.header)
if !reflect.DeepEqual(metadata, testCase.metadata) {
t.Fatalf("Test %d failed: Expected \"%#v\", got \"%#v\"", i+1, testCase.metadata, metadata)
}
}
}

View File

@ -325,12 +325,8 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
// Save metadata.
metadata := make(map[string]string)
// Save other metadata if available.
if objInfo.ContentType != "" {
metadata["content-type"] = objInfo.ContentType
}
if objInfo.ContentEncoding != "" {
metadata["content-encoding"] = objInfo.ContentEncoding
}
metadata = objInfo.UserDefined
// Do not set `md5sum` as CopyObject will not keep the
// same md5sum as the source.
@ -392,27 +388,10 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
return
}
// Save metadata.
metadata := make(map[string]string)
// Extract metadata to be saved from incoming HTTP header.
metadata := extractMetadataFromHeader(r.Header)
// Make sure we hex encode md5sum here.
metadata["md5Sum"] = hex.EncodeToString(md5Bytes)
// Save other metadata if available.
contentType := r.Header.Get("Content-Type")
if contentType != "" {
metadata["content-type"] = contentType
}
contentEncoding := r.Header.Get("Content-Encoding")
if contentEncoding != "" {
metadata["content-encoding"] = contentEncoding
}
for key := range r.Header {
cKey := http.CanonicalHeaderKey(key)
if strings.HasPrefix(cKey, "X-Amz-Meta-") {
metadata[cKey] = r.Header.Get(cKey)
} else if strings.HasPrefix(key, "X-Minio-Meta-") {
metadata[cKey] = r.Header.Get(cKey)
}
}
var md5Sum string
switch getRequestAuthType(r) {
@ -472,25 +451,8 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
}
}
// Save metadata.
metadata := make(map[string]string)
// Save other metadata if available.
contentType := r.Header.Get("Content-Type")
if contentType != "" {
metadata["content-type"] = contentType
}
contentEncoding := r.Header.Get("Content-Encoding")
if contentEncoding != "" {
metadata["content-encoding"] = contentEncoding
}
for key := range r.Header {
cKey := http.CanonicalHeaderKey(key)
if strings.HasPrefix(cKey, "X-Amz-Meta-") {
metadata[cKey] = r.Header.Get(cKey)
} else if strings.HasPrefix(key, "X-Minio-Meta-") {
metadata[cKey] = r.Header.Get(cKey)
}
}
// Extract metadata that needs to be saved.
metadata := extractMetadataFromHeader(r.Header)
uploadID, err := api.ObjectAPI.NewMultipartUpload(bucket, object, metadata)
if err != nil {