diff --git a/fakestorage/object.go b/fakestorage/object.go index c3f9369b9f..aa47f1579d 100644 --- a/fakestorage/object.go +++ b/fakestorage/object.go @@ -36,6 +36,7 @@ type ObjectAttrs struct { ContentType string ContentEncoding string ContentDisposition string + ContentLanguage string CacheControl string // Crc32c checksum of Content. calculated by server when it's upload methods are used. Crc32c string @@ -64,6 +65,7 @@ type jsonObject struct { ContentType string `json:"contentType"` ContentEncoding string `json:"contentEncoding"` ContentDisposition string `json:"contentDisposition"` + ContentLanguage string `json:"contentLanguage"` Crc32c string `json:"crc32c,omitempty"` Md5Hash string `json:"md5Hash,omitempty"` Etag string `json:"etag,omitempty"` @@ -85,6 +87,7 @@ func (o ObjectAttrs) MarshalJSON() ([]byte, error) { ContentType: o.ContentType, ContentEncoding: o.ContentEncoding, ContentDisposition: o.ContentDisposition, + ContentLanguage: o.ContentLanguage, Size: o.Size, Crc32c: o.Crc32c, Md5Hash: o.Md5Hash, @@ -115,6 +118,7 @@ func (o *ObjectAttrs) UnmarshalJSON(data []byte) error { o.ContentType = temp.ContentType o.ContentEncoding = temp.ContentEncoding o.ContentDisposition = temp.ContentDisposition + o.ContentLanguage = temp.ContentLanguage o.Size = temp.Size o.Crc32c = temp.Crc32c o.Md5Hash = temp.Md5Hash @@ -402,6 +406,7 @@ func toBackendObjects(objects []StreamingObject) []backend.StreamingObject { ContentType: o.ContentType, ContentEncoding: o.ContentEncoding, ContentDisposition: o.ContentDisposition, + ContentLanguage: o.ContentLanguage, CacheControl: o.CacheControl, ACL: o.ACL, Created: getCurrentIfZero(o.Created).Format(timestampFormat), @@ -429,6 +434,7 @@ func bufferedObjectsToBackendObjects(objects []Object) []backend.StreamingObject ContentType: o.ContentType, ContentEncoding: o.ContentEncoding, ContentDisposition: o.ContentDisposition, + ContentLanguage: o.ContentLanguage, ACL: o.ACL, Created: getCurrentIfZero(o.Created).Format(timestampFormat), Deleted: o.Deleted.Format(timestampFormat), @@ -459,6 +465,7 @@ func fromBackendObjects(objects []backend.StreamingObject) []StreamingObject { ContentType: o.ContentType, ContentEncoding: o.ContentEncoding, ContentDisposition: o.ContentDisposition, + ContentLanguage: o.ContentLanguage, CacheControl: o.CacheControl, Crc32c: o.Crc32c, Md5Hash: o.Md5Hash, @@ -488,6 +495,7 @@ func fromBackendObjectsAttrs(objectAttrs []backend.ObjectAttrs) []ObjectAttrs { ContentType: o.ContentType, ContentEncoding: o.ContentEncoding, ContentDisposition: o.ContentDisposition, + ContentLanguage: o.ContentLanguage, CacheControl: o.CacheControl, Crc32c: o.Crc32c, Md5Hash: o.Md5Hash, @@ -844,6 +852,9 @@ func (s *Server) rewriteObject(r *http.Request) jsonResponse { if metadata.ContentDisposition == "" { metadata.ContentDisposition = obj.ContentDisposition } + if metadata.ContentLanguage == "" { + metadata.ContentLanguage = obj.ContentLanguage + } dstBucket := vars["destinationBucket"] newObject := StreamingObject{ @@ -854,6 +865,7 @@ func (s *Server) rewriteObject(r *http.Request) jsonResponse { ContentType: metadata.ContentType, ContentEncoding: metadata.ContentEncoding, ContentDisposition: metadata.ContentDisposition, + ContentLanguage: metadata.ContentLanguage, Metadata: metadata.Metadata, }, Content: obj.Content, @@ -973,6 +985,9 @@ func (s *Server) downloadObject(w http.ResponseWriter, r *http.Request) { if obj.ContentDisposition != "" { w.Header().Set("Content-Disposition", obj.ContentDisposition) } + if obj.ContentLanguage != "" { + w.Header().Set("Content-Language", obj.ContentLanguage) + } // X-Goog-Stored-Content-Encoding must be set to the original encoding, // defaulting to "identity" if no encoding was set. storedContentEncoding := "identity" @@ -1098,6 +1113,7 @@ func (s *Server) patchObject(r *http.Request) jsonResponse { ContentType string ContentEncoding string ContentDisposition string + ContentLanguage string Metadata map[string]string `json:"metadata"` CustomTime string Acl []acls @@ -1115,6 +1131,7 @@ func (s *Server) patchObject(r *http.Request) jsonResponse { attrsToUpdate.ContentType = payload.ContentType attrsToUpdate.ContentEncoding = payload.ContentEncoding attrsToUpdate.ContentDisposition = payload.ContentDisposition + attrsToUpdate.ContentLanguage = payload.ContentLanguage attrsToUpdate.Metadata = payload.Metadata attrsToUpdate.CustomTime = payload.CustomTime @@ -1153,6 +1170,7 @@ func (s *Server) updateObject(r *http.Request) jsonResponse { Metadata map[string]string `json:"metadata"` ContentType string `json:"contentType"` ContentDisposition string `json:"contentDisposition"` + ContentLanguage string `json:"contentLanguage"` CustomTime string Acl []acls } @@ -1170,6 +1188,7 @@ func (s *Server) updateObject(r *http.Request) jsonResponse { attrsToUpdate.CustomTime = payload.CustomTime attrsToUpdate.ContentType = payload.ContentType attrsToUpdate.ContentDisposition = payload.ContentDisposition + attrsToUpdate.ContentLanguage = payload.ContentLanguage if len(payload.Acl) > 0 { attrsToUpdate.ACL = []storage.ACLRule{} for _, aclData := range payload.Acl { @@ -1203,6 +1222,7 @@ func (s *Server) composeObject(r *http.Request) jsonResponse { Bucket string ContentType string ContentDisposition string + ContentLanguage string Metadata map[string]string } } diff --git a/fakestorage/object_test.go b/fakestorage/object_test.go index 8a567fafd5..59f7a8a40f 100644 --- a/fakestorage/object_test.go +++ b/fakestorage/object_test.go @@ -52,6 +52,7 @@ func getObjectTestCases() objectTestCases { contentType = "text/plain; charset=utf-8" contentEncoding = "gzip" contentDisposition = "attachment; filename=\"replaced.txt\"" + contentLanguage = "fr" metaValue = "MetaValue" ) testInitExecTime := time.Now().Truncate(time.Microsecond) @@ -114,6 +115,7 @@ func getObjectTestCases() objectTestCases { ContentType: contentType, ContentEncoding: contentEncoding, ContentDisposition: contentDisposition, + ContentLanguage: contentLanguage, Crc32c: checksum.EncodedChecksum(uint32ToBytes(u32Checksum)), Md5Hash: checksum.EncodedHash(hash), Metadata: map[string]string{"MetaHeader": metaValue}, @@ -190,6 +192,9 @@ func checkObjectAttrs(testObj Object, attrs *storage.ObjectAttrs, t *testing.T) if attrs.ContentDisposition != testObj.ContentDisposition { t.Errorf("wrong content disposition\nwant %q\ngot %q", testObj.ContentDisposition, attrs.ContentDisposition) } + if attrs.ContentLanguage != testObj.ContentLanguage { + t.Errorf("wrong content language\nwant %q\ngot %q", testObj.ContentLanguage, attrs.ContentLanguage) + } if testObj.Content != nil && attrs.Size != int64(len(testObj.Content)) { t.Errorf("wrong size returned\nwant %d\ngot %d", len(testObj.Content), attrs.Size) } diff --git a/fakestorage/response.go b/fakestorage/response.go index 89585ec660..8957af37fa 100644 --- a/fakestorage/response.go +++ b/fakestorage/response.go @@ -119,6 +119,7 @@ type objectResponse struct { ContentType string `json:"contentType,omitempty"` ContentEncoding string `json:"contentEncoding,omitempty"` ContentDisposition string `json:"contentDisposition,omitempty"` + ContentLanguage string `json:"contentLanguage,omitempty"` Crc32c string `json:"crc32c,omitempty"` ACL []*objectAccessControl `json:"acl,omitempty"` Md5Hash string `json:"md5Hash,omitempty"` @@ -160,6 +161,7 @@ func newObjectResponse(obj ObjectAttrs, externalURL string) objectResponse { ContentType: obj.ContentType, ContentEncoding: obj.ContentEncoding, ContentDisposition: obj.ContentDisposition, + ContentLanguage: obj.ContentLanguage, Crc32c: obj.Crc32c, Md5Hash: obj.Md5Hash, Etag: obj.Etag, diff --git a/fakestorage/upload.go b/fakestorage/upload.go index ff0fa88eca..3f33eff5dc 100644 --- a/fakestorage/upload.go +++ b/fakestorage/upload.go @@ -54,6 +54,7 @@ type multipartMetadata struct { ContentType string `json:"contentType"` ContentEncoding string `json:"contentEncoding"` ContentDisposition string `json:"contentDisposition"` + ContentLanguage string `json:"ContentLanguage"` CacheControl string `json:"cacheControl"` CustomTime time.Time `json:"customTime,omitempty"` Name string `json:"name"` @@ -384,6 +385,7 @@ func (s *Server) multipartUpload(bucketName string, r *http.Request) jsonRespons CacheControl: metadata.CacheControl, ContentEncoding: metadata.ContentEncoding, ContentDisposition: metadata.ContentDisposition, + ContentLanguage: metadata.ContentLanguage, CustomTime: metadata.CustomTime, ACL: getObjectACL(predefinedACL), Metadata: metadata.Metadata, diff --git a/internal/backend/object.go b/internal/backend/object.go index 5040afb9df..aa2379c4e8 100644 --- a/internal/backend/object.go +++ b/internal/backend/object.go @@ -22,6 +22,7 @@ type ObjectAttrs struct { ContentType string ContentEncoding string ContentDisposition string + ContentLanguage string CacheControl string Crc32c string Md5Hash string diff --git a/internal/grpc/server.go b/internal/grpc/server.go index 48a4534e2c..928344538d 100644 --- a/internal/grpc/server.go +++ b/internal/grpc/server.go @@ -115,6 +115,7 @@ func (g *Server) GetObject(ctx context.Context, req *pb.GetObjectRequest) (*pb.O Generation: obj.ObjectAttrs.Generation, ContentType: obj.ObjectAttrs.ContentType, ContentDisposition: obj.ObjectAttrs.ContentDisposition, + ContentLanguage: obj.ObjectAttrs.ContentLanguage, }, nil } @@ -137,6 +138,7 @@ func (g *Server) PatchObject(ctx context.Context, req *pb.PatchObjectRequest) (* ContentType: req.Metadata.ContentType, ContentEncoding: req.Metadata.ContentEncoding, ContentDisposition: req.Metadata.ContentDisposition, + ContentLanguage: req.Metadata.ContentLanguage, } obj, err := g.backend.PatchObject(req.Bucket, req.Object, attrs) return makeObject(obj), err