mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
Make PutObject a nop for an object which ends with "/" and size is '0' (#3603)
This helps majority of S3 compatible applications while not returning an error upon directory create request. Fixes #2965
This commit is contained in:
parent
c3f7d1026f
commit
51fa4f7fe3
@ -115,13 +115,11 @@ func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter,
|
||||
return
|
||||
}
|
||||
|
||||
// If Content-Length is unknown or zero, deny the request. PutBucketNotification
|
||||
// always needs a Content-Length if incoming request is not chunked.
|
||||
if !contains(r.TransferEncoding, "chunked") {
|
||||
if r.ContentLength == -1 {
|
||||
writeErrorResponse(w, ErrMissingContentLength, r.URL)
|
||||
return
|
||||
}
|
||||
// If Content-Length is unknown or zero, deny the request.
|
||||
// PutBucketNotification always needs a Content-Length.
|
||||
if r.ContentLength == -1 || r.ContentLength == 0 {
|
||||
writeErrorResponse(w, ErrMissingContentLength, r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Reads the incoming notification configuration.
|
||||
|
@ -142,18 +142,15 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht
|
||||
}
|
||||
|
||||
// If Content-Length is unknown or zero, deny the
|
||||
// request. PutBucketPolicy always needs a Content-Length if
|
||||
// incoming request is not chunked.
|
||||
if !contains(r.TransferEncoding, "chunked") {
|
||||
if r.ContentLength == -1 || r.ContentLength == 0 {
|
||||
writeErrorResponse(w, ErrMissingContentLength, r.URL)
|
||||
return
|
||||
}
|
||||
// If Content-Length is greater than maximum allowed policy size.
|
||||
if r.ContentLength > maxAccessPolicySize {
|
||||
writeErrorResponse(w, ErrEntityTooLarge, r.URL)
|
||||
return
|
||||
}
|
||||
// request. PutBucketPolicy always needs a Content-Length.
|
||||
if r.ContentLength == -1 || r.ContentLength == 0 {
|
||||
writeErrorResponse(w, ErrMissingContentLength, r.URL)
|
||||
return
|
||||
}
|
||||
// If Content-Length is greater than maximum allowed policy size.
|
||||
if r.ContentLength > maxAccessPolicySize {
|
||||
writeErrorResponse(w, ErrEntityTooLarge, r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Read access policy up to maxAccessPolicySize.
|
||||
|
@ -542,6 +542,12 @@ func (fs fsObjects) GetObjectInfo(bucket, object string) (ObjectInfo, error) {
|
||||
// Additionally writes `fs.json` which carries the necessary metadata
|
||||
// for future object operations.
|
||||
func (fs fsObjects) PutObject(bucket string, object string, size int64, data io.Reader, metadata map[string]string, sha256sum string) (objInfo ObjectInfo, err error) {
|
||||
// This is a special case with size as '0' and object ends with
|
||||
// a slash separator, we treat it like a valid operation and
|
||||
// return success.
|
||||
if isObjectDir(object, size) {
|
||||
return dirObjectInfo(bucket, object, size, metadata), nil
|
||||
}
|
||||
if err = checkPutObjectArgs(bucket, object, fs); err != nil {
|
||||
return ObjectInfo{}, err
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
humanize "github.com/dustin/go-humanize"
|
||||
)
|
||||
@ -54,6 +55,32 @@ func isRemoteDisk(disk StorageAPI) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
// Checks if the object is a directory, this logic uses
|
||||
// if size == 0 and object ends with slashSeparator then
|
||||
// returns true.
|
||||
func isObjectDir(object string, size int64) bool {
|
||||
return strings.HasSuffix(object, slashSeparator) && size == 0
|
||||
}
|
||||
|
||||
// Converts just bucket, object metadata into ObjectInfo datatype.
|
||||
func dirObjectInfo(bucket, object string, size int64, metadata map[string]string) ObjectInfo {
|
||||
// This is a special case with size as '0' and object ends with
|
||||
// a slash separator, we treat it like a valid operation and
|
||||
// return success.
|
||||
md5Sum := metadata["md5Sum"]
|
||||
delete(metadata, "md5Sum")
|
||||
return ObjectInfo{
|
||||
Bucket: bucket,
|
||||
Name: object,
|
||||
ModTime: time.Now().UTC(),
|
||||
ContentType: "application/octet-stream",
|
||||
IsDir: true,
|
||||
Size: size,
|
||||
MD5Sum: md5Sum,
|
||||
UserDefined: metadata,
|
||||
}
|
||||
}
|
||||
|
||||
// House keeping code for FS/XL and distributed Minio setup.
|
||||
func houseKeeping(storageDisks []StorageAPI) error {
|
||||
var wg = &sync.WaitGroup{}
|
||||
|
@ -409,7 +409,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
||||
return
|
||||
}
|
||||
}
|
||||
if size == -1 && !contains(r.TransferEncoding, "chunked") {
|
||||
if size == -1 {
|
||||
writeErrorResponse(w, ErrMissingContentLength, r.URL)
|
||||
return
|
||||
}
|
||||
|
@ -130,6 +130,49 @@ func (s *TestSuiteCommon) TestBucketSQSNotificationWebHook(c *C) {
|
||||
verifyError(c, response, "InvalidArgument", "A specified destination ARN does not exist or is not well-formed. Verify the destination ARN.", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func (s *TestSuiteCommon) TestObjectDir(c *C) {
|
||||
bucketName := getRandomBucketName()
|
||||
// HTTP request to create the bucket.
|
||||
request, err := newTestSignedRequest("PUT", getMakeBucketURL(s.endPoint, bucketName),
|
||||
0, nil, s.accessKey, s.secretKey, s.signer)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
client := http.Client{Transport: s.transport}
|
||||
// execute the request.
|
||||
response, err := client.Do(request)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
// assert the http response status code.
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
request, err = newTestSignedRequest("PUT", getPutObjectURL(s.endPoint, bucketName, "my-object-directory/"),
|
||||
0, nil, s.accessKey, s.secretKey, s.signer)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
client = http.Client{Transport: s.transport}
|
||||
// execute the HTTP request.
|
||||
response, err = client.Do(request)
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
// assert the http response status code.
|
||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||
|
||||
request, err = newTestSignedRequest("PUT", getPutObjectURL(s.endPoint, bucketName, "my-object-directory/"),
|
||||
0, nil, s.accessKey, s.secretKey, s.signer)
|
||||
c.Assert(err, IsNil)
|
||||
|
||||
helloReader := bytes.NewReader([]byte("Hello, World"))
|
||||
request.ContentLength = helloReader.Size()
|
||||
request.Body = ioutil.NopCloser(helloReader)
|
||||
|
||||
client = http.Client{Transport: s.transport}
|
||||
// execute the HTTP request.
|
||||
response, err = client.Do(request)
|
||||
|
||||
c.Assert(err, IsNil)
|
||||
verifyError(c, response, "XMinioInvalidObjectName", "Object name contains unsupported characters. Unsupported characters are `^*|\\\"", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
func (s *TestSuiteCommon) TestBucketSQSNotificationAMQP(c *C) {
|
||||
// Sample bucket notification.
|
||||
bucketNotificationBuf := `<NotificationConfiguration><QueueConfiguration><Event>s3:ObjectCreated:Put</Event><Filter><S3Key><FilterRule><Name>prefix</Name><Value>images/</Value></FilterRule></S3Key></Filter><Id>1</Id><Queue>arn:minio:sqs:us-east-1:444455556666:amqp</Queue></QueueConfiguration></NotificationConfiguration>`
|
||||
@ -1135,7 +1178,9 @@ func (s *TestSuiteCommon) TestSHA256Mismatch(c *C) {
|
||||
c.Assert(request.Header.Get("x-amz-content-sha256"), Equals, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
|
||||
}
|
||||
// Set the body to generate signature mismatch.
|
||||
request.Body = ioutil.NopCloser(bytes.NewReader([]byte("Hello, World")))
|
||||
helloReader := bytes.NewReader([]byte("Hello, World"))
|
||||
request.ContentLength = helloReader.Size()
|
||||
request.Body = ioutil.NopCloser(helloReader)
|
||||
c.Assert(err, IsNil)
|
||||
// execute the HTTP request.
|
||||
response, err = client.Do(request)
|
||||
|
@ -440,6 +440,12 @@ func renameObject(disks []StorageAPI, srcBucket, srcObject, dstBucket, dstObject
|
||||
// writes `xl.json` which carries the necessary metadata for future
|
||||
// object operations.
|
||||
func (xl xlObjects) PutObject(bucket string, object string, size int64, data io.Reader, metadata map[string]string, sha256sum string) (objInfo ObjectInfo, err error) {
|
||||
// This is a special case with size as '0' and object ends with
|
||||
// a slash separator, we treat it like a valid operation and
|
||||
// return success.
|
||||
if isObjectDir(object, size) {
|
||||
return dirObjectInfo(bucket, object, size, metadata), nil
|
||||
}
|
||||
if err = checkPutObjectArgs(bucket, object, xl); err != nil {
|
||||
return ObjectInfo{}, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user