// Copyright (c) 2015-2021 MinIO, Inc.//// This file is part of MinIO Object Storage stack//// This program is free software: you can redistribute it and/or modify// it under the terms of the GNU Affero General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// This program is distributed in the hope that it will be useful// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU Affero General Public License for more details.//// You should have received a copy of the GNU Affero General Public License// along with this program. If not, see <http://www.gnu.org/licenses/>. package cmd import ( "context" "encoding/base64" "encoding/xml" "fmt" "net/http" "net/url" "path" "strconv" "strings" "time" "github.com/minio/minio/internal/crypto" "github.com/minio/minio/internal/handlers" xhttp "github.com/minio/minio/internal/http" "github.com/minio/minio/internal/logger") const ( // RFC3339 a subset of the ISO8601 timestamp format. e.g 2014-04-29T18:30:38Z iso8601TimeFormat = "2006-01-02T15:04:05.000Z" // Reply date format with nanosecond precision. maxObjectList = 1000 // Limit number of objects in a listObjectsResponse/listObjectsVersionsResponse. maxDeleteList = 1000 // Limit number of objects deleted in a delete call. maxUploadsList = 10000 // Limit number of uploads in a listUploadsResponse. maxPartsList = 10000 // Limit number of parts in a listPartsResponse.) // LocationResponse - format for location response.type LocationResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ LocationConstraint" json:"-"` Location string `xml:",chardata"`} // PolicyStatus captures information returned by GetBucketPolicyStatusHandlertype PolicyStatus struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ PolicyStatus" json:"-"` IsPublic string} // ListVersionsResponse - format for list bucket versions response.type ListVersionsResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListVersionsResult" json:"-"` Name string Prefix string KeyMarker string // When response is truncated (the IsTruncated element value in the response // is true), you can use the key name in this field as marker in the subsequent // request to get next set of objects. Server lists objects in alphabetical // order Note: This element is returned only if you have delimiter request parameter // specified. If response does not include the NextMaker and it is truncated, // you can use the value of the last Key in the response as the marker in the // subsequent request to get the next set of object keys. NextKeyMarker string `xml:"NextKeyMarker,omitempty"` // When the number of responses exceeds the value of MaxKeys, // NextVersionIdMarker specifies the first object version not // returned that satisfies the search criteria. Use this value // for the version-id-marker request parameter in a subsequent request. NextVersionIDMarker string `xml:"NextVersionIdMarker"` // Marks the last version of the Key returned in a truncated response. VersionIDMarker string `xml:"VersionIdMarker"` MaxKeys int Delimiter string // A flag that indicates whether or not ListObjects returned all of the results // that satisfied the search criteria. IsTruncated bool CommonPrefixes []CommonPrefix Versions []ObjectVersion // Encoding type used to encode object keys in the response. EncodingType string `xml:"EncodingType,omitempty"`} // ListObjectsResponse - format for list objects response.type ListObjectsResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult" json:"-"` Name string Prefix string Marker string // When response is truncated (the IsTruncated element value in the response // is true), you can use the key name in this field as marker in the subsequent // request to get next set of objects. Server lists objects in alphabetical // order Note: This element is returned only if you have delimiter request parameter // specified. If response does not include the NextMaker and it is truncated, // you can use the value of the last Key in the response as the marker in the // subsequent request to get the next set of object keys. NextMarker string `xml:"NextMarker,omitempty"` MaxKeys int Delimiter string // A flag that indicates whether or not ListObjects returned all of the results // that satisfied the search criteria. IsTruncated bool Contents []Object CommonPrefixes []CommonPrefix // Encoding type used to encode object keys in the response. EncodingType string `xml:"EncodingType,omitempty"`} // ListObjectsV2Response - format for list objects response.type ListObjectsV2Response struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult" json:"-"` Name string Prefix string StartAfter string `xml:"StartAfter,omitempty"` // When response is truncated (the IsTruncated element value in the response // is true), you can use the key name in this field as marker in the subsequent // request to get next set of objects. Server lists objects in alphabetical // order Note: This element is returned only if you have delimiter request parameter // specified. If response does not include the NextMaker and it is truncated, // you can use the value of the last Key in the response as the marker in the // subsequent request to get the next set of object keys. ContinuationToken string `xml:"ContinuationToken,omitempty"` NextContinuationToken string `xml:"NextContinuationToken,omitempty"` KeyCount int MaxKeys int Delimiter string // A flag that indicates whether or not ListObjects returned all of the results // that satisfied the search criteria. IsTruncated bool Contents []Object CommonPrefixes []CommonPrefix // Encoding type used to encode object keys in the response. EncodingType string `xml:"EncodingType,omitempty"`} // Part container for part metadata.type Part struct { PartNumber int LastModified string ETag string Size int64} // ListPartsResponse - format for list parts response.type ListPartsResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListPartsResult" json:"-"` Bucket string Key string UploadID string `xml:"UploadId"` Initiator Initiator Owner Owner // The class of storage used to store the object. StorageClass string PartNumberMarker int NextPartNumberMarker int MaxParts int IsTruncated bool // List of parts. Parts []Part `xml:"Part"`} // ListMultipartUploadsResponse - format for list multipart uploads response.type ListMultipartUploadsResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListMultipartUploadsResult" json:"-"` Bucket string KeyMarker string UploadIDMarker string `xml:"UploadIdMarker"` NextKeyMarker string NextUploadIDMarker string `xml:"NextUploadIdMarker"` Delimiter string Prefix string EncodingType string `xml:"EncodingType,omitempty"` MaxUploads int IsTruncated bool // List of pending uploads. Uploads []Upload `xml:"Upload"` // Delimed common prefixes. CommonPrefixes []CommonPrefix} // ListBucketsResponse - format for list buckets responsetype ListBucketsResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListAllMyBucketsResult" json:"-"` Owner Owner // Container for one or more buckets. Buckets struct { Buckets []Bucket `xml:"Bucket"` } // Buckets are nested} // Upload container for in progress multipart uploadtype Upload struct { Key string UploadID string `xml:"UploadId"` Initiator Initiator Owner Owner StorageClass string Initiated string} // CommonPrefix container for prefix response in ListObjectsResponsetype CommonPrefix struct { Prefix string} // Bucket container for bucket metadatatype Bucket struct { Name string CreationDate string // time string of format "2006-01-02T15:04:05.000Z"} // ObjectVersion container for object version metadatatype ObjectVersion struct { Object IsLatest bool VersionID string `xml:"VersionId"` isDeleteMarker bool} // MarshalXML - marshal ObjectVersionfunc (o ObjectVersion) MarshalXML(e *xml.Encoder, start xml.StartElement) error { if o.isDeleteMarker { start.Name.Local = "DeleteMarker" } else { start.Name.Local = "Version" } type objectVersionWrapper ObjectVersion return e.EncodeElement(objectVersionWrapper(o), start)} // StringMap is a map[string]stringtype StringMap map[string]string // MarshalXML - StringMap marshals into XML.func (s StringMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error { tokens := []xml.Token{start} for key, value := range s { t := xml.StartElement{} t.Name = xml.Name{ Space: "", Local: key, } tokens = append(tokens, t, xml.CharData(value), xml.EndElement{Name: t.Name}) } tokens = append(tokens, xml.EndElement{ Name: start.Name, }) for _, t := range tokens { if err := e.EncodeToken(t); err != nil { return err } } // flush to ensure tokens are written return e.Flush()} // Object container for object metadatatype Object struct { Key string LastModified string // time string of format "2006-01-02T15:04:05.000Z" ETag string Size int64 // Owner of the object. Owner Owner // The class of storage used to store the object. StorageClass string // UserMetadata user-defined metadata UserMetadata StringMap `xml:"UserMetadata,omitempty"`} // CopyObjectResponse container returns ETag and LastModified of the successfully copied objecttype CopyObjectResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CopyObjectResult" json:"-"` LastModified string // time string of format "2006-01-02T15:04:05.000Z" ETag string // md5sum of the copied object.} // CopyObjectPartResponse container returns ETag and LastModified of the successfully copied objecttype CopyObjectPartResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CopyPartResult" json:"-"` LastModified string // time string of format "2006-01-02T15:04:05.000Z" ETag string // md5sum of the copied object part.} // Initiator inherit from Owner struct, fields are sametype Initiator Owner // Owner - bucket owner/principaltype Owner struct { ID string DisplayName string} // InitiateMultipartUploadResponse container for InitiateMultiPartUpload response, provides uploadID to start MultiPart uploadtype InitiateMultipartUploadResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ InitiateMultipartUploadResult" json:"-"` Bucket string Key string UploadID string `xml:"UploadId"`} // CompleteMultipartUploadResponse container for completed multipart upload responsetype CompleteMultipartUploadResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CompleteMultipartUploadResult" json:"-"` Location string Bucket string Key string ETag string} // DeleteError structure.type DeleteError struct { Code string Message string Key string VersionID string `xml:"VersionId"`} // DeleteObjectsResponse container for multiple object deletes.type DeleteObjectsResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ DeleteResult" json:"-"` // Collection of all deleted objects DeletedObjects []DeletedObject `xml:"Deleted,omitempty"` // Collection of errors deleting certain objects. Errors []DeleteError `xml:"Error,omitempty"`} // PostResponse container for POST object request when success_action_status is set to 201type PostResponse struct { Bucket string Key string ETag string Location string} // returns "https" if the tls boolean is true, "http" otherwise.func getURLScheme(tls bool) string { if tls { return httpsScheme } return httpScheme} // getObjectLocation gets the fully qualified URL of an object.func getObjectLocation(r *http.Request, domains []string, bucket, object string) string { // unit tests do not have host set. if r.Host == "" { return path.Clean(r.URL.Path) } proto := handlers.GetSourceScheme(r) if proto == "" { proto = getURLScheme(globalIsTLS) } u := &url.URL{ Host: r.Host, Path: path.Join(SlashSeparator, bucket, object), Scheme: proto, } // If domain is set then we need to use bucket DNS style. for _, domain := range domains { if strings.HasPrefix(r.Host, bucket+"."+domain) { u.Path = path.Join(SlashSeparator, object) break } } return u.String()} // generates ListBucketsResponse from array of BucketInfo which can be// serialized to match XML and JSON API spec output.func generateListBucketsResponse(buckets []BucketInfo) ListBucketsResponse { listbuckets := make([]Bucket, 0, len(buckets)) data := ListBucketsResponse{} owner := Owner{ ID: globalMinioDefaultOwnerID, DisplayName: "minio", } for _, bucket := range buckets { listbuckets = append(listbuckets, Bucket{ Name: bucket.Name, CreationDate: bucket.Created.UTC().Format(iso8601TimeFormat), }) } data.Owner = owner data.Buckets.Buckets = listbuckets return data} // generates an ListBucketVersions response for the said bucket with other enumerated options.func generateListVersionsResponse(bucket, prefix, marker, versionIDMarker, delimiter, encodingType string, maxKeys int, resp ListObjectVersionsInfo) ListVersionsResponse { versions := make([]ObjectVersion, 0, len(resp.Objects)) owner := Owner{ ID: globalMinioDefaultOwnerID, DisplayName: "minio", } data := ListVersionsResponse{} for _, object := range resp.Objects { content := ObjectVersion{} if object.Name == "" { continue } content.Key = s3EncodeName(object.Name, encodingType) content.LastModified = object.ModTime.UTC().Format(iso8601TimeFormat) if object.ETag != "" { content.ETag = "\"" + object.ETag + "\"" } content.Size = object.Size if object.StorageClass != "" { content.StorageClass = object.StorageClass } else { content.StorageClass = globalMinioDefaultStorageClass } content.Owner = owner content.VersionID = object.VersionID if content.VersionID == "" { content.VersionID = nullVersionID } content.IsLatest = object.IsLatest content.isDeleteMarker = object.DeleteMarker versions = append(versions, content) } data.Name = bucket data.Versions = versions data.EncodingType = encodingType data.Prefix = s3EncodeName(prefix, encodingType) data.KeyMarker = s3EncodeName(marker, encodingType) data.Delimiter = s3EncodeName(delimiter, encodingType) data.MaxKeys = maxKeys data.NextKeyMarker = s3EncodeName(resp.NextMarker, encodingType) data.NextVersionIDMarker = resp.NextVersionIDMarker data.VersionIDMarker = versionIDMarker data.IsTruncated = resp.IsTruncated prefixes := make([]CommonPrefix, 0, len(resp.Prefixes)) for _, prefix := range resp.Prefixes { prefixItem := CommonPrefix{} prefixItem.Prefix = s3EncodeName(prefix, encodingType) prefixes = append(prefixes, prefixItem) } data.CommonPrefixes = prefixes return data} // generates an ListObjectsV1 response for the said bucket with other enumerated options.func generateListObjectsV1Response(bucket, prefix, marker, delimiter, encodingType string, maxKeys int, resp ListObjectsInfo) ListObjectsResponse { contents := make([]Object, 0, len(resp.Objects)) owner := Owner{ ID: globalMinioDefaultOwnerID,Lines 1–500 of 851. Load more
// Copyright (c) 2015-2021 MinIO, Inc. // // This file is part of MinIO Object Storage stack // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. package cmd import ( "context" "encoding/base64" "encoding/xml" "fmt" "net/http" "net/url" "path" "strconv" "strings" "time" "github.com/minio/minio/internal/crypto" "github.com/minio/minio/internal/handlers" xhttp "github.com/minio/minio/internal/http" "github.com/minio/minio/internal/logger" ) const ( // RFC3339 a subset of the ISO8601 timestamp format. e.g 2014-04-29T18:30:38Z iso8601TimeFormat = "2006-01-02T15:04:05.000Z" // Reply date format with nanosecond precision. maxObjectList = 1000 // Limit number of objects in a listObjectsResponse/listObjectsVersionsResponse. maxDeleteList = 1000 // Limit number of objects deleted in a delete call. maxUploadsList = 10000 // Limit number of uploads in a listUploadsResponse. maxPartsList = 10000 // Limit number of parts in a listPartsResponse. ) // LocationResponse - format for location response. type LocationResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ LocationConstraint" json:"-"` Location string `xml:",chardata"` } // PolicyStatus captures information returned by GetBucketPolicyStatusHandler type PolicyStatus struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ PolicyStatus" json:"-"` IsPublic string } // ListVersionsResponse - format for list bucket versions response. type ListVersionsResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListVersionsResult" json:"-"` Name string Prefix string KeyMarker string // When response is truncated (the IsTruncated element value in the response // is true), you can use the key name in this field as marker in the subsequent // request to get next set of objects. Server lists objects in alphabetical // order Note: This element is returned only if you have delimiter request parameter // specified. If response does not include the NextMaker and it is truncated, // you can use the value of the last Key in the response as the marker in the // subsequent request to get the next set of object keys. NextKeyMarker string `xml:"NextKeyMarker,omitempty"` // When the number of responses exceeds the value of MaxKeys, // NextVersionIdMarker specifies the first object version not // returned that satisfies the search criteria. Use this value // for the version-id-marker request parameter in a subsequent request. NextVersionIDMarker string `xml:"NextVersionIdMarker"` // Marks the last version of the Key returned in a truncated response. VersionIDMarker string `xml:"VersionIdMarker"` MaxKeys int Delimiter string // A flag that indicates whether or not ListObjects returned all of the results // that satisfied the search criteria. IsTruncated bool CommonPrefixes []CommonPrefix Versions []ObjectVersion // Encoding type used to encode object keys in the response. EncodingType string `xml:"EncodingType,omitempty"` } // ListObjectsResponse - format for list objects response. type ListObjectsResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult" json:"-"` Name string Prefix string Marker string // When response is truncated (the IsTruncated element value in the response // is true), you can use the key name in this field as marker in the subsequent // request to get next set of objects. Server lists objects in alphabetical // order Note: This element is returned only if you have delimiter request parameter // specified. If response does not include the NextMaker and it is truncated, // you can use the value of the last Key in the response as the marker in the // subsequent request to get the next set of object keys. NextMarker string `xml:"NextMarker,omitempty"` MaxKeys int Delimiter string // A flag that indicates whether or not ListObjects returned all of the results // that satisfied the search criteria. IsTruncated bool Contents []Object CommonPrefixes []CommonPrefix // Encoding type used to encode object keys in the response. EncodingType string `xml:"EncodingType,omitempty"` } // ListObjectsV2Response - format for list objects response. type ListObjectsV2Response struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult" json:"-"` Name string Prefix string StartAfter string `xml:"StartAfter,omitempty"` // When response is truncated (the IsTruncated element value in the response // is true), you can use the key name in this field as marker in the subsequent // request to get next set of objects. Server lists objects in alphabetical // order Note: This element is returned only if you have delimiter request parameter // specified. If response does not include the NextMaker and it is truncated, // you can use the value of the last Key in the response as the marker in the // subsequent request to get the next set of object keys. ContinuationToken string `xml:"ContinuationToken,omitempty"` NextContinuationToken string `xml:"NextContinuationToken,omitempty"` KeyCount int MaxKeys int Delimiter string // A flag that indicates whether or not ListObjects returned all of the results // that satisfied the search criteria. IsTruncated bool Contents []Object CommonPrefixes []CommonPrefix // Encoding type used to encode object keys in the response. EncodingType string `xml:"EncodingType,omitempty"` } // Part container for part metadata. type Part struct { PartNumber int LastModified string ETag string Size int64 } // ListPartsResponse - format for list parts response. type ListPartsResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListPartsResult" json:"-"` Bucket string Key string UploadID string `xml:"UploadId"` Initiator Initiator Owner Owner // The class of storage used to store the object. StorageClass string PartNumberMarker int NextPartNumberMarker int MaxParts int IsTruncated bool // List of parts. Parts []Part `xml:"Part"` } // ListMultipartUploadsResponse - format for list multipart uploads response. type ListMultipartUploadsResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListMultipartUploadsResult" json:"-"` Bucket string KeyMarker string UploadIDMarker string `xml:"UploadIdMarker"` NextKeyMarker string NextUploadIDMarker string `xml:"NextUploadIdMarker"` Delimiter string Prefix string EncodingType string `xml:"EncodingType,omitempty"` MaxUploads int IsTruncated bool // List of pending uploads. Uploads []Upload `xml:"Upload"` // Delimed common prefixes. CommonPrefixes []CommonPrefix } // ListBucketsResponse - format for list buckets response type ListBucketsResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListAllMyBucketsResult" json:"-"` Owner Owner // Container for one or more buckets. Buckets struct { Buckets []Bucket `xml:"Bucket"` } // Buckets are nested } // Upload container for in progress multipart upload type Upload struct { Key string UploadID string `xml:"UploadId"` Initiator Initiator Owner Owner StorageClass string Initiated string } // CommonPrefix container for prefix response in ListObjectsResponse type CommonPrefix struct { Prefix string } // Bucket container for bucket metadata type Bucket struct { Name string CreationDate string // time string of format "2006-01-02T15:04:05.000Z" } // ObjectVersion container for object version metadata type ObjectVersion struct { Object IsLatest bool VersionID string `xml:"VersionId"` isDeleteMarker bool } // MarshalXML - marshal ObjectVersion func (o ObjectVersion) MarshalXML(e *xml.Encoder, start xml.StartElement) error { if o.isDeleteMarker { start.Name.Local = "DeleteMarker" } else { start.Name.Local = "Version" } type objectVersionWrapper ObjectVersion return e.EncodeElement(objectVersionWrapper(o), start) } // StringMap is a map[string]string type StringMap map[string]string // MarshalXML - StringMap marshals into XML. func (s StringMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error { tokens := []xml.Token{start} for key, value := range s { t := xml.StartElement{} t.Name = xml.Name{ Space: "", Local: key, } tokens = append(tokens, t, xml.CharData(value), xml.EndElement{Name: t.Name}) } tokens = append(tokens, xml.EndElement{ Name: start.Name, }) for _, t := range tokens { if err := e.EncodeToken(t); err != nil { return err } } // flush to ensure tokens are written return e.Flush() } // Object container for object metadata type Object struct { Key string LastModified string // time string of format "2006-01-02T15:04:05.000Z" ETag string Size int64 // Owner of the object. Owner Owner // The class of storage used to store the object. StorageClass string // UserMetadata user-defined metadata UserMetadata StringMap `xml:"UserMetadata,omitempty"` } // CopyObjectResponse container returns ETag and LastModified of the successfully copied object type CopyObjectResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CopyObjectResult" json:"-"` LastModified string // time string of format "2006-01-02T15:04:05.000Z" ETag string // md5sum of the copied object. } // CopyObjectPartResponse container returns ETag and LastModified of the successfully copied object type CopyObjectPartResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CopyPartResult" json:"-"` LastModified string // time string of format "2006-01-02T15:04:05.000Z" ETag string // md5sum of the copied object part. } // Initiator inherit from Owner struct, fields are same type Initiator Owner // Owner - bucket owner/principal type Owner struct { ID string DisplayName string } // InitiateMultipartUploadResponse container for InitiateMultiPartUpload response, provides uploadID to start MultiPart upload type InitiateMultipartUploadResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ InitiateMultipartUploadResult" json:"-"` Bucket string Key string UploadID string `xml:"UploadId"` } // CompleteMultipartUploadResponse container for completed multipart upload response type CompleteMultipartUploadResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CompleteMultipartUploadResult" json:"-"` Location string Bucket string Key string ETag string } // DeleteError structure. type DeleteError struct { Code string Message string Key string VersionID string `xml:"VersionId"` } // DeleteObjectsResponse container for multiple object deletes. type DeleteObjectsResponse struct { XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ DeleteResult" json:"-"` // Collection of all deleted objects DeletedObjects []DeletedObject `xml:"Deleted,omitempty"` // Collection of errors deleting certain objects. Errors []DeleteError `xml:"Error,omitempty"` } // PostResponse container for POST object request when success_action_status is set to 201 type PostResponse struct { Bucket string Key string ETag string Location string } // returns "https" if the tls boolean is true, "http" otherwise. func getURLScheme(tls bool) string { if tls { return httpsScheme } return httpScheme } // getObjectLocation gets the fully qualified URL of an object. func getObjectLocation(r *http.Request, domains []string, bucket, object string) string { // unit tests do not have host set. if r.Host == "" { return path.Clean(r.URL.Path) } proto := handlers.GetSourceScheme(r) if proto == "" { proto = getURLScheme(globalIsTLS) } u := &url.URL{ Host: r.Host, Path: path.Join(SlashSeparator, bucket, object), Scheme: proto, } // If domain is set then we need to use bucket DNS style. for _, domain := range domains { if strings.HasPrefix(r.Host, bucket+"."+domain) { u.Path = path.Join(SlashSeparator, object) break } } return u.String() } // generates ListBucketsResponse from array of BucketInfo which can be // serialized to match XML and JSON API spec output. func generateListBucketsResponse(buckets []BucketInfo) ListBucketsResponse { listbuckets := make([]Bucket, 0, len(buckets)) data := ListBucketsResponse{} owner := Owner{ ID: globalMinioDefaultOwnerID, DisplayName: "minio", } for _, bucket := range buckets { listbuckets = append(listbuckets, Bucket{ Name: bucket.Name, CreationDate: bucket.Created.UTC().Format(iso8601TimeFormat), }) } data.Owner = owner data.Buckets.Buckets = listbuckets return data } // generates an ListBucketVersions response for the said bucket with other enumerated options. func generateListVersionsResponse(bucket, prefix, marker, versionIDMarker, delimiter, encodingType string, maxKeys int, resp ListObjectVersionsInfo) ListVersionsResponse { versions := make([]ObjectVersion, 0, len(resp.Objects)) owner := Owner{ ID: globalMinioDefaultOwnerID, DisplayName: "minio", } data := ListVersionsResponse{} for _, object := range resp.Objects { content := ObjectVersion{} if object.Name == "" { continue } content.Key = s3EncodeName(object.Name, encodingType) content.LastModified = object.ModTime.UTC().Format(iso8601TimeFormat) if object.ETag != "" { content.ETag = "\"" + object.ETag + "\"" } content.Size = object.Size if object.StorageClass != "" { content.StorageClass = object.StorageClass } else { content.StorageClass = globalMinioDefaultStorageClass } content.Owner = owner content.VersionID = object.VersionID if content.VersionID == "" { content.VersionID = nullVersionID } content.IsLatest = object.IsLatest content.isDeleteMarker = object.DeleteMarker versions = append(versions, content) } data.Name = bucket data.Versions = versions data.EncodingType = encodingType data.Prefix = s3EncodeName(prefix, encodingType) data.KeyMarker = s3EncodeName(marker, encodingType) data.Delimiter = s3EncodeName(delimiter, encodingType) data.MaxKeys = maxKeys data.NextKeyMarker = s3EncodeName(resp.NextMarker, encodingType) data.NextVersionIDMarker = resp.NextVersionIDMarker data.VersionIDMarker = versionIDMarker data.IsTruncated = resp.IsTruncated prefixes := make([]CommonPrefix, 0, len(resp.Prefixes)) for _, prefix := range resp.Prefixes { prefixItem := CommonPrefix{} prefixItem.Prefix = s3EncodeName(prefix, encodingType) prefixes = append(prefixes, prefixItem) } data.CommonPrefixes = prefixes return data } // generates an ListObjectsV1 response for the said bucket with other enumerated options. func generateListObjectsV1Response(bucket, prefix, marker, delimiter, encodingType string, maxKeys int, resp ListObjectsInfo) ListObjectsResponse { contents := make([]Object, 0, len(resp.Objects)) owner := Owner{ ID: globalMinioDefaultOwnerID,