diff --git a/backend/backend.go b/backend/backend.go index 81e5257..6b80441 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -30,7 +30,7 @@ type Backend interface { CopyPart(srcBucket, srcObject, DstBucket, uploadID, rangeHeader string, part int) (*types.CopyPartResult, error) PutObjectPart(bucket, object, uploadID string, part int, length int64, r io.Reader) (etag string, err error) - PutObject(bucket, object string, r io.Reader) (string, error) + PutObject(*s3.PutObjectInput) (*s3.PutObjectOutput, error) HeadObject(bucket, object string, etag string) (*s3.HeadObjectOutput, error) GetObject(bucket, object string, startOffset, length int64, writer io.Writer, etag string) (*s3.GetObjectOutput, error) GetObjectAcl(bucket, object string) (*s3.GetObjectAclOutput, error) @@ -120,8 +120,8 @@ func (BackendUnsupported) PutObjectPart(bucket, object, uploadID string, part in return "", s3err.GetAPIError(s3err.ErrNotImplemented) } -func (BackendUnsupported) PutObject(bucket, object string, r io.Reader) (string, error) { - return "", s3err.GetAPIError(s3err.ErrNotImplemented) +func (BackendUnsupported) PutObject(*s3.PutObjectInput) (*s3.PutObjectOutput, error) { + return nil, s3err.GetAPIError(s3err.ErrNotImplemented) } func (BackendUnsupported) DeleteObject(bucket, object string) error { return s3err.GetAPIError(s3err.ErrNotImplemented) diff --git a/backend/backend_moq_test.go b/backend/backend_moq_test.go index 4a91c27..8b17584 100644 --- a/backend/backend_moq_test.go +++ b/backend/backend_moq_test.go @@ -92,7 +92,7 @@ var _ Backend = &BackendMock{} // PutBucketAclFunc: func(putBucketAclInput *s3.PutBucketAclInput) error { // panic("mock out the PutBucketAcl method") // }, -// PutObjectFunc: func(bucket string, object string, r io.Reader) (string, error) { +// PutObjectFunc: func(putObjectInput *s3.PutObjectInput) (*s3.PutObjectOutput, error) { // panic("mock out the PutObject method") // }, // PutObjectAclFunc: func(putObjectAclInput *s3.PutObjectAclInput) error { @@ -202,7 +202,7 @@ type BackendMock struct { PutBucketAclFunc func(putBucketAclInput *s3.PutBucketAclInput) error // PutObjectFunc mocks the PutObject method. - PutObjectFunc func(bucket string, object string, r io.Reader) (string, error) + PutObjectFunc func(putObjectInput *s3.PutObjectInput) (*s3.PutObjectOutput, error) // PutObjectAclFunc mocks the PutObjectAcl method. PutObjectAclFunc func(putObjectAclInput *s3.PutObjectAclInput) error @@ -421,12 +421,8 @@ type BackendMock struct { } // PutObject holds details about calls to the PutObject method. PutObject []struct { - // Bucket is the bucket argument value. - Bucket string - // Object is the object argument value. - Object string - // R is the r argument value. - R io.Reader + // PutObjectInput is the putObjectInput argument value. + PutObjectInput *s3.PutObjectInput } // PutObjectAcl holds details about calls to the PutObjectAcl method. PutObjectAcl []struct { @@ -1430,23 +1426,19 @@ func (mock *BackendMock) PutBucketAclCalls() []struct { } // PutObject calls PutObjectFunc. -func (mock *BackendMock) PutObject(bucket string, object string, r io.Reader) (string, error) { +func (mock *BackendMock) PutObject(putObjectInput *s3.PutObjectInput) (*s3.PutObjectOutput, error) { if mock.PutObjectFunc == nil { panic("BackendMock.PutObjectFunc: method is nil but Backend.PutObject was just called") } callInfo := struct { - Bucket string - Object string - R io.Reader + PutObjectInput *s3.PutObjectInput }{ - Bucket: bucket, - Object: object, - R: r, + PutObjectInput: putObjectInput, } mock.lockPutObject.Lock() mock.calls.PutObject = append(mock.calls.PutObject, callInfo) mock.lockPutObject.Unlock() - return mock.PutObjectFunc(bucket, object, r) + return mock.PutObjectFunc(putObjectInput) } // PutObjectCalls gets all the calls that were made to PutObject. @@ -1454,14 +1446,10 @@ func (mock *BackendMock) PutObject(bucket string, object string, r io.Reader) (s // // len(mockedBackend.PutObjectCalls()) func (mock *BackendMock) PutObjectCalls() []struct { - Bucket string - Object string - R io.Reader + PutObjectInput *s3.PutObjectInput } { var calls []struct { - Bucket string - Object string - R io.Reader + PutObjectInput *s3.PutObjectInput } mock.lockPutObject.RLock() calls = mock.calls.PutObject diff --git a/s3api/controllers/backend_moq_test.go b/s3api/controllers/backend_moq_test.go index aff5385..e38f015 100644 --- a/s3api/controllers/backend_moq_test.go +++ b/s3api/controllers/backend_moq_test.go @@ -93,7 +93,7 @@ var _ backend.Backend = &BackendMock{} // PutBucketAclFunc: func(putBucketAclInput *s3.PutBucketAclInput) error { // panic("mock out the PutBucketAcl method") // }, -// PutObjectFunc: func(bucket string, object string, r io.Reader) (string, error) { +// PutObjectFunc: func(putObjectInput *s3.PutObjectInput) (*s3.PutObjectOutput, error) { // panic("mock out the PutObject method") // }, // PutObjectAclFunc: func(putObjectAclInput *s3.PutObjectAclInput) error { @@ -203,7 +203,7 @@ type BackendMock struct { PutBucketAclFunc func(putBucketAclInput *s3.PutBucketAclInput) error // PutObjectFunc mocks the PutObject method. - PutObjectFunc func(bucket string, object string, r io.Reader) (string, error) + PutObjectFunc func(putObjectInput *s3.PutObjectInput) (*s3.PutObjectOutput, error) // PutObjectAclFunc mocks the PutObjectAcl method. PutObjectAclFunc func(putObjectAclInput *s3.PutObjectAclInput) error @@ -422,12 +422,8 @@ type BackendMock struct { } // PutObject holds details about calls to the PutObject method. PutObject []struct { - // Bucket is the bucket argument value. - Bucket string - // Object is the object argument value. - Object string - // R is the r argument value. - R io.Reader + // PutObjectInput is the putObjectInput argument value. + PutObjectInput *s3.PutObjectInput } // PutObjectAcl holds details about calls to the PutObjectAcl method. PutObjectAcl []struct { @@ -1431,23 +1427,19 @@ func (mock *BackendMock) PutBucketAclCalls() []struct { } // PutObject calls PutObjectFunc. -func (mock *BackendMock) PutObject(bucket string, object string, r io.Reader) (string, error) { +func (mock *BackendMock) PutObject(putObjectInput *s3.PutObjectInput) (*s3.PutObjectOutput, error) { if mock.PutObjectFunc == nil { panic("BackendMock.PutObjectFunc: method is nil but Backend.PutObject was just called") } callInfo := struct { - Bucket string - Object string - R io.Reader + PutObjectInput *s3.PutObjectInput }{ - Bucket: bucket, - Object: object, - R: r, + PutObjectInput: putObjectInput, } mock.lockPutObject.Lock() mock.calls.PutObject = append(mock.calls.PutObject, callInfo) mock.lockPutObject.Unlock() - return mock.PutObjectFunc(bucket, object, r) + return mock.PutObjectFunc(putObjectInput) } // PutObjectCalls gets all the calls that were made to PutObject. @@ -1455,14 +1447,10 @@ func (mock *BackendMock) PutObject(bucket string, object string, r io.Reader) (s // // len(mockedBackend.PutObjectCalls()) func (mock *BackendMock) PutObjectCalls() []struct { - Bucket string - Object string - R io.Reader + PutObjectInput *s3.PutObjectInput } { var calls []struct { - Bucket string - Object string - R io.Reader + PutObjectInput *s3.PutObjectInput } mock.lockPutObject.RLock() calls = mock.calls.PutObject diff --git a/s3api/controllers/base.go b/s3api/controllers/base.go index 65b31ea..1201e9d 100644 --- a/s3api/controllers/base.go +++ b/s3api/controllers/base.go @@ -14,6 +14,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3/types" "github.com/gofiber/fiber/v2" "github.com/versity/scoutgw/backend" + "github.com/versity/scoutgw/s3api/utils" "github.com/versity/scoutgw/s3err" ) @@ -143,7 +144,7 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error { copySource, copySrcIfMatch, copySrcIfNoneMatch, copySrcModifSince, copySrcUnmodifSince, acl, grantFullControl, grantRead, grantReadACP, - granWrite, grantWriteACP := + granWrite, grantWriteACP, contentLengthStr := // Copy source headers ctx.Get("X-Amz-Copy-Source"), ctx.Get("X-Amz-Copy-Source-If-Match"), @@ -156,7 +157,9 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error { ctx.Get("X-Amz-Grant-Read"), ctx.Get("X-Amz-Grant-Read-Acp"), ctx.Get("X-Amz-Grant-Write"), - ctx.Get("X-Amz-Grant-Write-Acp") + ctx.Get("X-Amz-Grant-Write-Acp"), + // Other headers + ctx.Get("Content-Length") grants := grantFullControl + grantRead + grantReadACP + granWrite + grantWriteACP @@ -227,7 +230,20 @@ func (c S3ApiController) PutActions(ctx *fiber.Ctx) error { return responce(ctx, res, err) } - res, err := c.be.PutObject(dstBucket, dstKeyStart, bytes.NewReader(ctx.Request().Body())) + contentLength, err := strconv.ParseInt(contentLengthStr, 10, 64) + if err != nil { + return errors.New("wrong api call") + } + + metadata := utils.GetUserMetaData(&ctx.Request().Header) + + res, err := c.be.PutObject(&s3.PutObjectInput{ + Bucket: &dstBucket, + Key: &dstKeyStart, + ContentLength: contentLength, + Metadata: metadata, + Body: bytes.NewReader(ctx.Request().Body()), + }) return responce(ctx, res, err) } diff --git a/s3api/controllers/base_test.go b/s3api/controllers/base_test.go index 0b02a44..c6bbde5 100644 --- a/s3api/controllers/base_test.go +++ b/s3api/controllers/base_test.go @@ -406,8 +406,8 @@ func TestS3ApiController_PutActions(t *testing.T) { CopyObjectFunc: func(srcBucket, srcObject, DstBucket, dstObject string) (*s3.CopyObjectOutput, error) { return &s3.CopyObjectOutput{}, nil }, - PutObjectFunc: func(bucket, object string, r io.Reader) (string, error) { - return "hello", nil + PutObjectFunc: func(*s3.PutObjectInput) (*s3.PutObjectOutput, error) { + return &s3.PutObjectOutput{}, nil }, }} app.Put("/:bucket/:key/*", s3ApiController.PutActions) diff --git a/s3api/utils/utils.go b/s3api/utils/utils.go new file mode 100644 index 0000000..216b199 --- /dev/null +++ b/s3api/utils/utils.go @@ -0,0 +1,20 @@ +package utils + +import ( + "strings" + + "github.com/valyala/fasthttp" +) + +func GetUserMetaData(headers *fasthttp.RequestHeader) (metadata map[string]string) { + metadata = make(map[string]string) + headers.VisitAll(func(key, value []byte) { + if strings.HasPrefix(string(key), "X-Amz-Meta-") { + trimmedKey := strings.TrimPrefix(string(key), "X-Amz-Meta-") + headerValue := string(value) + metadata[trimmedKey] = headerValue + } + }) + + return +}