| 
									
										
										
										
											2015-02-24 08:46:48 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2015, 2016, 2017, 2018 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | 	"encoding/xml" | 
					
						
							| 
									
										
										
										
											2015-04-23 07:28:13 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2017-01-06 16:37:00 +08:00
										 |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2016-06-21 14:25:18 +08:00
										 |  |  | 	"path" | 
					
						
							| 
									
										
										
										
											2018-05-16 09:20:22 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2016-02-27 19:04:52 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2018-03-03 07:23:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2018-03-03 07:23:04 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/handlers" | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-24 08:46:48 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2016-10-17 23:44:55 +08:00
										 |  |  | 	timeFormatAMZLong = "2006-01-02T15:04:05.000Z" // Reply date format with nanosecond precision.
 | 
					
						
							|  |  |  | 	maxObjectList     = 1000                       // Limit number of objects in a listObjectsResponse.
 | 
					
						
							|  |  |  | 	maxUploadsList    = 1000                       // Limit number of uploads in a listUploadsResponse.
 | 
					
						
							|  |  |  | 	maxPartsList      = 1000                       // Limit number of parts in a listPartsResponse.
 | 
					
						
							| 
									
										
										
										
											2015-02-24 08:46:48 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | // 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"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ListObjectsResponse - format for list objects response.
 | 
					
						
							|  |  |  | type ListObjectsResponse struct { | 
					
						
							|  |  |  | 	XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult" json:"-"` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | 	Name   string | 
					
						
							|  |  |  | 	Prefix string | 
					
						
							|  |  |  | 	Marker string | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// 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.
 | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | 	NextMarker string `xml:"NextMarker,omitempty"` | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | 	MaxKeys   int | 
					
						
							|  |  |  | 	Delimiter string | 
					
						
							|  |  |  | 	// A flag that indicates whether or not ListObjects returned all of the results
 | 
					
						
							|  |  |  | 	// that satisfied the search criteria.
 | 
					
						
							|  |  |  | 	IsTruncated bool | 
					
						
							| 
									
										
										
										
											2016-06-01 13:10:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	Contents       []Object | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | 	CommonPrefixes []CommonPrefix | 
					
						
							| 
									
										
										
										
											2016-06-01 13:10:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Encoding type used to encode object keys in the response.
 | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | 	EncodingType string `xml:"EncodingType,omitempty"` | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-06-01 13:10:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | // ListObjectsV2Response - format for list objects response.
 | 
					
						
							|  |  |  | type ListObjectsV2Response struct { | 
					
						
							|  |  |  | 	XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult" json:"-"` | 
					
						
							| 
									
										
										
										
											2016-06-01 13:10:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | 	Name       string | 
					
						
							|  |  |  | 	Prefix     string | 
					
						
							|  |  |  | 	StartAfter string `xml:"StartAfter,omitempty"` | 
					
						
							| 
									
										
										
										
											2016-06-01 13:10:55 +08:00
										 |  |  | 	// 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.
 | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | 	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"` | 
					
						
							| 
									
										
										
										
											2016-06-01 13:10:55 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | // Part container for part metadata.
 | 
					
						
							|  |  |  | type Part struct { | 
					
						
							|  |  |  | 	PartNumber   int | 
					
						
							|  |  |  | 	LastModified string | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | 	ETag         string | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | 	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"` | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | 	Delimiter          string | 
					
						
							|  |  |  | 	Prefix             string | 
					
						
							|  |  |  | 	EncodingType       string `xml:"EncodingType,omitempty"` | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | 	MaxUploads         int | 
					
						
							|  |  |  | 	IsTruncated        bool | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// List of pending uploads.
 | 
					
						
							|  |  |  | 	Uploads []Upload `xml:"Upload"` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Delimed common prefixes.
 | 
					
						
							|  |  |  | 	CommonPrefixes []CommonPrefix | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ListBucketsResponse - format for list buckets response
 | 
					
						
							|  |  |  | type ListBucketsResponse struct { | 
					
						
							|  |  |  | 	XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListAllMyBucketsResult" json:"-"` | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	Owner Owner | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | 	// 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 { | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	Key          string | 
					
						
							|  |  |  | 	UploadID     string `xml:"UploadId"` | 
					
						
							|  |  |  | 	Initiator    Initiator | 
					
						
							|  |  |  | 	Owner        Owner | 
					
						
							|  |  |  | 	StorageClass string | 
					
						
							|  |  |  | 	Initiated    string | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CommonPrefix container for prefix response in ListObjectsResponse
 | 
					
						
							|  |  |  | type CommonPrefix struct { | 
					
						
							|  |  |  | 	Prefix string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Bucket container for bucket metadata
 | 
					
						
							|  |  |  | type Bucket struct { | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	Name         string | 
					
						
							|  |  |  | 	CreationDate string // time string of format "2006-01-02T15:04:05.000Z"
 | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Object container for object metadata
 | 
					
						
							|  |  |  | type Object struct { | 
					
						
							|  |  |  | 	Key          string | 
					
						
							|  |  |  | 	LastModified string // time string of format "2006-01-02T15:04:05.000Z"
 | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | 	ETag         string | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | 	Size         int64 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | 	// Owner of the object.
 | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | 	Owner Owner | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// The class of storage used to store the object.
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	StorageClass string | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | // CopyObjectResponse container returns ETag and LastModified of the successfully copied object
 | 
					
						
							| 
									
										
										
										
											2016-02-27 19:04:52 +08:00
										 |  |  | type CopyObjectResponse struct { | 
					
						
							|  |  |  | 	XMLName      xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CopyObjectResult" json:"-"` | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | 	LastModified string   // time string of format "2006-01-02T15:04:05.000Z"
 | 
					
						
							|  |  |  | 	ETag         string   // md5sum of the copied object.
 | 
					
						
							| 
									
										
										
										
											2016-02-27 19:04:52 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-01 01:38:34 +08:00
										 |  |  | // 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.
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | // 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 | 
					
						
							| 
									
										
										
										
											2016-05-05 06:24:10 +08:00
										 |  |  | 	Key      string | 
					
						
							|  |  |  | 	ETag     string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-06 08:43:48 +08:00
										 |  |  | // DeleteError structure.
 | 
					
						
							|  |  |  | type DeleteError struct { | 
					
						
							|  |  |  | 	Code    string | 
					
						
							|  |  |  | 	Message string | 
					
						
							|  |  |  | 	Key     string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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 []ObjectIdentifier `xml:"Deleted,omitempty"` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Collection of errors deleting certain objects.
 | 
					
						
							|  |  |  | 	Errors []DeleteError `xml:"Error,omitempty"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-19 05:39:56 +08:00
										 |  |  | // 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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-25 07:43:21 +08:00
										 |  |  | // 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.
 | 
					
						
							| 
									
										
										
										
											2019-02-23 11:18:01 +08:00
										 |  |  | func getObjectLocation(r *http.Request, domains []string, bucket, object string) string { | 
					
						
							| 
									
										
										
										
											2018-03-24 04:46:57 +08:00
										 |  |  | 	// unit tests do not have host set.
 | 
					
						
							|  |  |  | 	if r.Host == "" { | 
					
						
							|  |  |  | 		return path.Clean(r.URL.Path) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-03 07:23:04 +08:00
										 |  |  | 	proto := handlers.GetSourceScheme(r) | 
					
						
							| 
									
										
										
										
											2017-09-25 07:43:21 +08:00
										 |  |  | 	if proto == "" { | 
					
						
							|  |  |  | 		proto = getURLScheme(globalIsSSL) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-03 07:23:04 +08:00
										 |  |  | 	u := &url.URL{ | 
					
						
							|  |  |  | 		Host:   r.Host, | 
					
						
							| 
									
										
										
										
											2017-09-25 07:43:21 +08:00
										 |  |  | 		Path:   path.Join(slashSeparator, bucket, object), | 
					
						
							|  |  |  | 		Scheme: proto, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-03 07:23:04 +08:00
										 |  |  | 	// If domain is set then we need to use bucket DNS style.
 | 
					
						
							| 
									
										
										
										
											2019-02-23 11:18:01 +08:00
										 |  |  | 	for _, domain := range domains { | 
					
						
							| 
									
										
										
										
											2018-05-16 09:20:22 +08:00
										 |  |  | 		if strings.Contains(r.Host, domain) { | 
					
						
							|  |  |  | 			u.Host = bucket + "." + r.Host | 
					
						
							|  |  |  | 			u.Path = path.Join(slashSeparator, object) | 
					
						
							| 
									
										
										
										
											2019-02-23 11:18:01 +08:00
										 |  |  | 			break | 
					
						
							| 
									
										
										
										
											2018-05-16 09:20:22 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-03-03 07:23:04 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-09-25 07:43:21 +08:00
										 |  |  | 	return u.String() | 
					
						
							| 
									
										
										
										
											2016-05-05 06:24:10 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-22 05:51:05 +08:00
										 |  |  | // generates ListBucketsResponse from array of BucketInfo which can be
 | 
					
						
							|  |  |  | // serialized to match XML and JSON API spec output.
 | 
					
						
							| 
									
										
											  
											
												fs: Break fs package to top-level and introduce ObjectAPI interface.
ObjectAPI interface brings in changes needed for XL ObjectAPI layer.
The new interface for any ObjectAPI layer is as below
```
// ObjectAPI interface.
type ObjectAPI interface {
        // Bucket resource API.
        DeleteBucket(bucket string) *probe.Error
        ListBuckets() ([]BucketInfo, *probe.Error)
        MakeBucket(bucket string) *probe.Error
        GetBucketInfo(bucket string) (BucketInfo, *probe.Error)
        // Bucket query API.
        ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsResult, *probe.Error)
        ListMultipartUploads(bucket string, resources BucketMultipartResourcesMetadata) (BucketMultipartResourcesMetadata, *probe.Error)
        // Object resource API.
        GetObject(bucket, object string, startOffset int64) (io.ReadCloser, *probe.Error)
        GetObjectInfo(bucket, object string) (ObjectInfo, *probe.Error)
        PutObject(bucket string, object string, size int64, data io.Reader, metadata map[string]string) (ObjectInfo, *probe.Error)
        DeleteObject(bucket, object string) *probe.Error
        // Object query API.
        NewMultipartUpload(bucket, object string) (string, *probe.Error)
        PutObjectPart(bucket, object, uploadID string, partID int, size int64, data io.Reader, md5Hex string) (string, *probe.Error)
        ListObjectParts(bucket, object string, resources ObjectResourcesMetadata) (ObjectResourcesMetadata, *probe.Error)
        CompleteMultipartUpload(bucket string, object string, uploadID string, parts []CompletePart) (ObjectInfo, *probe.Error)
        AbortMultipartUpload(bucket, object, uploadID string) *probe.Error
}
```
											
										 
											2016-03-31 07:15:28 +08:00
										 |  |  | func generateListBucketsResponse(buckets []BucketInfo) ListBucketsResponse { | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +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{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  | 	owner.ID = globalMinioDefaultOwnerID | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 	for _, bucket := range buckets { | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | 		var listbucket = Bucket{} | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 		listbucket.Name = bucket.Name | 
					
						
							| 
									
										
										
										
											2017-10-04 01:34:51 +08:00
										 |  |  | 		listbucket.CreationDate = bucket.Created.UTC().Format(timeFormatAMZLong) | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 		listbuckets = append(listbuckets, listbucket) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data.Owner = owner | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | 	data.Buckets.Buckets = listbuckets | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return data | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-18 03:32:05 +08:00
										 |  |  | // generates an ListObjectsV1 response for the said bucket with other enumerated options.
 | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | func generateListObjectsV1Response(bucket, prefix, marker, delimiter, encodingType string, maxKeys int, resp ListObjectsInfo) ListObjectsResponse { | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  | 	owner.ID = globalMinioDefaultOwnerID | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 	for _, object := range resp.Objects { | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | 		var content = Object{} | 
					
						
							| 
									
										
										
										
											2016-03-12 08:31:24 +08:00
										 |  |  | 		if object.Name == "" { | 
					
						
							| 
									
										
										
										
											2015-03-01 06:44:26 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | 		content.Key = s3EncodeName(object.Name, encodingType) | 
					
						
							| 
									
										
										
										
											2016-10-17 23:44:55 +08:00
										 |  |  | 		content.LastModified = object.ModTime.UTC().Format(timeFormatAMZLong) | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | 		if object.ETag != "" { | 
					
						
							|  |  |  | 			content.ETag = "\"" + object.ETag + "\"" | 
					
						
							| 
									
										
										
										
											2016-02-02 04:19:54 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 		content.Size = object.Size | 
					
						
							| 
									
										
										
										
											2018-04-06 01:56:28 +08:00
										 |  |  | 		content.StorageClass = object.StorageClass | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 		content.Owner = owner | 
					
						
							|  |  |  | 		contents = append(contents, content) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	data.Name = bucket | 
					
						
							|  |  |  | 	data.Contents = contents | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | 	data.EncodingType = encodingType | 
					
						
							|  |  |  | 	data.Prefix = s3EncodeName(prefix, encodingType) | 
					
						
							|  |  |  | 	data.Marker = s3EncodeName(marker, encodingType) | 
					
						
							|  |  |  | 	data.Delimiter = s3EncodeName(delimiter, encodingType) | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 	data.MaxKeys = maxKeys | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | 	data.NextMarker = s3EncodeName(resp.NextMarker, encodingType) | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 	data.IsTruncated = resp.IsTruncated | 
					
						
							| 
									
										
										
										
											2016-06-01 13:10:55 +08:00
										 |  |  | 	for _, prefix := range resp.Prefixes { | 
					
						
							|  |  |  | 		var prefixItem = CommonPrefix{} | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | 		prefixItem.Prefix = s3EncodeName(prefix, encodingType) | 
					
						
							| 
									
										
										
										
											2016-06-01 13:10:55 +08:00
										 |  |  | 		prefixes = append(prefixes, prefixItem) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	data.CommonPrefixes = prefixes | 
					
						
							|  |  |  | 	return data | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-18 03:32:05 +08:00
										 |  |  | // generates an ListObjectsV2 response for the said bucket with other enumerated options.
 | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | func generateListObjectsV2Response(bucket, prefix, token, nextToken, startAfter, delimiter, encodingType string, fetchOwner, isTruncated bool, maxKeys int, objects []ObjectInfo, prefixes []string) ListObjectsV2Response { | 
					
						
							| 
									
										
										
										
											2016-06-01 13:10:55 +08:00
										 |  |  | 	var contents []Object | 
					
						
							| 
									
										
										
										
											2017-06-17 13:17:00 +08:00
										 |  |  | 	var commonPrefixes []CommonPrefix | 
					
						
							| 
									
										
										
										
											2016-06-01 13:10:55 +08:00
										 |  |  | 	var owner = Owner{} | 
					
						
							|  |  |  | 	var data = ListObjectsV2Response{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-11 01:44:38 +08:00
										 |  |  | 	if fetchOwner { | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  | 		owner.ID = globalMinioDefaultOwnerID | 
					
						
							| 
									
										
										
										
											2016-09-11 01:44:38 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-06-01 13:10:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-17 13:17:00 +08:00
										 |  |  | 	for _, object := range objects { | 
					
						
							| 
									
										
										
										
											2016-06-01 13:10:55 +08:00
										 |  |  | 		var content = Object{} | 
					
						
							|  |  |  | 		if object.Name == "" { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | 		content.Key = s3EncodeName(object.Name, encodingType) | 
					
						
							| 
									
										
										
										
											2016-10-17 23:44:55 +08:00
										 |  |  | 		content.LastModified = object.ModTime.UTC().Format(timeFormatAMZLong) | 
					
						
							| 
									
										
										
										
											2017-05-15 03:05:51 +08:00
										 |  |  | 		if object.ETag != "" { | 
					
						
							|  |  |  | 			content.ETag = "\"" + object.ETag + "\"" | 
					
						
							| 
									
										
										
										
											2016-06-01 13:10:55 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		content.Size = object.Size | 
					
						
							| 
									
										
										
										
											2018-04-06 01:56:28 +08:00
										 |  |  | 		content.StorageClass = object.StorageClass | 
					
						
							| 
									
										
										
										
											2016-06-01 13:10:55 +08:00
										 |  |  | 		content.Owner = owner | 
					
						
							|  |  |  | 		contents = append(contents, content) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	data.Name = bucket | 
					
						
							|  |  |  | 	data.Contents = contents | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | 	data.EncodingType = encodingType | 
					
						
							| 
									
										
										
										
											2019-02-25 10:50:28 +08:00
										 |  |  | 	data.StartAfter = s3EncodeName(startAfter, encodingType) | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | 	data.Delimiter = s3EncodeName(delimiter, encodingType) | 
					
						
							|  |  |  | 	data.Prefix = s3EncodeName(prefix, encodingType) | 
					
						
							| 
									
										
										
										
											2016-06-01 13:10:55 +08:00
										 |  |  | 	data.MaxKeys = maxKeys | 
					
						
							|  |  |  | 	data.ContinuationToken = token | 
					
						
							| 
									
										
										
										
											2017-06-17 13:17:00 +08:00
										 |  |  | 	data.NextContinuationToken = nextToken | 
					
						
							|  |  |  | 	data.IsTruncated = isTruncated | 
					
						
							|  |  |  | 	for _, prefix := range prefixes { | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | 		var prefixItem = CommonPrefix{} | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | 		prefixItem.Prefix = s3EncodeName(prefix, encodingType) | 
					
						
							| 
									
										
										
										
											2017-06-17 13:17:00 +08:00
										 |  |  | 		commonPrefixes = append(commonPrefixes, prefixItem) | 
					
						
							| 
									
										
										
										
											2015-03-01 06:44:26 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-17 13:17:00 +08:00
										 |  |  | 	data.CommonPrefixes = commonPrefixes | 
					
						
							| 
									
										
										
										
											2016-10-06 11:12:47 +08:00
										 |  |  | 	data.KeyCount = len(data.Contents) + len(data.CommonPrefixes) | 
					
						
							| 
									
										
										
										
											2015-02-16 09:03:27 +08:00
										 |  |  | 	return data | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-04-23 07:28:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-22 05:51:05 +08:00
										 |  |  | // generates CopyObjectResponse from etag and lastModified time.
 | 
					
						
							| 
									
										
										
										
											2016-02-27 19:04:52 +08:00
										 |  |  | func generateCopyObjectResponse(etag string, lastModified time.Time) CopyObjectResponse { | 
					
						
							|  |  |  | 	return CopyObjectResponse{ | 
					
						
							| 
									
										
										
										
											2017-02-01 01:38:34 +08:00
										 |  |  | 		ETag:         "\"" + etag + "\"", | 
					
						
							|  |  |  | 		LastModified: lastModified.UTC().Format(timeFormatAMZLong), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // generates CopyObjectPartResponse from etag and lastModified time.
 | 
					
						
							|  |  |  | func generateCopyObjectPartResponse(etag string, lastModified time.Time) CopyObjectPartResponse { | 
					
						
							|  |  |  | 	return CopyObjectPartResponse{ | 
					
						
							| 
									
										
										
										
											2016-02-27 19:04:52 +08:00
										 |  |  | 		ETag:         "\"" + etag + "\"", | 
					
						
							| 
									
										
										
										
											2016-10-17 23:44:55 +08:00
										 |  |  | 		LastModified: lastModified.UTC().Format(timeFormatAMZLong), | 
					
						
							| 
									
										
										
										
											2016-02-27 19:04:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-22 05:51:05 +08:00
										 |  |  | // generates InitiateMultipartUploadResponse for given bucket, key and uploadID.
 | 
					
						
							| 
									
										
										
										
											2015-07-10 10:01:15 +08:00
										 |  |  | func generateInitiateMultipartUploadResponse(bucket, key, uploadID string) InitiateMultipartUploadResponse { | 
					
						
							|  |  |  | 	return InitiateMultipartUploadResponse{ | 
					
						
							| 
									
										
										
										
											2015-05-08 10:55:30 +08:00
										 |  |  | 		Bucket:   bucket, | 
					
						
							|  |  |  | 		Key:      key, | 
					
						
							|  |  |  | 		UploadID: uploadID, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-22 05:51:05 +08:00
										 |  |  | // generates CompleteMultipartUploadResponse for given bucket, key, location and ETag.
 | 
					
						
							| 
									
										
										
										
											2015-07-10 10:01:15 +08:00
										 |  |  | 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, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-22 05:51:05 +08:00
										 |  |  | // generates ListPartsResponse from ListPartsInfo.
 | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | func generateListPartsResponse(partsInfo ListPartsInfo, encodingType string) ListPartsResponse { | 
					
						
							| 
									
										
										
										
											2015-05-10 02:41:26 +08:00
										 |  |  | 	listPartsResponse := ListPartsResponse{} | 
					
						
							| 
									
										
										
											
												objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
											
										 
											2016-04-03 16:34:20 +08:00
										 |  |  | 	listPartsResponse.Bucket = partsInfo.Bucket | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | 	listPartsResponse.Key = s3EncodeName(partsInfo.Object, encodingType) | 
					
						
							| 
									
										
										
											
												objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
											
										 
											2016-04-03 16:34:20 +08:00
										 |  |  | 	listPartsResponse.UploadID = partsInfo.UploadID | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  | 	listPartsResponse.StorageClass = globalMinioDefaultStorageClass | 
					
						
							|  |  |  | 	listPartsResponse.Initiator.ID = globalMinioDefaultOwnerID | 
					
						
							|  |  |  | 	listPartsResponse.Owner.ID = globalMinioDefaultOwnerID | 
					
						
							| 
									
										
										
										
											2015-05-10 02:41:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
											
												objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
											
										 
											2016-04-03 16:34:20 +08:00
										 |  |  | 	listPartsResponse.MaxParts = partsInfo.MaxParts | 
					
						
							|  |  |  | 	listPartsResponse.PartNumberMarker = partsInfo.PartNumberMarker | 
					
						
							|  |  |  | 	listPartsResponse.IsTruncated = partsInfo.IsTruncated | 
					
						
							|  |  |  | 	listPartsResponse.NextPartNumberMarker = partsInfo.NextPartNumberMarker | 
					
						
							| 
									
										
										
										
											2015-05-10 02:41:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
											
												objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
											
										 
											2016-04-03 16:34:20 +08:00
										 |  |  | 	listPartsResponse.Parts = make([]Part, len(partsInfo.Parts)) | 
					
						
							|  |  |  | 	for index, part := range partsInfo.Parts { | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | 		newPart := Part{} | 
					
						
							| 
									
										
										
										
											2015-05-10 02:41:26 +08:00
										 |  |  | 		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 | 
					
						
							| 
									
										
										
										
											2016-10-17 23:44:55 +08:00
										 |  |  | 		newPart.LastModified = part.LastModified.UTC().Format(timeFormatAMZLong) | 
					
						
							| 
									
										
										
										
											2016-03-20 14:44:43 +08:00
										 |  |  | 		listPartsResponse.Parts[index] = newPart | 
					
						
							| 
									
										
										
										
											2015-05-10 02:41:26 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return listPartsResponse | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-22 05:51:05 +08:00
										 |  |  | // generates ListMultipartUploadsResponse for given bucket and ListMultipartsInfo.
 | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | func generateListMultipartUploadsResponse(bucket string, multipartsInfo ListMultipartsInfo, encodingType string) ListMultipartUploadsResponse { | 
					
						
							| 
									
										
										
										
											2015-05-15 05:36:41 +08:00
										 |  |  | 	listMultipartUploadsResponse := ListMultipartUploadsResponse{} | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.Bucket = bucket | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | 	listMultipartUploadsResponse.Delimiter = s3EncodeName(multipartsInfo.Delimiter, encodingType) | 
					
						
							| 
									
										
										
											
												objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
											
										 
											2016-04-03 16:34:20 +08:00
										 |  |  | 	listMultipartUploadsResponse.IsTruncated = multipartsInfo.IsTruncated | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | 	listMultipartUploadsResponse.EncodingType = encodingType | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.Prefix = s3EncodeName(multipartsInfo.Prefix, encodingType) | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.KeyMarker = s3EncodeName(multipartsInfo.KeyMarker, encodingType) | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.NextKeyMarker = s3EncodeName(multipartsInfo.NextKeyMarker, encodingType) | 
					
						
							| 
									
										
										
											
												objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
											
										 
											2016-04-03 16:34:20 +08:00
										 |  |  | 	listMultipartUploadsResponse.MaxUploads = multipartsInfo.MaxUploads | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.NextUploadIDMarker = multipartsInfo.NextUploadIDMarker | 
					
						
							|  |  |  | 	listMultipartUploadsResponse.UploadIDMarker = multipartsInfo.UploadIDMarker | 
					
						
							| 
									
										
										
										
											2016-04-06 06:08:59 +08:00
										 |  |  | 	listMultipartUploadsResponse.CommonPrefixes = make([]CommonPrefix, len(multipartsInfo.CommonPrefixes)) | 
					
						
							|  |  |  | 	for index, commonPrefix := range multipartsInfo.CommonPrefixes { | 
					
						
							|  |  |  | 		listMultipartUploadsResponse.CommonPrefixes[index] = CommonPrefix{ | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | 			Prefix: s3EncodeName(commonPrefix, encodingType), | 
					
						
							| 
									
										
										
										
											2016-04-06 06:08:59 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
											
												objectAPI: Fix object API interface, remove unnecessary structs.
ObjectAPI changes.
```
ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsInfo, *probe.Error)
ListMultipartUploads(bucket, objectPrefix, keyMarker, uploadIDMarker, delimiter string, maxUploads int) (ListMultipartsInfo, *probe.Error)
ListObjectParts(bucket, object, uploadID string, partNumberMarker, maxParts int) (ListPartsInfo, *probe.Error)
CompleteMultipartUpload(bucket string, object string, uploadID string, parts []completePart) (ObjectInfo, *probe.Error)
```
											
										 
											2016-04-03 16:34:20 +08:00
										 |  |  | 	listMultipartUploadsResponse.Uploads = make([]Upload, len(multipartsInfo.Uploads)) | 
					
						
							|  |  |  | 	for index, upload := range multipartsInfo.Uploads { | 
					
						
							| 
									
										
										
										
											2016-02-20 08:04:29 +08:00
										 |  |  | 		newUpload := Upload{} | 
					
						
							| 
									
										
										
										
											2015-05-15 05:36:41 +08:00
										 |  |  | 		newUpload.UploadID = upload.UploadID | 
					
						
							| 
									
										
										
										
											2019-02-24 14:14:24 +08:00
										 |  |  | 		newUpload.Key = s3EncodeName(upload.Object, encodingType) | 
					
						
							| 
									
										
										
										
											2016-10-17 23:44:55 +08:00
										 |  |  | 		newUpload.Initiated = upload.Initiated.UTC().Format(timeFormatAMZLong) | 
					
						
							| 
									
										
										
										
											2016-03-20 14:44:43 +08:00
										 |  |  | 		listMultipartUploadsResponse.Uploads[index] = newUpload | 
					
						
							| 
									
										
										
										
											2015-05-15 05:36:41 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return listMultipartUploadsResponse | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-06 08:43:48 +08:00
										 |  |  | // generate multi objects delete response.
 | 
					
						
							|  |  |  | func generateMultiDeleteResponse(quiet bool, deletedObjects []ObjectIdentifier, errs []DeleteError) DeleteObjectsResponse { | 
					
						
							|  |  |  | 	deleteResp := DeleteObjectsResponse{} | 
					
						
							|  |  |  | 	if !quiet { | 
					
						
							|  |  |  | 		deleteResp.DeletedObjects = deletedObjects | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	deleteResp.Errors = errs | 
					
						
							|  |  |  | 	return deleteResp | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-06 16:37:00 +08:00
										 |  |  | func writeResponse(w http.ResponseWriter, statusCode int, response []byte, mType mimeType) { | 
					
						
							| 
									
										
										
										
											2016-01-08 16:40:06 +08:00
										 |  |  | 	setCommonHeaders(w) | 
					
						
							| 
									
										
										
										
											2017-01-06 16:37:00 +08:00
										 |  |  | 	if mType != mimeNone { | 
					
						
							|  |  |  | 		w.Header().Set("Content-Type", string(mType)) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-19 05:39:56 +08:00
										 |  |  | 	w.WriteHeader(statusCode) | 
					
						
							| 
									
										
										
										
											2017-01-06 16:37:00 +08:00
										 |  |  | 	if response != nil { | 
					
						
							|  |  |  | 		w.Write(response) | 
					
						
							|  |  |  | 		w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2016-01-08 16:40:06 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-04-30 01:51:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-06 16:37:00 +08:00
										 |  |  | // mimeType represents various MIME type used API responses.
 | 
					
						
							|  |  |  | type mimeType string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	// Means no response type.
 | 
					
						
							|  |  |  | 	mimeNone mimeType = "" | 
					
						
							|  |  |  | 	// Means response type is JSON.
 | 
					
						
							|  |  |  | 	mimeJSON mimeType = "application/json" | 
					
						
							|  |  |  | 	// Means response type is XML.
 | 
					
						
							|  |  |  | 	mimeXML mimeType = "application/xml" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // writeSuccessResponseJSON writes success headers and response if any,
 | 
					
						
							|  |  |  | // with content-type set to `application/json`.
 | 
					
						
							|  |  |  | func writeSuccessResponseJSON(w http.ResponseWriter, response []byte) { | 
					
						
							|  |  |  | 	writeResponse(w, http.StatusOK, response, mimeJSON) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // writeSuccessResponseXML writes success headers and response if any,
 | 
					
						
							|  |  |  | // with content-type set to `application/xml`.
 | 
					
						
							|  |  |  | func writeSuccessResponseXML(w http.ResponseWriter, response []byte) { | 
					
						
							|  |  |  | 	writeResponse(w, http.StatusOK, response, mimeXML) | 
					
						
							| 
									
										
										
										
											2016-12-19 05:39:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | // writeSuccessNoContent writes success headers with http status 204
 | 
					
						
							| 
									
										
										
										
											2015-10-17 11:02:37 +08:00
										 |  |  | func writeSuccessNoContent(w http.ResponseWriter) { | 
					
						
							| 
									
										
										
										
											2017-01-06 16:37:00 +08:00
										 |  |  | 	writeResponse(w, http.StatusNoContent, nil, mimeNone) | 
					
						
							| 
									
										
										
										
											2015-10-17 11:02:37 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | // writeRedirectSeeOther writes Location header with http status 303
 | 
					
						
							|  |  |  | func writeRedirectSeeOther(w http.ResponseWriter, location string) { | 
					
						
							|  |  |  | 	w.Header().Set("Location", location) | 
					
						
							| 
									
										
										
										
											2017-01-06 16:37:00 +08:00
										 |  |  | 	writeResponse(w, http.StatusSeeOther, nil, mimeNone) | 
					
						
							| 
									
										
										
										
											2016-12-21 01:32:17 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-06 16:37:00 +08:00
										 |  |  | func writeSuccessResponseHeadersOnly(w http.ResponseWriter) { | 
					
						
							|  |  |  | 	writeResponse(w, http.StatusOK, nil, mimeNone) | 
					
						
							| 
									
										
										
										
											2016-06-16 11:31:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-06 16:37:00 +08:00
										 |  |  | // writeErrorRespone writes error headers
 | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | func writeErrorResponse(ctx context.Context, w http.ResponseWriter, err APIError, reqURL *url.URL, browser bool) { | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	switch err.Code { | 
					
						
							|  |  |  | 	case "SlowDown", "XMinioServerNotInitialized", "XMinioReadQuorum", "XMinioWriteQuorum": | 
					
						
							| 
									
										
										
										
											2017-12-20 16:00:14 +08:00
										 |  |  | 		// Set retry-after header to indicate user-agents to retry request after 120secs.
 | 
					
						
							|  |  |  | 		// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
 | 
					
						
							|  |  |  | 		w.Header().Set("Retry-After", "120") | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	case "AccessDenied": | 
					
						
							| 
									
										
										
										
											2018-12-04 15:38:24 +08:00
										 |  |  | 		// The request is from browser and also if browser
 | 
					
						
							|  |  |  | 		// is enabled we need to redirect.
 | 
					
						
							| 
									
										
										
										
											2019-04-04 14:10:37 +08:00
										 |  |  | 		if browser { | 
					
						
							| 
									
										
										
										
											2018-11-27 04:15:12 +08:00
										 |  |  | 			w.Header().Set("Location", minioReservedBucketPath+reqURL.Path) | 
					
						
							|  |  |  | 			w.WriteHeader(http.StatusTemporaryRedirect) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-12-20 16:00:14 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-11-27 04:15:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-29 05:51:49 +08:00
										 |  |  | 	// Generate error response.
 | 
					
						
							| 
									
										
										
										
											2019-02-21 14:20:15 +08:00
										 |  |  | 	errorResponse := getAPIErrorResponse(ctx, err, reqURL.Path, | 
					
						
							|  |  |  | 		w.Header().Get(responseRequestIDKey), w.Header().Get(responseDeploymentIDKey)) | 
					
						
							| 
									
										
										
										
											2016-06-16 11:31:06 +08:00
										 |  |  | 	encodedErrorResponse := encodeResponse(errorResponse) | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	writeResponse(w, err.HTTPStatusCode, encodedErrorResponse, mimeXML) | 
					
						
							| 
									
										
										
										
											2017-01-06 16:37:00 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | func writeErrorResponseHeadersOnly(w http.ResponseWriter, err APIError) { | 
					
						
							|  |  |  | 	writeResponse(w, err.HTTPStatusCode, nil, mimeNone) | 
					
						
							| 
									
										
										
										
											2015-04-23 07:28:13 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // writeErrorResponseJSON - writes error response in JSON format;
 | 
					
						
							|  |  |  | // useful for admin APIs.
 | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | func writeErrorResponseJSON(ctx context.Context, w http.ResponseWriter, err APIError, reqURL *url.URL) { | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	// Generate error response.
 | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 	errorResponse := getAPIErrorResponse(ctx, err, reqURL.Path, w.Header().Get(responseRequestIDKey), w.Header().Get(responseDeploymentIDKey)) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	encodedErrorResponse := encodeResponseJSON(errorResponse) | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	writeResponse(w, err.HTTPStatusCode, encodedErrorResponse, mimeJSON) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // writeCustomErrorResponseJSON - similar to writeErrorResponseJSON,
 | 
					
						
							|  |  |  | // but accepts the error message directly (this allows messages to be
 | 
					
						
							|  |  |  | // dynamically generated.)
 | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | func writeCustomErrorResponseJSON(ctx context.Context, w http.ResponseWriter, err APIError, | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	errBody string, reqURL *url.URL) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 	reqInfo := logger.GetReqInfo(ctx) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	errorResponse := APIErrorResponse{ | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		Code:       err.Code, | 
					
						
							|  |  |  | 		Message:    errBody, | 
					
						
							|  |  |  | 		Resource:   reqURL.Path, | 
					
						
							|  |  |  | 		BucketName: reqInfo.BucketName, | 
					
						
							|  |  |  | 		Key:        reqInfo.ObjectName, | 
					
						
							|  |  |  | 		RequestID:  w.Header().Get(responseRequestIDKey), | 
					
						
							|  |  |  | 		HostID:     w.Header().Get(responseDeploymentIDKey), | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	encodedErrorResponse := encodeResponseJSON(errorResponse) | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	writeResponse(w, err.HTTPStatusCode, encodedErrorResponse, mimeJSON) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-03-06 04:10:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // writeCustomErrorResponseXML - similar to writeErrorResponse,
 | 
					
						
							|  |  |  | // but accepts the error message directly (this allows messages to be
 | 
					
						
							|  |  |  | // dynamically generated.)
 | 
					
						
							|  |  |  | func writeCustomErrorResponseXML(ctx context.Context, w http.ResponseWriter, err APIError, errBody string, reqURL *url.URL, browser bool) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch err.Code { | 
					
						
							|  |  |  | 	case "SlowDown", "XMinioServerNotInitialized", "XMinioReadQuorum", "XMinioWriteQuorum": | 
					
						
							|  |  |  | 		// Set retry-after header to indicate user-agents to retry request after 120secs.
 | 
					
						
							|  |  |  | 		// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
 | 
					
						
							|  |  |  | 		w.Header().Set("Retry-After", "120") | 
					
						
							|  |  |  | 	case "AccessDenied": | 
					
						
							|  |  |  | 		// The request is from browser and also if browser
 | 
					
						
							|  |  |  | 		// is enabled we need to redirect.
 | 
					
						
							|  |  |  | 		if browser && globalIsBrowserEnabled { | 
					
						
							|  |  |  | 			w.Header().Set("Location", minioReservedBucketPath+reqURL.Path) | 
					
						
							|  |  |  | 			w.WriteHeader(http.StatusTemporaryRedirect) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	reqInfo := logger.GetReqInfo(ctx) | 
					
						
							|  |  |  | 	errorResponse := APIErrorResponse{ | 
					
						
							|  |  |  | 		Code:       err.Code, | 
					
						
							|  |  |  | 		Message:    errBody, | 
					
						
							|  |  |  | 		Resource:   reqURL.Path, | 
					
						
							|  |  |  | 		BucketName: reqInfo.BucketName, | 
					
						
							|  |  |  | 		Key:        reqInfo.ObjectName, | 
					
						
							|  |  |  | 		RequestID:  w.Header().Get(responseRequestIDKey), | 
					
						
							|  |  |  | 		HostID:     w.Header().Get(responseDeploymentIDKey), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	encodedErrorResponse := encodeResponse(errorResponse) | 
					
						
							|  |  |  | 	writeResponse(w, err.HTTPStatusCode, encodedErrorResponse, mimeXML) | 
					
						
							|  |  |  | } |