1
0
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:
Harshavardhana
2015-05-01 15:52:08 -07:00
parent 28315e7492
commit 78a78c072a
23 changed files with 1008 additions and 875 deletions

View File

@@ -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)
}