From a2e344bf30c7750c0a152f046a70d327f2f5ccf5 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Mon, 8 Apr 2019 16:54:46 -0700 Subject: [PATCH] Preserve ETag case for S3 compatibility (#7498) Most hadoop distributions hortonworks, cloudera all depend on aws-sdk-java 1.7.x to 1.10.x - the releases which have bugs related case sensitive check for ETag header. Go changes the case of the headers set to be canonical but only preserves them when set through a direct map. This fixes most compatibility issues we have had in the past supporting older hadoop distributions. --- cmd/api-headers.go | 3 ++- cmd/bucket-handlers.go | 2 +- cmd/object-handlers-common.go | 4 ++-- cmd/object-handlers.go | 6 +++--- cmd/test-utils_test.go | 2 +- staticcheck.conf | 2 +- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/cmd/api-headers.go b/cmd/api-headers.go index e0aae773d..969303188 100644 --- a/cmd/api-headers.go +++ b/cmd/api-headers.go @@ -76,7 +76,7 @@ func setObjectHeaders(w http.ResponseWriter, objInfo ObjectInfo, rs *HTTPRangeSp // Set Etag if available. if objInfo.ETag != "" { - w.Header().Set("ETag", "\""+objInfo.ETag+"\"") + w.Header()["ETag"] = []string{"\"" + objInfo.ETag + "\""} } if objInfo.ContentType != "" { @@ -90,6 +90,7 @@ func setObjectHeaders(w http.ResponseWriter, objInfo ObjectInfo, rs *HTTPRangeSp if !objInfo.Expires.IsZero() { w.Header().Set("Expires", objInfo.Expires.UTC().Format(http.TimeFormat)) } + // Set all other user defined metadata. for k, v := range objInfo.UserDefined { if hasPrefix(k, ReservedMetadataPrefix) { diff --git a/cmd/bucket-handlers.go b/cmd/bucket-handlers.go index fa44013d9..3e33664a6 100644 --- a/cmd/bucket-handlers.go +++ b/cmd/bucket-handlers.go @@ -669,7 +669,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h } location := getObjectLocation(r, globalDomainNames, bucket, object) - w.Header().Set("ETag", `"`+objInfo.ETag+`"`) + w.Header()["ETag"] = []string{`"` + objInfo.ETag + `"`} w.Header().Set("Location", location) // Notify object created event. diff --git a/cmd/object-handlers-common.go b/cmd/object-handlers-common.go index c99d9fe26..b0c6614e6 100644 --- a/cmd/object-handlers-common.go +++ b/cmd/object-handlers-common.go @@ -71,7 +71,7 @@ func checkCopyObjectPreconditions(ctx context.Context, w http.ResponseWriter, r w.Header().Set("Last-Modified", objInfo.ModTime.UTC().Format(http.TimeFormat)) if objInfo.ETag != "" { - w.Header().Set("ETag", "\""+objInfo.ETag+"\"") + w.Header()["ETag"] = []string{"\"" + objInfo.ETag + "\""} } } // x-amz-copy-source-if-modified-since: Return the object only if it has been modified @@ -166,7 +166,7 @@ func checkPreconditions(ctx context.Context, w http.ResponseWriter, r *http.Requ w.Header().Set("Last-Modified", objInfo.ModTime.UTC().Format(http.TimeFormat)) if objInfo.ETag != "" { - w.Header().Set("ETag", "\""+objInfo.ETag+"\"") + w.Header()["ETag"] = []string{"\"" + objInfo.ETag + "\""} } } // If-Modified-Since : Return the object only if it has been modified since the specified time, diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index c497dada6..ab8faa9af 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -1258,7 +1258,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req } else if hasServerSideEncryptionHeader(r.Header) { etag = getDecryptedETag(r.Header, objInfo, false) } - w.Header().Set("ETag", "\""+etag+"\"") + w.Header()["ETag"] = []string{"\"" + etag + "\""} if objectAPI.IsEncryptionSupported() { if crypto.IsEncrypted(objInfo.UserDefined) { @@ -1941,7 +1941,7 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http } else if isEncrypted { etag = tryDecryptETag(objectEncryptionKey, partInfo.ETag, crypto.SSEC.IsRequested(r.Header)) } - w.Header().Set("ETag", "\""+etag+"\"") + w.Header()["ETag"] = []string{"\"" + etag + "\""} writeSuccessResponseHeadersOnly(w) } @@ -2304,7 +2304,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite } // Set etag. - w.Header().Set("ETag", "\""+objInfo.ETag+"\"") + w.Header()["ETag"] = []string{"\"" + objInfo.ETag + "\""} // Write success response. writeSuccessResponseXML(w, encodedSuccessResponse) diff --git a/cmd/test-utils_test.go b/cmd/test-utils_test.go index de3dc7cdf..f06b5125d 100644 --- a/cmd/test-utils_test.go +++ b/cmd/test-utils_test.go @@ -2413,7 +2413,7 @@ func uploadTestObject(t *testing.T, apiRouter http.Handler, creds auth.Credentia rec = httptest.NewRecorder() apiRouter.ServeHTTP(rec, req) checkRespErr(rec, http.StatusOK) - etag := rec.Header().Get("ETag") + etag := rec.Header()["ETag"][0] if etag == "" { t.Fatalf("Unexpected empty etag") } diff --git a/staticcheck.conf b/staticcheck.conf index ee0604cd7..3d47c8b2a 100644 --- a/staticcheck.conf +++ b/staticcheck.conf @@ -1 +1 @@ -checks = ["all", "-ST1005", "-ST1000", "-SA4000", "-SA9004", "-SA1019"] +checks = ["all", "-ST1005", "-ST1000", "-SA4000", "-SA9004", "-SA1019", "-SA1008"]