| 
									
										
										
										
											2015-02-24 08:46:48 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2015-07-25 08:51:40 +08:00
										 |  |  |  * Minio Cloud Storage, (C) 2015 Minio, Inc. | 
					
						
							| 
									
										
										
										
											2015-02-24 08:46:48 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-19 15:52:01 +08:00
										 |  |  | package main | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2015-04-23 07:28:13 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2015-02-28 09:50:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/fs" | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-24 08:46:48 +08:00
										 |  |  | // Reply date format
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 	rfcFormat = "2006-01-02T15:04:05.000Z" | 
					
						
							| 
									
										
										
										
											2015-02-24 08:46:48 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // takes an array of Bucketmetadata information for serialization
 | 
					
						
							|  |  |  | // input:
 | 
					
						
							|  |  |  | // array of bucket metadata
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // output:
 | 
					
						
							|  |  |  | // populated struct that can be serialized to match xml and json api spec output
 | 
					
						
							| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  | func generateListBucketsResponse(buckets []fs.BucketMetadata) ListBucketsResponse { | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 	var listbuckets []*Bucket | 
					
						
							| 
									
										
										
										
											2015-04-30 06:28:04 +08:00
										 |  |  | 	var data = ListBucketsResponse{} | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 	var owner = Owner{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	owner.ID = "minio" | 
					
						
							|  |  |  | 	owner.DisplayName = "minio" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, bucket := range buckets { | 
					
						
							|  |  |  | 		var listbucket = &Bucket{} | 
					
						
							|  |  |  | 		listbucket.Name = bucket.Name | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 		listbucket.CreationDate = bucket.Created.Format(rfcFormat) | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 		listbuckets = append(listbuckets, listbucket) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data.Owner = owner | 
					
						
							|  |  |  | 	data.Buckets.Bucket = listbuckets | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return data | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-09 02:12:02 +08:00
										 |  |  | // generates an AccessControlPolicy response for the said ACL.
 | 
					
						
							| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  | func generateAccessControlPolicyResponse(acl fs.BucketACL) AccessControlPolicyResponse { | 
					
						
							| 
									
										
										
										
											2015-10-09 02:12:02 +08:00
										 |  |  | 	accessCtrlPolicyResponse := AccessControlPolicyResponse{} | 
					
						
							|  |  |  | 	accessCtrlPolicyResponse.Owner = Owner{ | 
					
						
							|  |  |  | 		ID:          "minio", | 
					
						
							|  |  |  | 		DisplayName: "minio", | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defaultGrant := Grant{} | 
					
						
							|  |  |  | 	defaultGrant.Grantee.ID = "minio" | 
					
						
							|  |  |  | 	defaultGrant.Grantee.DisplayName = "minio" | 
					
						
							|  |  |  | 	defaultGrant.Permission = "FULL_CONTROL" | 
					
						
							|  |  |  | 	accessCtrlPolicyResponse.AccessControlList.Grant = append(accessCtrlPolicyResponse.AccessControlList.Grant, defaultGrant) | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case acl.IsPublicRead(): | 
					
						
							|  |  |  | 		publicReadGrant := Grant{} | 
					
						
							|  |  |  | 		publicReadGrant.Grantee.ID = "minio" | 
					
						
							|  |  |  | 		publicReadGrant.Grantee.DisplayName = "minio" | 
					
						
							|  |  |  | 		publicReadGrant.Grantee.URI = "http://acs.amazonaws.com/groups/global/AllUsers" | 
					
						
							|  |  |  | 		publicReadGrant.Permission = "READ" | 
					
						
							|  |  |  | 		accessCtrlPolicyResponse.AccessControlList.Grant = append(accessCtrlPolicyResponse.AccessControlList.Grant, publicReadGrant) | 
					
						
							|  |  |  | 	case acl.IsPublicReadWrite(): | 
					
						
							|  |  |  | 		publicReadGrant := Grant{} | 
					
						
							|  |  |  | 		publicReadGrant.Grantee.ID = "minio" | 
					
						
							|  |  |  | 		publicReadGrant.Grantee.DisplayName = "minio" | 
					
						
							|  |  |  | 		publicReadGrant.Grantee.URI = "http://acs.amazonaws.com/groups/global/AllUsers" | 
					
						
							|  |  |  | 		publicReadGrant.Permission = "READ" | 
					
						
							|  |  |  | 		publicReadWriteGrant := Grant{} | 
					
						
							|  |  |  | 		publicReadWriteGrant.Grantee.ID = "minio" | 
					
						
							|  |  |  | 		publicReadWriteGrant.Grantee.DisplayName = "minio" | 
					
						
							|  |  |  | 		publicReadWriteGrant.Grantee.URI = "http://acs.amazonaws.com/groups/global/AllUsers" | 
					
						
							|  |  |  | 		publicReadWriteGrant.Permission = "WRITE" | 
					
						
							|  |  |  | 		accessCtrlPolicyResponse.AccessControlList.Grant = append(accessCtrlPolicyResponse.AccessControlList.Grant, publicReadGrant) | 
					
						
							|  |  |  | 		accessCtrlPolicyResponse.AccessControlList.Grant = append(accessCtrlPolicyResponse.AccessControlList.Grant, publicReadWriteGrant) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return accessCtrlPolicyResponse | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // generates an ListObjects response for the said bucket with other enumerated options.
 | 
					
						
							| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  | func generateListObjectsResponse(bucket string, objects []fs.ObjectMetadata, bucketResources fs.BucketResourcesMetadata) ListObjectsResponse { | 
					
						
							| 
									
										
										
										
											2015-05-06 05:27:18 +08:00
										 |  |  | 	var contents []*Object | 
					
						
							|  |  |  | 	var prefixes []*CommonPrefix | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 	var owner = Owner{} | 
					
						
							| 
									
										
										
										
											2015-04-30 06:28:04 +08:00
										 |  |  | 	var data = ListObjectsResponse{} | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	owner.ID = "minio" | 
					
						
							|  |  |  | 	owner.DisplayName = "minio" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, object := range objects { | 
					
						
							| 
									
										
										
										
											2015-05-06 05:27:18 +08:00
										 |  |  | 		var content = &Object{} | 
					
						
							| 
									
										
										
										
											2015-07-01 08:08:18 +08:00
										 |  |  | 		if object.Object == "" { | 
					
						
							| 
									
										
										
										
											2015-03-01 06:44:26 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-07-01 08:08:18 +08:00
										 |  |  | 		content.Key = object.Object | 
					
						
							| 
									
										
										
										
											2015-07-10 10:34:07 +08:00
										 |  |  | 		content.LastModified = object.Created.Format(rfcFormat) | 
					
						
							| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  | 		content.ETag = "\"" + object.Md5 + "\"" | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 		content.Size = object.Size | 
					
						
							|  |  |  | 		content.StorageClass = "STANDARD" | 
					
						
							|  |  |  | 		content.Owner = owner | 
					
						
							|  |  |  | 		contents = append(contents, content) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-05-06 05:27:18 +08:00
										 |  |  | 	// TODO - support EncodingType in xml decoding
 | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 	data.Name = bucket | 
					
						
							|  |  |  | 	data.Contents = contents | 
					
						
							| 
									
										
										
										
											2015-03-01 06:44:26 +08:00
										 |  |  | 	data.MaxKeys = bucketResources.Maxkeys | 
					
						
							|  |  |  | 	data.Prefix = bucketResources.Prefix | 
					
						
							|  |  |  | 	data.Delimiter = bucketResources.Delimiter | 
					
						
							|  |  |  | 	data.Marker = bucketResources.Marker | 
					
						
							| 
									
										
										
										
											2015-05-06 05:27:18 +08:00
										 |  |  | 	data.NextMarker = bucketResources.NextMarker | 
					
						
							| 
									
										
										
										
											2015-03-01 06:44:26 +08:00
										 |  |  | 	data.IsTruncated = bucketResources.IsTruncated | 
					
						
							|  |  |  | 	for _, prefix := range bucketResources.CommonPrefixes { | 
					
						
							| 
									
										
										
										
											2015-05-06 05:27:18 +08:00
										 |  |  | 		var prefixItem = &CommonPrefix{} | 
					
						
							| 
									
										
										
										
											2015-03-01 06:44:26 +08:00
										 |  |  | 		prefixItem.Prefix = prefix | 
					
						
							|  |  |  | 		prefixes = append(prefixes, prefixItem) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	data.CommonPrefixes = prefixes | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 	return data | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-04-23 07:28:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-10 10:01:15 +08:00
										 |  |  | // generateInitiateMultipartUploadResponse
 | 
					
						
							|  |  |  | func generateInitiateMultipartUploadResponse(bucket, key, uploadID string) InitiateMultipartUploadResponse { | 
					
						
							|  |  |  | 	return InitiateMultipartUploadResponse{ | 
					
						
							| 
									
										
										
										
											2015-05-08 10:55:30 +08:00
										 |  |  | 		Bucket:   bucket, | 
					
						
							|  |  |  | 		Key:      key, | 
					
						
							|  |  |  | 		UploadID: uploadID, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-10 10:01:15 +08:00
										 |  |  | // generateCompleteMultipartUploadResponse
 | 
					
						
							|  |  |  | func generateCompleteMultpartUploadResponse(bucket, key, location, etag string) CompleteMultipartUploadResponse { | 
					
						
							|  |  |  | 	return CompleteMultipartUploadResponse{ | 
					
						
							| 
									
										
										
										
											2015-05-08 13:43:19 +08:00
										 |  |  | 		Location: location, | 
					
						
							|  |  |  | 		Bucket:   bucket, | 
					
						
							|  |  |  | 		Key:      key, | 
					
						
							|  |  |  | 		ETag:     etag, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-10 02:41:26 +08:00
										 |  |  | // generateListPartsResult
 | 
					
						
							| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  | func generateListPartsResponse(objectMetadata fs.ObjectResourcesMetadata) ListPartsResponse { | 
					
						
							| 
									
										
										
										
											2015-05-10 02:41:26 +08:00
										 |  |  | 	// TODO - support EncodingType in xml decoding
 | 
					
						
							|  |  |  | 	listPartsResponse := ListPartsResponse{} | 
					
						
							|  |  |  | 	listPartsResponse.Bucket = objectMetadata.Bucket | 
					
						
							| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  | 	listPartsResponse.Key = objectMetadata.Object | 
					
						
							| 
									
										
										
										
											2015-05-10 02:41:26 +08:00
										 |  |  | 	listPartsResponse.UploadID = objectMetadata.UploadID | 
					
						
							|  |  |  | 	listPartsResponse.StorageClass = "STANDARD" | 
					
						
							|  |  |  | 	listPartsResponse.Initiator.ID = "minio" | 
					
						
							|  |  |  | 	listPartsResponse.Initiator.DisplayName = "minio" | 
					
						
							|  |  |  | 	listPartsResponse.Owner.ID = "minio" | 
					
						
							|  |  |  | 	listPartsResponse.Owner.DisplayName = "minio" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	listPartsResponse.MaxParts = objectMetadata.MaxParts | 
					
						
							|  |  |  | 	listPartsResponse.PartNumberMarker = objectMetadata.PartNumberMarker | 
					
						
							|  |  |  | 	listPartsResponse.IsTruncated = objectMetadata.IsTruncated | 
					
						
							|  |  |  | 	listPartsResponse.NextPartNumberMarker = objectMetadata.NextPartNumberMarker | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	listPartsResponse.Part = make([]*Part, len(objectMetadata.Part)) | 
					
						
							|  |  |  | 	for _, part := range objectMetadata.Part { | 
					
						
							|  |  |  | 		newPart := &Part{} | 
					
						
							|  |  |  | 		newPart.PartNumber = part.PartNumber | 
					
						
							| 
									
										
										
										
											2015-06-09 16:12:32 +08:00
										 |  |  | 		newPart.ETag = "\"" + part.ETag + "\"" | 
					
						
							| 
									
										
										
										
											2015-05-10 02:41:26 +08:00
										 |  |  | 		newPart.Size = part.Size | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 		newPart.LastModified = part.LastModified.Format(rfcFormat) | 
					
						
							| 
									
										
										
										
											2015-05-10 02:41:26 +08:00
										 |  |  | 		listPartsResponse.Part = append(listPartsResponse.Part, newPart) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return listPartsResponse | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-10 10:01:15 +08:00
										 |  |  | // generateListMultipartUploadsResponse
 | 
					
						
							| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  | func generateListMultipartUploadsResponse(bucket string, metadata fs.BucketMultipartResourcesMetadata) ListMultipartUploadsResponse { | 
					
						
							| 
									
										
										
										
											2015-05-15 05:36:41 +08:00
										 |  |  | 	listMultipartUploadsResponse := ListMultipartUploadsResponse{} | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.Bucket = bucket | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.Delimiter = metadata.Delimiter | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.IsTruncated = metadata.IsTruncated | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.EncodingType = metadata.EncodingType | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.Prefix = metadata.Prefix | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.KeyMarker = metadata.KeyMarker | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.NextKeyMarker = metadata.NextKeyMarker | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.MaxUploads = metadata.MaxUploads | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.NextUploadIDMarker = metadata.NextUploadIDMarker | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.UploadIDMarker = metadata.UploadIDMarker | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.Upload = make([]*Upload, len(metadata.Upload)) | 
					
						
							|  |  |  | 	for _, upload := range metadata.Upload { | 
					
						
							|  |  |  | 		newUpload := &Upload{} | 
					
						
							|  |  |  | 		newUpload.UploadID = upload.UploadID | 
					
						
							| 
									
										
										
										
											2015-10-17 02:26:01 +08:00
										 |  |  | 		newUpload.Key = upload.Object | 
					
						
							| 
									
										
										
										
											2015-07-09 07:54:32 +08:00
										 |  |  | 		newUpload.Initiated = upload.Initiated.Format(rfcFormat) | 
					
						
							| 
									
										
										
										
											2015-05-15 05:36:41 +08:00
										 |  |  | 		listMultipartUploadsResponse.Upload = append(listMultipartUploadsResponse.Upload, newUpload) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return listMultipartUploadsResponse | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 16:40:06 +08:00
										 |  |  | // writeSuccessResponse write success headers and response if any.
 | 
					
						
							|  |  |  | func writeSuccessResponse(w http.ResponseWriter, response []byte) { | 
					
						
							|  |  |  | 	setCommonHeaders(w) | 
					
						
							|  |  |  | 	if response == nil { | 
					
						
							|  |  |  | 		w.WriteHeader(http.StatusOK) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	w.Write(response) | 
					
						
							|  |  |  | 	w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2015-04-30 01:51:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-17 11:02:37 +08:00
										 |  |  | // writeSuccessNoContent write success headers with http status 204
 | 
					
						
							|  |  |  | func writeSuccessNoContent(w http.ResponseWriter) { | 
					
						
							| 
									
										
										
										
											2016-01-08 16:40:06 +08:00
										 |  |  | 	setCommonHeaders(w) | 
					
						
							| 
									
										
										
										
											2015-10-17 11:02:37 +08:00
										 |  |  | 	w.WriteHeader(http.StatusNoContent) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-10 02:41:26 +08:00
										 |  |  | // writeErrorRespone write error headers
 | 
					
						
							| 
									
										
										
										
											2015-10-04 15:27:49 +08:00
										 |  |  | func writeErrorResponse(w http.ResponseWriter, req *http.Request, errorType int, resource string) { | 
					
						
							| 
									
										
										
										
											2015-04-23 07:28:13 +08:00
										 |  |  | 	error := getErrorCode(errorType) | 
					
						
							| 
									
										
										
										
											2015-04-30 06:28:04 +08:00
										 |  |  | 	// generate error response
 | 
					
						
							| 
									
										
										
										
											2015-04-23 07:28:13 +08:00
										 |  |  | 	errorResponse := getErrorResponse(error, resource) | 
					
						
							| 
									
										
										
										
											2015-10-04 15:27:49 +08:00
										 |  |  | 	encodedErrorResponse := encodeErrorResponse(errorResponse) | 
					
						
							| 
									
										
										
										
											2015-04-30 06:28:04 +08:00
										 |  |  | 	// set common headers
 | 
					
						
							| 
									
										
										
										
											2016-01-08 16:40:06 +08:00
										 |  |  | 	setCommonHeaders(w) | 
					
						
							| 
									
										
										
										
											2015-05-16 11:44:42 +08:00
										 |  |  | 	// write Header
 | 
					
						
							| 
									
										
										
										
											2015-04-23 07:28:13 +08:00
										 |  |  | 	w.WriteHeader(error.HTTPStatusCode) | 
					
						
							| 
									
										
										
										
											2015-09-19 17:36:50 +08:00
										 |  |  | 	// HEAD should have no body, do not attempt to write to it
 | 
					
						
							| 
									
										
										
										
											2015-07-11 12:06:43 +08:00
										 |  |  | 	if req.Method != "HEAD" { | 
					
						
							|  |  |  | 		// write error body
 | 
					
						
							|  |  |  | 		w.Write(encodedErrorResponse) | 
					
						
							| 
									
										
										
										
											2016-01-08 16:40:06 +08:00
										 |  |  | 		w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2015-07-11 12:06:43 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-23 07:28:13 +08:00
										 |  |  | } |