mirror of
https://github.com/minio/mc.git
synced 2025-11-12 01:02:26 +03:00
Full cleanup of s3 client for mc
This commit is contained in:
@@ -17,13 +17,7 @@
|
||||
package s3
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"net/http"
|
||||
|
||||
@@ -38,8 +32,6 @@ func isValidBucketACL(acl string) bool {
|
||||
case "public-read":
|
||||
fallthrough
|
||||
case "public-read-write":
|
||||
fallthrough
|
||||
case "":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@@ -48,99 +40,27 @@ func isValidBucketACL(acl string) bool {
|
||||
|
||||
/// Bucket API operations
|
||||
|
||||
// Get list of buckets
|
||||
func (c *s3Client) listBucketsInternal() ([]*client.Item, error) {
|
||||
var res *http.Response
|
||||
var err error
|
||||
|
||||
u := fmt.Sprintf("%s://%s/", c.Scheme, c.Host)
|
||||
req, err := c.newRequest("GET", u, nil)
|
||||
if err != nil {
|
||||
return nil, iodine.New(err, nil)
|
||||
}
|
||||
|
||||
// do not ignore signatures for 'listBuckets()'
|
||||
// it is never a public request for amazon s3
|
||||
// so lets aggressively verify
|
||||
if strings.Contains(c.Host, "amazonaws.com") && (c.AccessKeyID == "" || c.SecretAccessKey == "") {
|
||||
msg := "Authorization key cannot be empty for listing buckets, please choose a valid bucketname if its a public request"
|
||||
return nil, iodine.New(errors.New(msg), nil)
|
||||
}
|
||||
if c.AccessKeyID != "" && c.SecretAccessKey != "" {
|
||||
c.signRequest(req, c.Host)
|
||||
}
|
||||
|
||||
res, err = c.Transport.RoundTrip(req)
|
||||
if err != nil {
|
||||
return nil, iodine.New(err, nil)
|
||||
}
|
||||
if res != nil {
|
||||
if res.StatusCode != http.StatusOK {
|
||||
err = NewError(res)
|
||||
return nil, iodine.New(err, nil)
|
||||
}
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
type bucket struct {
|
||||
Name string
|
||||
CreationDate time.Time
|
||||
}
|
||||
type allMyBuckets struct {
|
||||
Buckets struct {
|
||||
Bucket []*bucket
|
||||
}
|
||||
}
|
||||
var buckets allMyBuckets
|
||||
if err := xml.NewDecoder(res.Body).Decode(&buckets); err != nil {
|
||||
return nil, iodine.New(client.UnexpectedError{
|
||||
Err: errors.New("Malformed response received from server")},
|
||||
map[string]string{"XMLError": err.Error()})
|
||||
}
|
||||
var items []*client.Item
|
||||
for _, b := range buckets.Buckets.Bucket {
|
||||
item := new(client.Item)
|
||||
item.Name = b.Name
|
||||
item.Time = b.CreationDate
|
||||
item.FileType = os.ModeDir
|
||||
items = append(items, item)
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// PutBucket - create new bucket
|
||||
func (c *s3Client) PutBucket(acl string) error {
|
||||
if !isValidBucketACL(acl) {
|
||||
return iodine.New(InvalidACL{ACL: acl}, nil)
|
||||
}
|
||||
bucket, _ := c.url2BucketAndObject()
|
||||
if !client.IsValidBucketName(bucket) || strings.Contains(bucket, ".") {
|
||||
// PutBucket - create a new bucket
|
||||
func (c *s3Client) PutBucket() error {
|
||||
bucket, object := c.url2BucketAndObject()
|
||||
if !client.IsValidBucketName(bucket) {
|
||||
return iodine.New(InvalidBucketName{Bucket: bucket}, nil)
|
||||
}
|
||||
var req *http.Request
|
||||
var err error
|
||||
switch len(acl) > 0 {
|
||||
case true:
|
||||
u := fmt.Sprintf("%s://%s/%s?acl", c.Scheme, c.Host, bucket)
|
||||
// new request
|
||||
req, err = c.newRequest("PUT", u, nil)
|
||||
if err != nil {
|
||||
return iodine.New(err, nil)
|
||||
}
|
||||
// by default without acl while creating a bucket
|
||||
// make it default "private"
|
||||
req.Header.Add("x-amz-acl", acl)
|
||||
default:
|
||||
u := fmt.Sprintf("%s://%s/%s", c.Scheme, c.Host, bucket)
|
||||
// new request
|
||||
req, err = c.newRequest("PUT", u, nil)
|
||||
if err != nil {
|
||||
return iodine.New(err, nil)
|
||||
}
|
||||
// by default without acl while creating a bucket
|
||||
// make it default "private"
|
||||
req.Header.Add("x-amz-acl", "private")
|
||||
if object != "" {
|
||||
return iodine.New(InvalidQueryURL{URL: ""}, nil)
|
||||
}
|
||||
requestURL, err := c.getRequestURL()
|
||||
if err != nil {
|
||||
return iodine.New(err, nil)
|
||||
}
|
||||
// new request
|
||||
req, err := c.newRequest("PUT", requestURL, nil)
|
||||
if err != nil {
|
||||
return iodine.New(err, nil)
|
||||
}
|
||||
// by default while creating a bucket make it default "private"
|
||||
req.Header.Add("x-amz-acl", "private")
|
||||
|
||||
if c.AccessKeyID != "" && c.SecretAccessKey != "" {
|
||||
c.signRequest(req, c.Host)
|
||||
}
|
||||
@@ -157,87 +77,52 @@ func (c *s3Client) PutBucket(acl string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *s3Client) getBucketMetadata(bucket string) (item *client.Item, err error) {
|
||||
if !client.IsValidBucketName(bucket) || strings.Contains(bucket, ".") {
|
||||
return nil, iodine.New(InvalidBucketName{Bucket: bucket}, nil)
|
||||
func (c *s3Client) PutBucketACL(acl string) error {
|
||||
if !isValidBucketACL(acl) {
|
||||
return iodine.New(InvalidACL{ACL: acl}, nil)
|
||||
}
|
||||
u := fmt.Sprintf("%s://%s/%s", c.Scheme, c.Host, bucket)
|
||||
req, err := c.newRequest("HEAD", u, nil)
|
||||
if err != nil {
|
||||
return nil, iodine.New(err, nil)
|
||||
}
|
||||
if c.AccessKeyID != "" && c.SecretAccessKey != "" {
|
||||
c.signRequest(req, c.Host)
|
||||
}
|
||||
res, err := c.Transport.RoundTrip(req)
|
||||
if err != nil {
|
||||
return nil, iodine.New(err, nil)
|
||||
}
|
||||
item = new(client.Item)
|
||||
item.Name = bucket
|
||||
item.FileType = os.ModeDir
|
||||
|
||||
defer res.Body.Close()
|
||||
switch res.StatusCode {
|
||||
case http.StatusOK:
|
||||
fallthrough
|
||||
case http.StatusMovedPermanently:
|
||||
return item, nil
|
||||
default:
|
||||
return nil, iodine.New(NewError(res), nil)
|
||||
}
|
||||
}
|
||||
|
||||
// getObjectMetadata - returns nil, os.ErrNotExist if not on object storage
|
||||
func (c *s3Client) getObjectMetadata(bucket, object string) (item *client.Item, err error) {
|
||||
if !client.IsValidBucketName(bucket) || strings.Contains(bucket, ".") {
|
||||
return nil, iodine.New(InvalidBucketName{Bucket: bucket}, nil)
|
||||
}
|
||||
queryURL := c.objectURL(bucket, object)
|
||||
if !c.isValidQueryURL(queryURL) {
|
||||
return nil, iodine.New(InvalidQueryURL{URL: queryURL}, nil)
|
||||
}
|
||||
req, err := c.newRequest("HEAD", queryURL, nil)
|
||||
if err != nil {
|
||||
return nil, iodine.New(err, nil)
|
||||
}
|
||||
if c.AccessKeyID != "" && c.SecretAccessKey != "" {
|
||||
c.signRequest(req, c.Host)
|
||||
}
|
||||
res, err := c.Transport.RoundTrip(req)
|
||||
if err != nil {
|
||||
return nil, iodine.New(err, nil)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
switch res.StatusCode {
|
||||
case http.StatusNotFound:
|
||||
return nil, iodine.New(ObjectNotFound{Bucket: bucket, Object: object}, nil)
|
||||
case http.StatusOK:
|
||||
contentLength, err := strconv.ParseInt(res.Header.Get("Content-Length"), 10, 64)
|
||||
if err != nil {
|
||||
return nil, iodine.New(err, nil)
|
||||
}
|
||||
date, err := time.Parse(time.RFC1123, res.Header.Get("Last-Modified"))
|
||||
// AWS S3 uses RFC1123 standard for Date in HTTP header, unlike XML content
|
||||
if err != nil {
|
||||
return nil, iodine.New(err, nil)
|
||||
}
|
||||
item = new(client.Item)
|
||||
item.Name = object
|
||||
item.Time = date
|
||||
item.Size = contentLength
|
||||
item.FileType = 0
|
||||
return item, nil
|
||||
default:
|
||||
return nil, iodine.New(NewError(res), nil)
|
||||
}
|
||||
}
|
||||
|
||||
// Stat - send a 'HEAD' on a bucket or object to see if exists
|
||||
func (c *s3Client) Stat() (*client.Item, error) {
|
||||
bucket, object := c.url2BucketAndObject()
|
||||
if object == "" {
|
||||
return c.getBucketMetadata(bucket)
|
||||
if !client.IsValidBucketName(bucket) {
|
||||
return iodine.New(InvalidBucketName{Bucket: bucket}, nil)
|
||||
}
|
||||
return c.getObjectMetadata(bucket, object)
|
||||
if object != "" {
|
||||
return iodine.New(InvalidQueryURL{URL: ""}, nil)
|
||||
}
|
||||
requestURL, err := c.getRequestURL()
|
||||
if err != nil {
|
||||
return iodine.New(err, nil)
|
||||
}
|
||||
|
||||
// new request
|
||||
u := fmt.Sprintf("%s?acl", requestURL)
|
||||
req, err := c.newRequest("PUT", u, nil)
|
||||
if err != nil {
|
||||
return iodine.New(err, nil)
|
||||
}
|
||||
|
||||
// by default without acl while creating a bucket
|
||||
// make it default "private"
|
||||
req.Header.Add("x-amz-acl", acl)
|
||||
|
||||
if c.AccessKeyID != "" && c.SecretAccessKey != "" {
|
||||
c.signRequest(req, c.Host)
|
||||
}
|
||||
|
||||
res, err := c.Transport.RoundTrip(req)
|
||||
if err != nil {
|
||||
return iodine.New(err, nil)
|
||||
}
|
||||
if res != nil {
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return iodine.New(NewError(res), nil)
|
||||
}
|
||||
}
|
||||
defer res.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stat - send a 'HEAD' on a bucket or object to get its metadata
|
||||
func (c *s3Client) Stat() (*client.Content, error) {
|
||||
bucket, object := c.url2BucketAndObject()
|
||||
return c.getMetadata(bucket, object)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user