Skip to content

Commit

Permalink
Support content-disposition (#1630)
Browse files Browse the repository at this point in the history
Co-authored-by: yuk1ty <yuk1ty@users.noreply.github.com>
  • Loading branch information
yuk1ty and yuk1ty authored Sep 22, 2024
1 parent 50ee9de commit c523dad
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 155 deletions.
236 changes: 128 additions & 108 deletions fakestorage/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ var errInvalidGeneration = errors.New("invalid generation ID")

// ObjectAttrs returns only the meta-data about an object without its contents.
type ObjectAttrs struct {
BucketName string
Name string
Size int64
ContentType string
ContentEncoding string
BucketName string
Name string
Size int64
ContentType string
ContentEncoding string
ContentDisposition string
// Crc32c checksum of Content. calculated by server when it's upload methods are used.
Crc32c string
Md5Hash string
Expand All @@ -54,40 +55,42 @@ func (o *ObjectAttrs) id() string {
}

type jsonObject struct {
BucketName string `json:"bucket"`
Name string `json:"name"`
Size int64 `json:"size,string"`
ContentType string `json:"contentType"`
ContentEncoding string `json:"contentEncoding"`
Crc32c string `json:"crc32c,omitempty"`
Md5Hash string `json:"md5Hash,omitempty"`
Etag string `json:"etag,omitempty"`
ACL []aclRule `json:"acl,omitempty"`
Created time.Time `json:"created,omitempty"`
Updated time.Time `json:"updated,omitempty"`
Deleted time.Time `json:"deleted,omitempty"`
CustomTime time.Time `json:"customTime,omitempty"`
Generation int64 `json:"generation,omitempty,string"`
Metadata map[string]string `json:"metadata,omitempty"`
BucketName string `json:"bucket"`
Name string `json:"name"`
Size int64 `json:"size,string"`
ContentType string `json:"contentType"`
ContentEncoding string `json:"contentEncoding"`
ContentDisposition string `json:"contentDisposition"`
Crc32c string `json:"crc32c,omitempty"`
Md5Hash string `json:"md5Hash,omitempty"`
Etag string `json:"etag,omitempty"`
ACL []aclRule `json:"acl,omitempty"`
Created time.Time `json:"created,omitempty"`
Updated time.Time `json:"updated,omitempty"`
Deleted time.Time `json:"deleted,omitempty"`
CustomTime time.Time `json:"customTime,omitempty"`
Generation int64 `json:"generation,omitempty,string"`
Metadata map[string]string `json:"metadata,omitempty"`
}

// MarshalJSON for ObjectAttrs to use ACLRule instead of storage.ACLRule
func (o ObjectAttrs) MarshalJSON() ([]byte, error) {
temp := jsonObject{
BucketName: o.BucketName,
Name: o.Name,
ContentType: o.ContentType,
ContentEncoding: o.ContentEncoding,
Size: o.Size,
Crc32c: o.Crc32c,
Md5Hash: o.Md5Hash,
Etag: o.Etag,
Created: o.Created,
Updated: o.Updated,
Deleted: o.Deleted,
CustomTime: o.CustomTime,
Generation: o.Generation,
Metadata: o.Metadata,
BucketName: o.BucketName,
Name: o.Name,
ContentType: o.ContentType,
ContentEncoding: o.ContentEncoding,
ContentDisposition: o.ContentDisposition,
Size: o.Size,
Crc32c: o.Crc32c,
Md5Hash: o.Md5Hash,
Etag: o.Etag,
Created: o.Created,
Updated: o.Updated,
Deleted: o.Deleted,
CustomTime: o.CustomTime,
Generation: o.Generation,
Metadata: o.Metadata,
}
temp.ACL = make([]aclRule, len(o.ACL))
for i, ACL := range o.ACL {
Expand All @@ -106,6 +109,7 @@ func (o *ObjectAttrs) UnmarshalJSON(data []byte) error {
o.Name = temp.Name
o.ContentType = temp.ContentType
o.ContentEncoding = temp.ContentEncoding
o.ContentDisposition = temp.ContentDisposition
o.Size = temp.Size
o.Crc32c = temp.Crc32c
o.Md5Hash = temp.Md5Hash
Expand Down Expand Up @@ -387,17 +391,18 @@ func toBackendObjects(objects []StreamingObject) []backend.StreamingObject {
for _, o := range objects {
backendObjects = append(backendObjects, backend.StreamingObject{
ObjectAttrs: backend.ObjectAttrs{
BucketName: o.BucketName,
Name: o.Name,
ContentType: o.ContentType,
ContentEncoding: o.ContentEncoding,
ACL: o.ACL,
Created: getCurrentIfZero(o.Created).Format(timestampFormat),
Deleted: o.Deleted.Format(timestampFormat),
Updated: getCurrentIfZero(o.Updated).Format(timestampFormat),
CustomTime: o.CustomTime.Format(timestampFormat),
Generation: o.Generation,
Metadata: o.Metadata,
BucketName: o.BucketName,
Name: o.Name,
ContentType: o.ContentType,
ContentEncoding: o.ContentEncoding,
ContentDisposition: o.ContentDisposition,
ACL: o.ACL,
Created: getCurrentIfZero(o.Created).Format(timestampFormat),
Deleted: o.Deleted.Format(timestampFormat),
Updated: getCurrentIfZero(o.Updated).Format(timestampFormat),
CustomTime: o.CustomTime.Format(timestampFormat),
Generation: o.Generation,
Metadata: o.Metadata,
},
Content: o.Content,
})
Expand All @@ -411,21 +416,22 @@ func bufferedObjectsToBackendObjects(objects []Object) []backend.StreamingObject
o := bufferedObject.StreamingObject()
backendObjects = append(backendObjects, backend.StreamingObject{
ObjectAttrs: backend.ObjectAttrs{
BucketName: o.BucketName,
Name: o.Name,
ContentType: o.ContentType,
ContentEncoding: o.ContentEncoding,
ACL: o.ACL,
Created: getCurrentIfZero(o.Created).Format(timestampFormat),
Deleted: o.Deleted.Format(timestampFormat),
Updated: getCurrentIfZero(o.Updated).Format(timestampFormat),
CustomTime: o.CustomTime.Format(timestampFormat),
Generation: o.Generation,
Metadata: o.Metadata,
Crc32c: o.Crc32c,
Md5Hash: o.Md5Hash,
Size: o.Size,
Etag: o.Etag,
BucketName: o.BucketName,
Name: o.Name,
ContentType: o.ContentType,
ContentEncoding: o.ContentEncoding,
ContentDisposition: o.ContentDisposition,
ACL: o.ACL,
Created: getCurrentIfZero(o.Created).Format(timestampFormat),
Deleted: o.Deleted.Format(timestampFormat),
Updated: getCurrentIfZero(o.Updated).Format(timestampFormat),
CustomTime: o.CustomTime.Format(timestampFormat),
Generation: o.Generation,
Metadata: o.Metadata,
Crc32c: o.Crc32c,
Md5Hash: o.Md5Hash,
Size: o.Size,
Etag: o.Etag,
},
Content: o.Content,
})
Expand All @@ -438,21 +444,22 @@ func fromBackendObjects(objects []backend.StreamingObject) []StreamingObject {
for _, o := range objects {
backendObjects = append(backendObjects, StreamingObject{
ObjectAttrs: ObjectAttrs{
BucketName: o.BucketName,
Name: o.Name,
Size: o.Size,
ContentType: o.ContentType,
ContentEncoding: o.ContentEncoding,
Crc32c: o.Crc32c,
Md5Hash: o.Md5Hash,
Etag: o.Etag,
ACL: o.ACL,
Created: convertTimeWithoutError(o.Created),
Deleted: convertTimeWithoutError(o.Deleted),
Updated: convertTimeWithoutError(o.Updated),
CustomTime: convertTimeWithoutError(o.CustomTime),
Generation: o.Generation,
Metadata: o.Metadata,
BucketName: o.BucketName,
Name: o.Name,
Size: o.Size,
ContentType: o.ContentType,
ContentEncoding: o.ContentEncoding,
ContentDisposition: o.ContentDisposition,
Crc32c: o.Crc32c,
Md5Hash: o.Md5Hash,
Etag: o.Etag,
ACL: o.ACL,
Created: convertTimeWithoutError(o.Created),
Deleted: convertTimeWithoutError(o.Deleted),
Updated: convertTimeWithoutError(o.Updated),
CustomTime: convertTimeWithoutError(o.CustomTime),
Generation: o.Generation,
Metadata: o.Metadata,
},
Content: o.Content,
})
Expand All @@ -464,21 +471,22 @@ func fromBackendObjectsAttrs(objectAttrs []backend.ObjectAttrs) []ObjectAttrs {
oattrs := make([]ObjectAttrs, 0, len(objectAttrs))
for _, o := range objectAttrs {
oattrs = append(oattrs, ObjectAttrs{
BucketName: o.BucketName,
Name: o.Name,
Size: o.Size,
ContentType: o.ContentType,
ContentEncoding: o.ContentEncoding,
Crc32c: o.Crc32c,
Md5Hash: o.Md5Hash,
Etag: o.Etag,
ACL: o.ACL,
Created: convertTimeWithoutError(o.Created),
Deleted: convertTimeWithoutError(o.Deleted),
Updated: convertTimeWithoutError(o.Updated),
CustomTime: convertTimeWithoutError(o.CustomTime),
Generation: o.Generation,
Metadata: o.Metadata,
BucketName: o.BucketName,
Name: o.Name,
Size: o.Size,
ContentType: o.ContentType,
ContentEncoding: o.ContentEncoding,
ContentDisposition: o.ContentDisposition,
Crc32c: o.Crc32c,
Md5Hash: o.Md5Hash,
Etag: o.Etag,
ACL: o.ACL,
Created: convertTimeWithoutError(o.Created),
Deleted: convertTimeWithoutError(o.Deleted),
Updated: convertTimeWithoutError(o.Updated),
CustomTime: convertTimeWithoutError(o.CustomTime),
Generation: o.Generation,
Metadata: o.Metadata,
})
}
return oattrs
Expand Down Expand Up @@ -768,16 +776,20 @@ func (s *Server) rewriteObject(r *http.Request) jsonResponse {
if metadata.ContentEncoding == "" {
metadata.ContentEncoding = obj.ContentEncoding
}
if metadata.ContentDisposition == "" {
metadata.ContentDisposition = obj.ContentDisposition
}

dstBucket := vars["destinationBucket"]
newObject := StreamingObject{
ObjectAttrs: ObjectAttrs{
BucketName: dstBucket,
Name: vars["destinationObject"],
ACL: obj.ACL,
ContentType: metadata.ContentType,
ContentEncoding: metadata.ContentEncoding,
Metadata: metadata.Metadata,
BucketName: dstBucket,
Name: vars["destinationObject"],
ACL: obj.ACL,
ContentType: metadata.ContentType,
ContentEncoding: metadata.ContentEncoding,
ContentDisposition: metadata.ContentDisposition,
Metadata: metadata.Metadata,
},
Content: obj.Content,
}
Expand Down Expand Up @@ -890,6 +902,9 @@ func (s *Server) downloadObject(w http.ResponseWriter, r *http.Request) {
if obj.ContentEncoding != "" && !transcoded {
w.Header().Set("Content-Encoding", obj.ContentEncoding)
}
if obj.ContentDisposition != "" {
w.Header().Set("Content-Disposition", obj.ContentDisposition)
}
// X-Goog-Stored-Content-Encoding must be set to the original encoding,
// defaulting to "identity" if no encoding was set.
storedContentEncoding := "identity"
Expand Down Expand Up @@ -1012,11 +1027,12 @@ func (s *Server) patchObject(r *http.Request) jsonResponse {
}

var payload struct {
ContentType string
ContentEncoding string
Metadata map[string]string `json:"metadata"`
CustomTime string
Acl []acls
ContentType string
ContentEncoding string
ContentDisposition string
Metadata map[string]string `json:"metadata"`
CustomTime string
Acl []acls
}
err := json.NewDecoder(r.Body).Decode(&payload)
if err != nil {
Expand All @@ -1030,6 +1046,7 @@ func (s *Server) patchObject(r *http.Request) jsonResponse {

attrsToUpdate.ContentType = payload.ContentType
attrsToUpdate.ContentEncoding = payload.ContentEncoding
attrsToUpdate.ContentDisposition = payload.ContentDisposition
attrsToUpdate.Metadata = payload.Metadata
attrsToUpdate.CustomTime = payload.CustomTime

Expand Down Expand Up @@ -1065,10 +1082,11 @@ func (s *Server) updateObject(r *http.Request) jsonResponse {
}

var payload struct {
Metadata map[string]string `json:"metadata"`
ContentType string `json:"contentType"`
CustomTime string
Acl []acls
Metadata map[string]string `json:"metadata"`
ContentType string `json:"contentType"`
ContentDisposition string `json:"contentDisposition"`
CustomTime string
Acl []acls
}
err := json.NewDecoder(r.Body).Decode(&payload)
if err != nil {
Expand All @@ -1083,6 +1101,7 @@ func (s *Server) updateObject(r *http.Request) jsonResponse {
attrsToUpdate.Metadata = payload.Metadata
attrsToUpdate.CustomTime = payload.CustomTime
attrsToUpdate.ContentType = payload.ContentType
attrsToUpdate.ContentDisposition = payload.ContentDisposition
if len(payload.Acl) > 0 {
attrsToUpdate.ACL = []storage.ACLRule{}
for _, aclData := range payload.Acl {
Expand Down Expand Up @@ -1113,9 +1132,10 @@ func (s *Server) composeObject(r *http.Request) jsonResponse {
Name string
}
Destination struct {
Bucket string
ContentType string
Metadata map[string]string
Bucket string
ContentType string
ContentDisposition string
Metadata map[string]string
}
}

Expand Down
29 changes: 17 additions & 12 deletions fakestorage/object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,12 @@ type objectTestCases []struct {

func getObjectTestCases() objectTestCases {
const (
bucketName = "some-bucket"
content = "some nice content"
contentType = "text/plain; charset=utf-8"
contentEncoding = "gzip"
metaValue = "MetaValue"
bucketName = "some-bucket"
content = "some nice content"
contentType = "text/plain; charset=utf-8"
contentEncoding = "gzip"
contentDisposition = "attachment; filename=\"replaced.txt\""
metaValue = "MetaValue"
)
testInitExecTime := time.Now().Truncate(time.Microsecond)
u32Checksum := uint32Checksum([]byte(content))
Expand Down Expand Up @@ -107,13 +108,14 @@ func getObjectTestCases() objectTestCases {
Object{
Content: []byte(content),
ObjectAttrs: ObjectAttrs{
BucketName: bucketName,
Name: "img/location/meta.jpg",
ContentType: contentType,
ContentEncoding: contentEncoding,
Crc32c: checksum.EncodedChecksum(uint32ToBytes(u32Checksum)),
Md5Hash: checksum.EncodedHash(hash),
Metadata: map[string]string{"MetaHeader": metaValue},
BucketName: bucketName,
Name: "img/location/meta.jpg",
ContentType: contentType,
ContentEncoding: contentEncoding,
ContentDisposition: contentDisposition,
Crc32c: checksum.EncodedChecksum(uint32ToBytes(u32Checksum)),
Md5Hash: checksum.EncodedHash(hash),
Metadata: map[string]string{"MetaHeader": metaValue},
},
},
},
Expand Down Expand Up @@ -170,6 +172,9 @@ func checkObjectAttrs(testObj Object, attrs *storage.ObjectAttrs, t *testing.T)
if attrs.ContentEncoding != testObj.ContentEncoding {
t.Errorf("wrong content encoding\nwant %q\ngot %q", testObj.ContentEncoding, attrs.ContentEncoding)
}
if attrs.ContentDisposition != testObj.ContentDisposition {
t.Errorf("wrong content disposition\nwant %q\ngot %q", testObj.ContentDisposition, attrs.ContentDisposition)
}
if testObj.Content != nil && attrs.Size != int64(len(testObj.Content)) {
t.Errorf("wrong size returned\nwant %d\ngot %d", len(testObj.Content), attrs.Size)
}
Expand Down
Loading

0 comments on commit c523dad

Please sign in to comment.