|
|
@ -25,7 +25,8 @@ import ( |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
type Storage struct { |
|
|
|
type Storage struct { |
|
|
|
data map[string]storedObject |
|
|
|
bucketdata map[string]storedBucket |
|
|
|
|
|
|
|
objectdata map[string]storedObject |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type storedObject struct { |
|
|
|
type storedObject struct { |
|
|
@ -33,17 +34,64 @@ type storedObject struct { |
|
|
|
data []byte |
|
|
|
data []byte |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type storedBucket struct { |
|
|
|
|
|
|
|
metadata BucketMetadata |
|
|
|
|
|
|
|
// owner string // TODO
|
|
|
|
|
|
|
|
// id string // TODO
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type BucketMetadata struct { |
|
|
|
|
|
|
|
Name string |
|
|
|
|
|
|
|
Created int64 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
type ObjectMetadata struct { |
|
|
|
type ObjectMetadata struct { |
|
|
|
Key string |
|
|
|
Key string |
|
|
|
SecCreated int64 |
|
|
|
SecCreated int64 |
|
|
|
Size int |
|
|
|
Size int |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func isValidBucket(bucket string) bool { |
|
|
|
|
|
|
|
l := len(bucket) |
|
|
|
|
|
|
|
if l < 3 || l > 63 { |
|
|
|
|
|
|
|
return false |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
valid := false |
|
|
|
|
|
|
|
prev := byte('.') |
|
|
|
|
|
|
|
for i := 0; i < len(bucket); i++ { |
|
|
|
|
|
|
|
c := bucket[i] |
|
|
|
|
|
|
|
switch { |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
return false |
|
|
|
|
|
|
|
case 'a' <= c && c <= 'z': |
|
|
|
|
|
|
|
valid = true |
|
|
|
|
|
|
|
case '0' <= c && c <= '9': |
|
|
|
|
|
|
|
// Is allowed, but bucketname can't be just numbers.
|
|
|
|
|
|
|
|
// Therefore, don't set valid to true
|
|
|
|
|
|
|
|
case c == '-': |
|
|
|
|
|
|
|
if prev == '.' { |
|
|
|
|
|
|
|
return false |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
case c == '.': |
|
|
|
|
|
|
|
if prev == '.' || prev == '-' { |
|
|
|
|
|
|
|
return false |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
prev = c |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if prev == '-' || prev == '.' { |
|
|
|
|
|
|
|
return false |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return valid |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (storage *Storage) CopyObjectToWriter(w io.Writer, bucket string, object string) (int64, error) { |
|
|
|
func (storage *Storage) CopyObjectToWriter(w io.Writer, bucket string, object string) (int64, error) { |
|
|
|
// TODO synchronize access
|
|
|
|
// TODO synchronize access
|
|
|
|
// get object
|
|
|
|
// get object
|
|
|
|
key := bucket + ":" + object |
|
|
|
key := bucket + ":" + object |
|
|
|
if val, ok := storage.data[key]; ok { |
|
|
|
if val, ok := storage.objectdata[key]; ok { |
|
|
|
objectBuffer := bytes.NewBuffer(val.data) |
|
|
|
objectBuffer := bytes.NewBuffer(val.data) |
|
|
|
written, err := io.Copy(w, objectBuffer) |
|
|
|
written, err := io.Copy(w, objectBuffer) |
|
|
|
return written, err |
|
|
|
return written, err |
|
|
@ -54,7 +102,7 @@ func (storage *Storage) CopyObjectToWriter(w io.Writer, bucket string, object st |
|
|
|
|
|
|
|
|
|
|
|
func (storage *Storage) StoreObject(bucket string, key string, data io.Reader) error { |
|
|
|
func (storage *Storage) StoreObject(bucket string, key string, data io.Reader) error { |
|
|
|
objectKey := bucket + ":" + key |
|
|
|
objectKey := bucket + ":" + key |
|
|
|
if _, ok := storage.data[objectKey]; ok == true { |
|
|
|
if _, ok := storage.objectdata[objectKey]; ok == true { |
|
|
|
return ObjectExists{bucket: bucket, key: key} |
|
|
|
return ObjectExists{bucket: bucket, key: key} |
|
|
|
} |
|
|
|
} |
|
|
|
var bytesBuffer bytes.Buffer |
|
|
|
var bytesBuffer bytes.Buffer |
|
|
@ -67,14 +115,32 @@ func (storage *Storage) StoreObject(bucket string, key string, data io.Reader) e |
|
|
|
} |
|
|
|
} |
|
|
|
newObject.data = bytesBuffer.Bytes() |
|
|
|
newObject.data = bytesBuffer.Bytes() |
|
|
|
} |
|
|
|
} |
|
|
|
storage.data[objectKey] = newObject |
|
|
|
storage.objectdata[objectKey] = newObject |
|
|
|
|
|
|
|
return nil |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (storage *Storage) StoreBucket(bucketName string) error { |
|
|
|
|
|
|
|
if !isValidBucket(bucketName) { |
|
|
|
|
|
|
|
return BucketNameInvalid{bucket: bucketName} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if _, ok := storage.bucketdata[bucketName]; ok == true { |
|
|
|
|
|
|
|
return BucketExists{bucket: bucketName} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
newBucket := storedBucket{} |
|
|
|
|
|
|
|
newBucket.metadata = BucketMetadata{ |
|
|
|
|
|
|
|
Name: bucketName, |
|
|
|
|
|
|
|
Created: time.Now().Unix(), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
log.Println(bucketName) |
|
|
|
|
|
|
|
storage.bucketdata[bucketName] = newBucket |
|
|
|
return nil |
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (storage *Storage) ListObjects(bucket, prefix string, count int) []ObjectMetadata { |
|
|
|
func (storage *Storage) ListObjects(bucket, prefix string, count int) []ObjectMetadata { |
|
|
|
|
|
|
|
// TODO prefix and count handling
|
|
|
|
var results []ObjectMetadata |
|
|
|
var results []ObjectMetadata |
|
|
|
for key, object := range storage.data { |
|
|
|
for key, object := range storage.objectdata { |
|
|
|
log.Println(key) |
|
|
|
|
|
|
|
if strings.HasPrefix(key, bucket+":") { |
|
|
|
if strings.HasPrefix(key, bucket+":") { |
|
|
|
results = append(results, object.metadata) |
|
|
|
results = append(results, object.metadata) |
|
|
|
} |
|
|
|
} |
|
|
@ -82,12 +148,22 @@ func (storage *Storage) ListObjects(bucket, prefix string, count int) []ObjectMe |
|
|
|
return results |
|
|
|
return results |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (storage *Storage) ListBuckets(prefix string) []BucketMetadata { |
|
|
|
|
|
|
|
// TODO prefix handling
|
|
|
|
|
|
|
|
var results []BucketMetadata |
|
|
|
|
|
|
|
for _, bucket := range storage.bucketdata { |
|
|
|
|
|
|
|
results = append(results, bucket.metadata) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return results |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func Start() (chan<- string, <-chan error, *Storage) { |
|
|
|
func Start() (chan<- string, <-chan error, *Storage) { |
|
|
|
ctrlChannel := make(chan string) |
|
|
|
ctrlChannel := make(chan string) |
|
|
|
errorChannel := make(chan error) |
|
|
|
errorChannel := make(chan error) |
|
|
|
go start(ctrlChannel, errorChannel) |
|
|
|
go start(ctrlChannel, errorChannel) |
|
|
|
return ctrlChannel, errorChannel, &Storage{ |
|
|
|
return ctrlChannel, errorChannel, &Storage{ |
|
|
|
data: make(map[string]storedObject), |
|
|
|
bucketdata: make(map[string]storedBucket), |
|
|
|
|
|
|
|
objectdata: make(map[string]storedObject), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|