mirror of
https://github.com/minio/minio.git
synced 2025-02-26 04:49:16 -05:00
API/handler: CopyObject make it behave in accordance with S3 spec. (#2155)
Fixes bugs found while running s3verify tool - fixes #2152
This commit is contained in:
parent
3341fe9b28
commit
5102a5877e
@ -265,7 +265,7 @@ var errorCodeResponse = map[APIErrorCode]APIError{
|
|||||||
},
|
},
|
||||||
ErrPreconditionFailed: {
|
ErrPreconditionFailed: {
|
||||||
Code: "PreconditionFailed",
|
Code: "PreconditionFailed",
|
||||||
Description: "At least one of the preconditions you specified did not hold.",
|
Description: "At least one of the pre-conditions you specified did not hold",
|
||||||
HTTPStatusCode: http.StatusPreconditionFailed,
|
HTTPStatusCode: http.StatusPreconditionFailed,
|
||||||
},
|
},
|
||||||
ErrRequestTimeTooSkewed: {
|
ErrRequestTimeTooSkewed: {
|
||||||
|
@ -188,7 +188,7 @@ func checkLastModified(w http.ResponseWriter, r *http.Request, modtime time.Time
|
|||||||
delete(h, "Content-Type")
|
delete(h, "Content-Type")
|
||||||
delete(h, "Content-Length")
|
delete(h, "Content-Length")
|
||||||
delete(h, "Content-Range")
|
delete(h, "Content-Range")
|
||||||
w.WriteHeader(http.StatusPreconditionFailed)
|
writeErrorResponse(w, r, ErrPreconditionFailed, r.URL.Path)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -376,6 +376,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
writeErrorResponse(w, r, toAPIErrorCode(err), objectSource)
|
writeErrorResponse(w, r, toAPIErrorCode(err), objectSource)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify before writing.
|
// Verify before writing.
|
||||||
|
|
||||||
// Verify x-amz-copy-source-if-modified-since and
|
// Verify x-amz-copy-source-if-modified-since and
|
||||||
@ -385,8 +386,11 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify x-amz-copy-source-if-match and
|
if objInfo.MD5Sum != "" {
|
||||||
// x-amz-copy-source-if-none-match.
|
w.Header().Set("ETag", "\""+objInfo.MD5Sum+"\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify x-amz-copy-source-if-match and x-amz-copy-source-if-none-match.
|
||||||
if checkCopySourceETag(w, r) {
|
if checkCopySourceETag(w, r) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -452,6 +456,11 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
// modtime is the modification time of the resource to be served, or
|
// modtime is the modification time of the resource to be served, or
|
||||||
// IsZero(). return value is whether this request is now complete.
|
// IsZero(). return value is whether this request is now complete.
|
||||||
func checkCopySourceLastModified(w http.ResponseWriter, r *http.Request, modtime time.Time) bool {
|
func checkCopySourceLastModified(w http.ResponseWriter, r *http.Request, modtime time.Time) bool {
|
||||||
|
// writer always has quoted string
|
||||||
|
// transform reader's etag to
|
||||||
|
if r.Method != "PUT" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if modtime.IsZero() || modtime.Equal(time.Unix(0, 0)) {
|
if modtime.IsZero() || modtime.Equal(time.Unix(0, 0)) {
|
||||||
// If the object doesn't have a modtime (IsZero), or the modtime
|
// If the object doesn't have a modtime (IsZero), or the modtime
|
||||||
// is obviously garbage (Unix time == 0), then ignore modtimes
|
// is obviously garbage (Unix time == 0), then ignore modtimes
|
||||||
@ -483,7 +492,7 @@ func checkCopySourceLastModified(w http.ResponseWriter, r *http.Request, modtime
|
|||||||
delete(h, "Content-Type")
|
delete(h, "Content-Type")
|
||||||
delete(h, "Content-Length")
|
delete(h, "Content-Length")
|
||||||
delete(h, "Content-Range")
|
delete(h, "Content-Range")
|
||||||
w.WriteHeader(http.StatusPreconditionFailed)
|
writeErrorResponse(w, r, ErrPreconditionFailed, r.URL.Path)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -498,18 +507,21 @@ func checkCopySourceLastModified(w http.ResponseWriter, r *http.Request, modtime
|
|||||||
// headers. The return value is whether this request is now considered
|
// headers. The return value is whether this request is now considered
|
||||||
// complete.
|
// complete.
|
||||||
func checkCopySourceETag(w http.ResponseWriter, r *http.Request) bool {
|
func checkCopySourceETag(w http.ResponseWriter, r *http.Request) bool {
|
||||||
|
// writer always has quoted string
|
||||||
|
// transform reader's etag to
|
||||||
|
if r.Method != "PUT" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
etag := w.Header().Get("ETag")
|
etag := w.Header().Get("ETag")
|
||||||
// Tag must be provided...
|
// Tag must be provided...
|
||||||
if etag == "" {
|
if etag == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if inm := r.Header.Get("x-amz-copy-source-if-none-match"); inm != "" {
|
if inm := r.Header.Get("x-amz-copy-source-if-none-match"); inm != "" {
|
||||||
// Return the object only if its entity tag (ETag) is different
|
// Return the object only if its entity tag (ETag) is
|
||||||
// from the one specified; otherwise, return a 304 (not modified).
|
// different from the one specified; otherwise, return a 304
|
||||||
if r.Method != "PUT" {
|
// (not modified).
|
||||||
return false
|
if isETagEqual(inm, etag) || isETagEqual(inm, "*") {
|
||||||
}
|
|
||||||
if inm == etag || inm == "*" {
|
|
||||||
h := w.Header()
|
h := w.Header()
|
||||||
// Remove Content headers if set
|
// Remove Content headers if set
|
||||||
delete(h, "Content-Type")
|
delete(h, "Content-Type")
|
||||||
@ -518,19 +530,16 @@ func checkCopySourceETag(w http.ResponseWriter, r *http.Request) bool {
|
|||||||
w.WriteHeader(http.StatusNotModified)
|
w.WriteHeader(http.StatusNotModified)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
} else if inm := r.Header.Get("x-amz-copy-source-if-match"); inm != "" {
|
} else if inm := r.Header.Get("x-amz-copy-source-if-match"); !isETagEqual(inm, "") {
|
||||||
// Return the object only if its entity tag (ETag) is the same
|
// Return the object only if its entity tag (ETag) is the same
|
||||||
// as the one specified; otherwise, return a 412 (precondition failed).
|
// as the one specified; otherwise, return a 412 (precondition failed).
|
||||||
if r.Method != "PUT" {
|
if !isETagEqual(inm, etag) {
|
||||||
return false
|
|
||||||
}
|
|
||||||
if inm != etag {
|
|
||||||
h := w.Header()
|
h := w.Header()
|
||||||
// Remove Content headers if set
|
// Remove Content headers if set
|
||||||
delete(h, "Content-Type")
|
delete(h, "Content-Type")
|
||||||
delete(h, "Content-Length")
|
delete(h, "Content-Length")
|
||||||
delete(h, "Content-Range")
|
delete(h, "Content-Range")
|
||||||
w.WriteHeader(http.StatusPreconditionFailed)
|
writeErrorResponse(w, r, ErrPreconditionFailed, r.URL.Path)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user