|
|
@ -213,7 +213,7 @@ func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r |
|
|
|
objInfo.UserDefined = objectlock.FilterObjectLockMetadata(objInfo.UserDefined, getRetPerms != ErrNone, legalHoldPerms != ErrNone) |
|
|
|
objInfo.UserDefined = objectlock.FilterObjectLockMetadata(objInfo.UserDefined, getRetPerms != ErrNone, legalHoldPerms != ErrNone) |
|
|
|
|
|
|
|
|
|
|
|
if objectAPI.IsEncryptionSupported() { |
|
|
|
if objectAPI.IsEncryptionSupported() { |
|
|
|
if _, err = DecryptObjectInfo(&objInfo, r.Header); err != nil { |
|
|
|
if _, err = DecryptObjectInfo(&objInfo, r); err != nil { |
|
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) |
|
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
@ -371,23 +371,43 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req |
|
|
|
|
|
|
|
|
|
|
|
// Get request range.
|
|
|
|
// Get request range.
|
|
|
|
var rs *HTTPRangeSpec |
|
|
|
var rs *HTTPRangeSpec |
|
|
|
|
|
|
|
var rangeErr error |
|
|
|
rangeHeader := r.Header.Get(xhttp.Range) |
|
|
|
rangeHeader := r.Header.Get(xhttp.Range) |
|
|
|
if rangeHeader != "" { |
|
|
|
if rangeHeader != "" { |
|
|
|
if rs, err = parseRequestRangeSpec(rangeHeader); err != nil { |
|
|
|
rs, rangeErr = parseRequestRangeSpec(rangeHeader) |
|
|
|
// Handle only errInvalidRange. Ignore other
|
|
|
|
} |
|
|
|
// parse error and treat it as regular Get
|
|
|
|
|
|
|
|
// request like Amazon S3.
|
|
|
|
// Validate pre-conditions if any.
|
|
|
|
if err == errInvalidRange { |
|
|
|
opts.CheckPrecondFn = func(oi ObjectInfo) bool { |
|
|
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidRange), r.URL, guessIsBrowserReq(r)) |
|
|
|
if objectAPI.IsEncryptionSupported() { |
|
|
|
return |
|
|
|
if _, err := DecryptObjectInfo(&oi, r); err != nil { |
|
|
|
|
|
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) |
|
|
|
|
|
|
|
return true |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if checkPreconditions(ctx, w, r, oi, opts) { |
|
|
|
|
|
|
|
return true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
logger.LogIf(ctx, err, logger.Application) |
|
|
|
// Handle only errInvalidRange. Ignore other
|
|
|
|
|
|
|
|
// parse error and treat it as regular Get
|
|
|
|
|
|
|
|
// request like Amazon S3.
|
|
|
|
|
|
|
|
if rangeErr == errInvalidRange { |
|
|
|
|
|
|
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrInvalidRange), r.URL, guessIsBrowserReq(r)) |
|
|
|
|
|
|
|
return true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if rangeErr != nil { |
|
|
|
|
|
|
|
logger.LogIf(ctx, rangeErr, logger.Application) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
gr, err := getObjectNInfo(ctx, bucket, object, rs, r.Header, readLock, opts) |
|
|
|
gr, err := getObjectNInfo(ctx, bucket, object, rs, r.Header, readLock, opts) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
|
|
|
|
if isErrPreconditionFailed(err) { |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
if globalBucketVersioningSys.Enabled(bucket) && gr != nil { |
|
|
|
if globalBucketVersioningSys.Enabled(bucket) && gr != nil { |
|
|
|
// Versioning enabled quite possibly object is deleted might be delete-marker
|
|
|
|
// Versioning enabled quite possibly object is deleted might be delete-marker
|
|
|
|
// if present set the headers, no idea why AWS S3 sets these headers.
|
|
|
|
// if present set the headers, no idea why AWS S3 sets these headers.
|
|
|
@ -409,18 +429,6 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req |
|
|
|
// filter object lock metadata if permission does not permit
|
|
|
|
// filter object lock metadata if permission does not permit
|
|
|
|
objInfo.UserDefined = objectlock.FilterObjectLockMetadata(objInfo.UserDefined, getRetPerms != ErrNone, legalHoldPerms != ErrNone) |
|
|
|
objInfo.UserDefined = objectlock.FilterObjectLockMetadata(objInfo.UserDefined, getRetPerms != ErrNone, legalHoldPerms != ErrNone) |
|
|
|
|
|
|
|
|
|
|
|
if objectAPI.IsEncryptionSupported() { |
|
|
|
|
|
|
|
if _, err = DecryptObjectInfo(&objInfo, r.Header); err != nil { |
|
|
|
|
|
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Validate pre-conditions if any.
|
|
|
|
|
|
|
|
if checkPreconditions(ctx, w, r, objInfo, opts) { |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Set encryption response headers
|
|
|
|
// Set encryption response headers
|
|
|
|
if objectAPI.IsEncryptionSupported() { |
|
|
|
if objectAPI.IsEncryptionSupported() { |
|
|
|
if crypto.IsEncrypted(objInfo.UserDefined) { |
|
|
|
if crypto.IsEncrypted(objInfo.UserDefined) { |
|
|
@ -455,7 +463,8 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req |
|
|
|
|
|
|
|
|
|
|
|
// Write object content to response body
|
|
|
|
// Write object content to response body
|
|
|
|
if _, err = io.Copy(httpWriter, gr); err != nil { |
|
|
|
if _, err = io.Copy(httpWriter, gr); err != nil { |
|
|
|
if !httpWriter.HasWritten() && !statusCodeWritten { // write error response only if no data or headers has been written to client yet
|
|
|
|
if !httpWriter.HasWritten() && !statusCodeWritten { |
|
|
|
|
|
|
|
// write error response only if no data or headers has been written to client yet
|
|
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) |
|
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) |
|
|
|
} |
|
|
|
} |
|
|
|
return |
|
|
|
return |
|
|
@ -551,23 +560,6 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Get request range.
|
|
|
|
|
|
|
|
var rs *HTTPRangeSpec |
|
|
|
|
|
|
|
rangeHeader := r.Header.Get(xhttp.Range) |
|
|
|
|
|
|
|
if rangeHeader != "" { |
|
|
|
|
|
|
|
if rs, err = parseRequestRangeSpec(rangeHeader); err != nil { |
|
|
|
|
|
|
|
// Handle only errInvalidRange. Ignore other
|
|
|
|
|
|
|
|
// parse error and treat it as regular Get
|
|
|
|
|
|
|
|
// request like Amazon S3.
|
|
|
|
|
|
|
|
if err == errInvalidRange { |
|
|
|
|
|
|
|
writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrInvalidRange)) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.LogIf(ctx, err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
objInfo, err := getObjectInfo(ctx, bucket, object, opts) |
|
|
|
objInfo, err := getObjectInfo(ctx, bucket, object, opts) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
if globalBucketVersioningSys.Enabled(bucket) { |
|
|
|
if globalBucketVersioningSys.Enabled(bucket) { |
|
|
@ -590,14 +582,40 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re |
|
|
|
objInfo.UserDefined = objectlock.FilterObjectLockMetadata(objInfo.UserDefined, getRetPerms != ErrNone, legalHoldPerms != ErrNone) |
|
|
|
objInfo.UserDefined = objectlock.FilterObjectLockMetadata(objInfo.UserDefined, getRetPerms != ErrNone, legalHoldPerms != ErrNone) |
|
|
|
|
|
|
|
|
|
|
|
if objectAPI.IsEncryptionSupported() { |
|
|
|
if objectAPI.IsEncryptionSupported() { |
|
|
|
if _, err = DecryptObjectInfo(&objInfo, r.Header); err != nil { |
|
|
|
if _, err = DecryptObjectInfo(&objInfo, r); err != nil { |
|
|
|
writeErrorResponseHeadersOnly(w, toAPIError(ctx, err)) |
|
|
|
writeErrorResponseHeadersOnly(w, toAPIError(ctx, err)) |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Validate pre-conditions if any.
|
|
|
|
|
|
|
|
if checkPreconditions(ctx, w, r, objInfo, opts) { |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Get request range.
|
|
|
|
|
|
|
|
var rs *HTTPRangeSpec |
|
|
|
|
|
|
|
rangeHeader := r.Header.Get(xhttp.Range) |
|
|
|
|
|
|
|
if rangeHeader != "" { |
|
|
|
|
|
|
|
if rs, err = parseRequestRangeSpec(rangeHeader); err != nil { |
|
|
|
|
|
|
|
// Handle only errInvalidRange. Ignore other
|
|
|
|
|
|
|
|
// parse error and treat it as regular Get
|
|
|
|
|
|
|
|
// request like Amazon S3.
|
|
|
|
|
|
|
|
if err == errInvalidRange { |
|
|
|
|
|
|
|
writeErrorResponseHeadersOnly(w, errorCodes.ToAPIErr(ErrInvalidRange)) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger.LogIf(ctx, err) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Set encryption response headers
|
|
|
|
// Set encryption response headers
|
|
|
|
if objectAPI.IsEncryptionSupported() { |
|
|
|
if objectAPI.IsEncryptionSupported() { |
|
|
|
|
|
|
|
if _, err = DecryptObjectInfo(&objInfo, r); err != nil { |
|
|
|
|
|
|
|
writeErrorResponseHeadersOnly(w, toAPIError(ctx, err)) |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
if crypto.IsEncrypted(objInfo.UserDefined) { |
|
|
|
if crypto.IsEncrypted(objInfo.UserDefined) { |
|
|
|
switch { |
|
|
|
switch { |
|
|
|
case crypto.S3.IsEncrypted(objInfo.UserDefined): |
|
|
|
case crypto.S3.IsEncrypted(objInfo.UserDefined): |
|
|
@ -614,11 +632,6 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Validate pre-conditions if any.
|
|
|
|
|
|
|
|
if checkPreconditions(ctx, w, r, objInfo, opts) { |
|
|
|
|
|
|
|
return |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Set standard object headers.
|
|
|
|
// Set standard object headers.
|
|
|
|
if err = setObjectHeaders(w, objInfo, rs); err != nil { |
|
|
|
if err = setObjectHeaders(w, objInfo, rs); err != nil { |
|
|
|
writeErrorResponseHeadersOnly(w, toAPIError(ctx, err)) |
|
|
|
writeErrorResponseHeadersOnly(w, toAPIError(ctx, err)) |
|
|
@ -898,16 +911,25 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re |
|
|
|
cpSrcDstSame := isStringEqual(pathJoin(srcBucket, srcObject), pathJoin(dstBucket, dstObject)) |
|
|
|
cpSrcDstSame := isStringEqual(pathJoin(srcBucket, srcObject), pathJoin(dstBucket, dstObject)) |
|
|
|
|
|
|
|
|
|
|
|
getObjectNInfo := objectAPI.GetObjectNInfo |
|
|
|
getObjectNInfo := objectAPI.GetObjectNInfo |
|
|
|
|
|
|
|
if api.CacheAPI() != nil { |
|
|
|
|
|
|
|
getObjectNInfo = api.CacheAPI().GetObjectNInfo |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var lock = noLock |
|
|
|
var lock = noLock |
|
|
|
if !cpSrcDstSame { |
|
|
|
if !cpSrcDstSame { |
|
|
|
lock = readLock |
|
|
|
lock = readLock |
|
|
|
} |
|
|
|
} |
|
|
|
checkCopyPrecondFn := func(o ObjectInfo, encETag string) bool { |
|
|
|
checkCopyPrecondFn := func(o ObjectInfo) bool { |
|
|
|
return checkCopyObjectPreconditions(ctx, w, r, o, encETag) |
|
|
|
if objectAPI.IsEncryptionSupported() { |
|
|
|
|
|
|
|
if _, err := DecryptObjectInfo(&o, r); err != nil { |
|
|
|
|
|
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) |
|
|
|
|
|
|
|
return true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return checkCopyObjectPreconditions(ctx, w, r, o) |
|
|
|
} |
|
|
|
} |
|
|
|
getOpts.CheckCopyPrecondFn = checkCopyPrecondFn |
|
|
|
getOpts.CheckPrecondFn = checkCopyPrecondFn |
|
|
|
srcOpts.CheckCopyPrecondFn = checkCopyPrecondFn |
|
|
|
|
|
|
|
var rs *HTTPRangeSpec |
|
|
|
var rs *HTTPRangeSpec |
|
|
|
gr, err := getObjectNInfo(ctx, srcBucket, srcObject, rs, r.Header, lock, getOpts) |
|
|
|
gr, err := getObjectNInfo(ctx, srcBucket, srcObject, rs, r.Header, lock, getOpts) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
@ -1790,22 +1812,31 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt |
|
|
|
|
|
|
|
|
|
|
|
// Get request range.
|
|
|
|
// Get request range.
|
|
|
|
var rs *HTTPRangeSpec |
|
|
|
var rs *HTTPRangeSpec |
|
|
|
rangeHeader := r.Header.Get(xhttp.AmzCopySourceRange) |
|
|
|
var parseRangeErr error |
|
|
|
if rangeHeader != "" { |
|
|
|
if rangeHeader := r.Header.Get(xhttp.AmzCopySourceRange); rangeHeader != "" { |
|
|
|
var parseRangeErr error |
|
|
|
rs, parseRangeErr = parseCopyPartRangeSpec(rangeHeader) |
|
|
|
if rs, parseRangeErr = parseCopyPartRangeSpec(rangeHeader); parseRangeErr != nil { |
|
|
|
} |
|
|
|
logger.GetReqInfo(ctx).AppendTags("rangeHeader", rangeHeader) |
|
|
|
|
|
|
|
|
|
|
|
checkCopyPartPrecondFn := func(o ObjectInfo) bool { |
|
|
|
|
|
|
|
if objectAPI.IsEncryptionSupported() { |
|
|
|
|
|
|
|
if _, err := DecryptObjectInfo(&o, r); err != nil { |
|
|
|
|
|
|
|
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) |
|
|
|
|
|
|
|
return true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if checkCopyObjectPartPreconditions(ctx, w, r, o) { |
|
|
|
|
|
|
|
return true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if parseRangeErr != nil { |
|
|
|
logger.LogIf(ctx, parseRangeErr) |
|
|
|
logger.LogIf(ctx, parseRangeErr) |
|
|
|
writeCopyPartErr(ctx, w, parseRangeErr, r.URL, guessIsBrowserReq(r)) |
|
|
|
writeCopyPartErr(ctx, w, parseRangeErr, r.URL, guessIsBrowserReq(r)) |
|
|
|
return |
|
|
|
// Range header mismatch is pre-condition like failure
|
|
|
|
|
|
|
|
// so return true to indicate Range precondition failed.
|
|
|
|
|
|
|
|
return true |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return false |
|
|
|
} |
|
|
|
} |
|
|
|
checkCopyPartPrecondFn := func(o ObjectInfo, encETag string) bool { |
|
|
|
getOpts.CheckPrecondFn = checkCopyPartPrecondFn |
|
|
|
return checkCopyObjectPartPreconditions(ctx, w, r, o, encETag) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
getOpts.CheckCopyPrecondFn = checkCopyPartPrecondFn |
|
|
|
|
|
|
|
srcOpts.CheckCopyPrecondFn = checkCopyPartPrecondFn |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
gr, err := getObjectNInfo(ctx, srcBucket, srcObject, rs, r.Header, readLock, getOpts) |
|
|
|
gr, err := getObjectNInfo(ctx, srcBucket, srcObject, rs, r.Header, readLock, getOpts) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|