| 
									
										
										
										
											2016-01-26 09:29:20 +08:00
										 |  |  | /* | 
					
						
							|  |  |  |  * Minio Cloud Storage, (C) 2016 Minio, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * 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-01-24 11:44:32 +08:00
										 |  |  | package main | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2016-02-04 14:46:45 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2016-02-22 14:38:38 +08:00
										 |  |  | 	"path" | 
					
						
							| 
									
										
										
										
											2016-02-07 19:37:54 +08:00
										 |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2016-02-04 14:46:45 +08:00
										 |  |  | 	"runtime" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-25 14:26:53 +08:00
										 |  |  | 	jwtgo "github.com/dgrijalva/jwt-go" | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:20 +08:00
										 |  |  | 	jwtreq "github.com/dgrijalva/jwt-go/request" | 
					
						
							| 
									
										
										
										
											2016-02-04 14:46:45 +08:00
										 |  |  | 	"github.com/dustin/go-humanize" | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 	"github.com/gorilla/mux" | 
					
						
							| 
									
										
										
										
											2016-02-13 10:55:17 +08:00
										 |  |  | 	"github.com/gorilla/rpc/v2/json2" | 
					
						
							| 
									
										
										
										
											2016-02-24 05:05:47 +08:00
										 |  |  | 	"github.com/minio/miniobrowser" | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-18 18:13:52 +08:00
										 |  |  | // isJWTReqAuthenticated validates if any incoming request to be a
 | 
					
						
							|  |  |  | // valid JWT authenticated request.
 | 
					
						
							|  |  |  | func isJWTReqAuthenticated(req *http.Request) bool { | 
					
						
							| 
									
										
										
										
											2016-07-12 12:57:40 +08:00
										 |  |  | 	jwt, err := newJWT() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		errorIf(err, "unable to initialize a new JWT") | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:20 +08:00
										 |  |  | 	var reqCallback jwtgo.Keyfunc | 
					
						
							|  |  |  | 	reqCallback = func(token *jwtgo.Token) (interface{}, error) { | 
					
						
							| 
									
										
										
										
											2016-01-27 17:52:54 +08:00
										 |  |  | 		if _, ok := token.Method.(*jwtgo.SigningMethodHMAC); !ok { | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | 			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
											  
											
												config/main: Re-write config files - add to new config v3
- New config format.
```
{
	"version": "3",
	"address": ":9000",
    "backend": {
          "type": "fs",
          "disk": "/path"
    },
	"credential": {
		"accessKey": "WLGDGYAQYIGI833EV05A",
		"secretKey": "BYvgJM101sHngl2uzjXS/OBF/aMxAN06JrJ3qJlF"
	},
	"region": "us-east-1",
	"logger": {
		"file": {
			"enable": false,
			"fileName": "",
			"level": "error"
		},
		"syslog": {
			"enable": false,
			"address": "",
			"level": "debug"
		},
		"console": {
			"enable": true,
			"level": "fatal"
		}
	}
}
```
New command lines in lieu of supporting XL.
Minio initialize filesystem backend.
~~~
$ minio init fs <path>
~~~
Minio initialize XL backend.
~~~
$ minio init xl <url1>...<url16>
~~~
For 'fs' backend it starts the server.
~~~
$ minio server
~~~
For 'xl' backend it waits for servers to join.
~~~
$ minio server
... [PROGRESS BAR] of servers connecting
~~~
Now on other servers execute 'join' and they connect.
~~~
....
minio join <url1> -- from <url2> && minio server
minio join <url1> -- from <url3> && minio server
...
...
minio join <url1> -- from <url16> && minio server
~~~
											
										 
											2016-02-13 07:27:10 +08:00
										 |  |  | 		return []byte(jwt.SecretAccessKey), nil | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:20 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	token, err := jwtreq.ParseFromRequest(req, jwtreq.AuthorizationHeaderExtractor, reqCallback) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		errorIf(err, "token parsing failed") | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-18 18:13:52 +08:00
										 |  |  | 	return token.Valid | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-08 14:44:08 +08:00
										 |  |  | // WebGenericArgs - empty struct for calls that don't accept arguments
 | 
					
						
							| 
									
										
										
										
											2016-03-22 02:15:08 +08:00
										 |  |  | // for ex. ServerInfo, GenerateAuth
 | 
					
						
							| 
									
										
										
										
											2016-04-08 14:44:08 +08:00
										 |  |  | type WebGenericArgs struct{} | 
					
						
							| 
									
										
										
										
											2016-03-22 02:15:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-08 14:44:08 +08:00
										 |  |  | // WebGenericRep - reply structure for calls for which reply is success/failure
 | 
					
						
							| 
									
										
										
										
											2016-02-19 16:00:32 +08:00
										 |  |  | // for ex. RemoveObject MakeBucket
 | 
					
						
							| 
									
										
										
										
											2016-04-08 14:44:08 +08:00
										 |  |  | type WebGenericRep struct { | 
					
						
							| 
									
										
										
										
											2016-02-19 16:00:32 +08:00
										 |  |  | 	UIVersion string `json:"uiVersion"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ServerInfoRep - server info reply.
 | 
					
						
							|  |  |  | type ServerInfoRep struct { | 
					
						
							|  |  |  | 	MinioVersion  string | 
					
						
							|  |  |  | 	MinioMemory   string | 
					
						
							|  |  |  | 	MinioPlatform string | 
					
						
							|  |  |  | 	MinioRuntime  string | 
					
						
							| 
									
										
										
										
											2016-08-04 04:47:03 +08:00
										 |  |  | 	MinioEnvVars  []string | 
					
						
							| 
									
										
										
										
											2016-02-19 16:00:32 +08:00
										 |  |  | 	UIVersion     string `json:"uiVersion"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-04 14:46:45 +08:00
										 |  |  | // ServerInfo - get server info.
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:45:15 +08:00
										 |  |  | func (web *webAPIHandlers) ServerInfo(r *http.Request, args *WebGenericArgs, reply *ServerInfoRep) error { | 
					
						
							| 
									
										
										
										
											2016-02-18 18:13:52 +08:00
										 |  |  | 	if !isJWTReqAuthenticated(r) { | 
					
						
							| 
									
										
										
										
											2016-02-13 10:55:17 +08:00
										 |  |  | 		return &json2.Error{Message: "Unauthorized request"} | 
					
						
							| 
									
										
										
										
											2016-02-04 14:46:45 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	host, err := os.Hostname() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		host = "" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	memstats := &runtime.MemStats{} | 
					
						
							|  |  |  | 	runtime.ReadMemStats(memstats) | 
					
						
							|  |  |  | 	mem := fmt.Sprintf("Used: %s | Allocated: %s | Used-Heap: %s | Allocated-Heap: %s", | 
					
						
							|  |  |  | 		humanize.Bytes(memstats.Alloc), | 
					
						
							|  |  |  | 		humanize.Bytes(memstats.TotalAlloc), | 
					
						
							|  |  |  | 		humanize.Bytes(memstats.HeapAlloc), | 
					
						
							|  |  |  | 		humanize.Bytes(memstats.HeapSys)) | 
					
						
							|  |  |  | 	platform := fmt.Sprintf("Host: %s | OS: %s | Arch: %s", | 
					
						
							|  |  |  | 		host, | 
					
						
							|  |  |  | 		runtime.GOOS, | 
					
						
							|  |  |  | 		runtime.GOARCH) | 
					
						
							|  |  |  | 	goruntime := fmt.Sprintf("Version: %s | CPUs: %s", runtime.Version(), strconv.Itoa(runtime.NumCPU())) | 
					
						
							| 
									
										
										
										
											2016-08-04 04:47:03 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	reply.MinioEnvVars = os.Environ() | 
					
						
							| 
									
										
										
										
											2016-02-09 04:40:22 +08:00
										 |  |  | 	reply.MinioVersion = minioVersion | 
					
						
							|  |  |  | 	reply.MinioMemory = mem | 
					
						
							|  |  |  | 	reply.MinioPlatform = platform | 
					
						
							|  |  |  | 	reply.MinioRuntime = goruntime | 
					
						
							| 
									
										
										
										
											2016-02-24 05:05:47 +08:00
										 |  |  | 	reply.UIVersion = miniobrowser.UIVersion | 
					
						
							| 
									
										
										
										
											2016-02-04 14:46:45 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-27 05:13:10 +08:00
										 |  |  | // StorageInfoRep - contains storage usage statistics.
 | 
					
						
							|  |  |  | type StorageInfoRep struct { | 
					
						
							|  |  |  | 	StorageInfo StorageInfo `json:"storageInfo"` | 
					
						
							|  |  |  | 	UIVersion   string      `json:"uiVersion"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // StorageInfo - web call to gather storage usage statistics.
 | 
					
						
							|  |  |  | func (web *webAPIHandlers) StorageInfo(r *http.Request, args *GenericArgs, reply *StorageInfoRep) error { | 
					
						
							|  |  |  | 	if !isJWTReqAuthenticated(r) { | 
					
						
							|  |  |  | 		return &json2.Error{Message: "Unauthorized request"} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	reply.UIVersion = miniobrowser.UIVersion | 
					
						
							|  |  |  | 	reply.StorageInfo = web.ObjectAPI.StorageInfo() | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-19 16:00:32 +08:00
										 |  |  | // MakeBucketArgs - make bucket args.
 | 
					
						
							|  |  |  | type MakeBucketArgs struct { | 
					
						
							|  |  |  | 	BucketName string `json:"bucketName"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-25 14:26:53 +08:00
										 |  |  | // MakeBucket - make a bucket.
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:45:15 +08:00
										 |  |  | func (web *webAPIHandlers) MakeBucket(r *http.Request, args *MakeBucketArgs, reply *WebGenericRep) error { | 
					
						
							| 
									
										
										
										
											2016-02-18 18:13:52 +08:00
										 |  |  | 	if !isJWTReqAuthenticated(r) { | 
					
						
							| 
									
										
										
										
											2016-02-13 10:55:17 +08:00
										 |  |  | 		return &json2.Error{Message: "Unauthorized request"} | 
					
						
							| 
									
										
										
										
											2016-01-25 14:26:53 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-24 05:05:47 +08:00
										 |  |  | 	reply.UIVersion = miniobrowser.UIVersion | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 	if err := web.ObjectAPI.MakeBucket(args.BucketName); err != nil { | 
					
						
							|  |  |  | 		return &json2.Error{Message: err.Error()} | 
					
						
							| 
									
										
										
										
											2016-02-13 10:55:17 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-01-25 14:26:53 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-19 16:00:32 +08:00
										 |  |  | // ListBucketsRep - list buckets response
 | 
					
						
							|  |  |  | type ListBucketsRep struct { | 
					
						
							| 
									
										
										
											
												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
										 |  |  | 	Buckets   []WebBucketInfo `json:"buckets"` | 
					
						
							|  |  |  | 	UIVersion string          `json:"uiVersion"` | 
					
						
							| 
									
										
										
										
											2016-02-19 16:00:32 +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
										 |  |  | // WebBucketInfo container for list buckets metadata.
 | 
					
						
							|  |  |  | type WebBucketInfo struct { | 
					
						
							| 
									
										
										
										
											2016-02-19 16:00:32 +08:00
										 |  |  | 	// The name of the bucket.
 | 
					
						
							|  |  |  | 	Name string `json:"name"` | 
					
						
							|  |  |  | 	// Date the bucket was created.
 | 
					
						
							|  |  |  | 	CreationDate time.Time `json:"creationDate"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | // ListBuckets - list buckets api.
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:45:15 +08:00
										 |  |  | func (web *webAPIHandlers) ListBuckets(r *http.Request, args *WebGenericArgs, reply *ListBucketsRep) error { | 
					
						
							| 
									
										
										
										
											2016-02-18 18:13:52 +08:00
										 |  |  | 	if !isJWTReqAuthenticated(r) { | 
					
						
							| 
									
										
										
										
											2016-02-13 10:55:17 +08:00
										 |  |  | 		return &json2.Error{Message: "Unauthorized request"} | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 	buckets, err := web.ObjectAPI.ListBuckets() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return &json2.Error{Message: err.Error()} | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-01-25 14:26:53 +08:00
										 |  |  | 	for _, bucket := range buckets { | 
					
						
							| 
									
										
										
										
											2016-02-20 13:45:37 +08:00
										 |  |  | 		// List all buckets which are not private.
 | 
					
						
							| 
									
										
										
										
											2016-03-28 03:37:21 +08:00
										 |  |  | 		if bucket.Name != path.Base(reservedBucket) { | 
					
						
							| 
									
										
										
											
												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
										 |  |  | 			reply.Buckets = append(reply.Buckets, WebBucketInfo{ | 
					
						
							| 
									
										
										
										
											2016-02-20 13:45:37 +08:00
										 |  |  | 				Name:         bucket.Name, | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 				CreationDate: bucket.Created, | 
					
						
							| 
									
										
										
										
											2016-02-20 13:45:37 +08:00
										 |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-24 05:05:47 +08:00
										 |  |  | 	reply.UIVersion = miniobrowser.UIVersion | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-19 16:00:32 +08:00
										 |  |  | // ListObjectsArgs - list object args.
 | 
					
						
							|  |  |  | type ListObjectsArgs struct { | 
					
						
							|  |  |  | 	BucketName string `json:"bucketName"` | 
					
						
							|  |  |  | 	Prefix     string `json:"prefix"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ListObjectsRep - list objects response.
 | 
					
						
							|  |  |  | type ListObjectsRep struct { | 
					
						
							| 
									
										
										
											
												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
										 |  |  | 	Objects   []WebObjectInfo `json:"objects"` | 
					
						
							|  |  |  | 	UIVersion string          `json:"uiVersion"` | 
					
						
							| 
									
										
										
										
											2016-02-19 16:00:32 +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
										 |  |  | // WebObjectInfo container for list objects metadata.
 | 
					
						
							|  |  |  | type WebObjectInfo struct { | 
					
						
							| 
									
										
										
										
											2016-02-19 16:00:32 +08:00
										 |  |  | 	// Name of the object
 | 
					
						
							|  |  |  | 	Key string `json:"name"` | 
					
						
							|  |  |  | 	// Date and time the object was last modified.
 | 
					
						
							|  |  |  | 	LastModified time.Time `json:"lastModified"` | 
					
						
							|  |  |  | 	// Size in bytes of the object.
 | 
					
						
							|  |  |  | 	Size int64 `json:"size"` | 
					
						
							|  |  |  | 	// ContentType is mime type of the object.
 | 
					
						
							|  |  |  | 	ContentType string `json:"contentType"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | // ListObjects - list objects api.
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:45:15 +08:00
										 |  |  | func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, reply *ListObjectsRep) error { | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 	marker := "" | 
					
						
							| 
									
										
										
										
											2016-02-18 18:13:52 +08:00
										 |  |  | 	if !isJWTReqAuthenticated(r) { | 
					
						
							| 
									
										
										
										
											2016-02-13 10:55:17 +08:00
										 |  |  | 		return &json2.Error{Message: "Unauthorized request"} | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 	for { | 
					
						
							| 
									
										
											  
											
												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
										 |  |  | 		lo, err := web.ObjectAPI.ListObjects(args.BucketName, args.Prefix, marker, "/", 1000) | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 			return &json2.Error{Message: err.Error()} | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 		marker = lo.NextMarker | 
					
						
							|  |  |  | 		for _, obj := range lo.Objects { | 
					
						
							| 
									
										
										
											
												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
										 |  |  | 			reply.Objects = append(reply.Objects, WebObjectInfo{ | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 				Key:          obj.Name, | 
					
						
							| 
									
										
										
										
											2016-04-09 01:37:38 +08:00
										 |  |  | 				LastModified: obj.ModTime, | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 				Size:         obj.Size, | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2016-02-02 04:47:46 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 		for _, prefix := range lo.Prefixes { | 
					
						
							| 
									
										
										
											
												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
										 |  |  | 			reply.Objects = append(reply.Objects, WebObjectInfo{ | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 				Key: prefix, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !lo.IsTruncated { | 
					
						
							|  |  |  | 			break | 
					
						
							| 
									
										
										
										
											2016-02-02 04:19:54 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-24 05:05:47 +08:00
										 |  |  | 	reply.UIVersion = miniobrowser.UIVersion | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-19 16:00:32 +08:00
										 |  |  | // RemoveObjectArgs - args to remove an object
 | 
					
						
							|  |  |  | type RemoveObjectArgs struct { | 
					
						
							|  |  |  | 	TargetHost string `json:"targetHost"` | 
					
						
							|  |  |  | 	BucketName string `json:"bucketName"` | 
					
						
							|  |  |  | 	ObjectName string `json:"objectName"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-05 22:16:36 +08:00
										 |  |  | // RemoveObject - removes an object.
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:45:15 +08:00
										 |  |  | func (web *webAPIHandlers) RemoveObject(r *http.Request, args *RemoveObjectArgs, reply *WebGenericRep) error { | 
					
						
							| 
									
										
										
										
											2016-02-18 18:13:52 +08:00
										 |  |  | 	if !isJWTReqAuthenticated(r) { | 
					
						
							| 
									
										
										
										
											2016-02-13 10:55:17 +08:00
										 |  |  | 		return &json2.Error{Message: "Unauthorized request"} | 
					
						
							| 
									
										
										
										
											2016-02-05 22:16:36 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-02-24 05:05:47 +08:00
										 |  |  | 	reply.UIVersion = miniobrowser.UIVersion | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 	if err := web.ObjectAPI.DeleteObject(args.BucketName, args.ObjectName); err != nil { | 
					
						
							|  |  |  | 		return &json2.Error{Message: err.Error()} | 
					
						
							| 
									
										
										
										
											2016-02-13 10:55:17 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-02-05 22:16:36 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-19 16:00:32 +08:00
										 |  |  | // LoginArgs - login arguments.
 | 
					
						
							|  |  |  | type LoginArgs struct { | 
					
						
							|  |  |  | 	Username string `json:"username" form:"username"` | 
					
						
							|  |  |  | 	Password string `json:"password" form:"password"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // LoginRep - login reply.
 | 
					
						
							|  |  |  | type LoginRep struct { | 
					
						
							|  |  |  | 	Token     string `json:"token"` | 
					
						
							|  |  |  | 	UIVersion string `json:"uiVersion"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | // Login - user login handler.
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:45:15 +08:00
										 |  |  | func (web *webAPIHandlers) Login(r *http.Request, args *LoginArgs, reply *LoginRep) error { | 
					
						
							| 
									
										
										
										
											2016-07-12 12:57:40 +08:00
										 |  |  | 	jwt, err := newJWT() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return &json2.Error{Message: err.Error()} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err = jwt.Authenticate(args.Username, args.Password); err != nil { | 
					
						
							|  |  |  | 		return &json2.Error{Message: err.Error()} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	token, err := jwt.GenerateToken(args.Username) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return &json2.Error{Message: err.Error()} | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-12 12:57:40 +08:00
										 |  |  | 	reply.Token = token | 
					
						
							|  |  |  | 	reply.UIVersion = miniobrowser.UIVersion | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-01-24 11:44:32 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-03-22 02:15:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // GenerateAuthReply - reply for GenerateAuth
 | 
					
						
							|  |  |  | type GenerateAuthReply struct { | 
					
						
							|  |  |  | 	AccessKey string `json:"accessKey"` | 
					
						
							|  |  |  | 	SecretKey string `json:"secretKey"` | 
					
						
							|  |  |  | 	UIVersion string `json:"uiVersion"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:45:15 +08:00
										 |  |  | func (web webAPIHandlers) GenerateAuth(r *http.Request, args *WebGenericArgs, reply *GenerateAuthReply) error { | 
					
						
							| 
									
										
										
										
											2016-03-22 02:15:08 +08:00
										 |  |  | 	if !isJWTReqAuthenticated(r) { | 
					
						
							|  |  |  | 		return &json2.Error{Message: "Unauthorized request"} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	cred := mustGenAccessKeys() | 
					
						
							|  |  |  | 	reply.AccessKey = cred.AccessKeyID | 
					
						
							|  |  |  | 	reply.SecretKey = cred.SecretAccessKey | 
					
						
							|  |  |  | 	reply.UIVersion = miniobrowser.UIVersion | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SetAuthArgs - argument for SetAuth
 | 
					
						
							|  |  |  | type SetAuthArgs struct { | 
					
						
							|  |  |  | 	AccessKey string `json:"accessKey"` | 
					
						
							|  |  |  | 	SecretKey string `json:"secretKey"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SetAuthReply - reply for SetAuth
 | 
					
						
							|  |  |  | type SetAuthReply struct { | 
					
						
							|  |  |  | 	Token     string `json:"token"` | 
					
						
							|  |  |  | 	UIVersion string `json:"uiVersion"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SetAuth - Set accessKey and secretKey credentials.
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:45:15 +08:00
										 |  |  | func (web *webAPIHandlers) SetAuth(r *http.Request, args *SetAuthArgs, reply *SetAuthReply) error { | 
					
						
							| 
									
										
										
										
											2016-03-22 02:15:08 +08:00
										 |  |  | 	if !isJWTReqAuthenticated(r) { | 
					
						
							|  |  |  | 		return &json2.Error{Message: "Unauthorized request"} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-30 17:47:20 +08:00
										 |  |  | 	if !isValidAccessKey.MatchString(args.AccessKey) { | 
					
						
							|  |  |  | 		return &json2.Error{Message: "Invalid Access Key"} | 
					
						
							| 
									
										
										
										
											2016-03-22 02:15:08 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-30 17:47:20 +08:00
										 |  |  | 	if !isValidSecretKey.MatchString(args.SecretKey) { | 
					
						
							|  |  |  | 		return &json2.Error{Message: "Invalid Secret Key"} | 
					
						
							| 
									
										
										
										
											2016-03-22 02:15:08 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	cred := credential{args.AccessKey, args.SecretKey} | 
					
						
							|  |  |  | 	serverConfig.SetCredential(cred) | 
					
						
							|  |  |  | 	if err := serverConfig.Save(); err != nil { | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 		return &json2.Error{Message: err.Error()} | 
					
						
							| 
									
										
										
										
											2016-03-22 02:15:08 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-12 12:57:40 +08:00
										 |  |  | 	jwt, err := newJWT() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return &json2.Error{Message: err.Error()} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err = jwt.Authenticate(args.AccessKey, args.SecretKey); err != nil { | 
					
						
							|  |  |  | 		return &json2.Error{Message: err.Error()} | 
					
						
							| 
									
										
										
										
											2016-03-22 02:15:08 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	token, err := jwt.GenerateToken(args.AccessKey) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 		return &json2.Error{Message: err.Error()} | 
					
						
							| 
									
										
										
										
											2016-03-22 02:15:08 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	reply.Token = token | 
					
						
							|  |  |  | 	reply.UIVersion = miniobrowser.UIVersion | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // GetAuthReply - Reply current credentials.
 | 
					
						
							|  |  |  | type GetAuthReply struct { | 
					
						
							|  |  |  | 	AccessKey string `json:"accessKey"` | 
					
						
							|  |  |  | 	SecretKey string `json:"secretKey"` | 
					
						
							|  |  |  | 	UIVersion string `json:"uiVersion"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // GetAuth - return accessKey and secretKey credentials.
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:45:15 +08:00
										 |  |  | func (web *webAPIHandlers) GetAuth(r *http.Request, args *WebGenericArgs, reply *GetAuthReply) error { | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 	if !isJWTReqAuthenticated(r) { | 
					
						
							|  |  |  | 		return &json2.Error{Message: "Unauthorized request"} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	creds := serverConfig.GetCredential() | 
					
						
							|  |  |  | 	reply.AccessKey = creds.AccessKeyID | 
					
						
							|  |  |  | 	reply.SecretKey = creds.SecretAccessKey | 
					
						
							|  |  |  | 	reply.UIVersion = miniobrowser.UIVersion | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Upload - file upload handler.
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:45:15 +08:00
										 |  |  | func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 	if !isJWTReqAuthenticated(r) { | 
					
						
							|  |  |  | 		writeWebErrorResponse(w, errInvalidToken) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	bucket := vars["bucket"] | 
					
						
							|  |  |  | 	object := vars["object"] | 
					
						
							| 
									
										
										
										
											2016-07-28 12:11:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Extract incoming metadata if any.
 | 
					
						
							|  |  |  | 	metadata := extractMetadataFromHeader(r.Header) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if _, err := web.ObjectAPI.PutObject(bucket, object, -1, r.Body, metadata); err != nil { | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 		writeWebErrorResponse(w, err) | 
					
						
							| 
									
										
										
										
											2016-07-28 12:11:15 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Fetch object info for notifications.
 | 
					
						
							|  |  |  | 	objInfo, err := web.ObjectAPI.GetObjectInfo(bucket, object) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		errorIf(err, "Unable to fetch object info for \"%s\"", path.Join(bucket, object)) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-05 13:01:58 +08:00
										 |  |  | 	if eventN.IsBucketNotificationSet(bucket) { | 
					
						
							|  |  |  | 		// Notify object created event.
 | 
					
						
							|  |  |  | 		eventNotify(eventData{ | 
					
						
							|  |  |  | 			Type:    ObjectCreatedPut, | 
					
						
							|  |  |  | 			Bucket:  bucket, | 
					
						
							|  |  |  | 			ObjInfo: objInfo, | 
					
						
							|  |  |  | 			ReqParams: map[string]string{ | 
					
						
							|  |  |  | 				"sourceIPAddress": r.RemoteAddr, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Download - file download handler.
 | 
					
						
							| 
									
										
										
										
											2016-04-13 03:45:15 +08:00
										 |  |  | func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	bucket := vars["bucket"] | 
					
						
							|  |  |  | 	object := vars["object"] | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:20 +08:00
										 |  |  | 	tokenStr := r.URL.Query().Get("token") | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-12 12:57:40 +08:00
										 |  |  | 	jwt, err := newJWT() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		errorIf(err, "error in getting new JWT") | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:20 +08:00
										 |  |  | 	token, e := jwtgo.Parse(tokenStr, func(token *jwtgo.Token) (interface{}, error) { | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 		if _, ok := token.Method.(*jwtgo.SigningMethodHMAC); !ok { | 
					
						
							|  |  |  | 			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return []byte(jwt.SecretAccessKey), nil | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:20 +08:00
										 |  |  | 	if e != nil || !token.Valid { | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 		writeWebErrorResponse(w, errInvalidToken) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-08 14:44:08 +08:00
										 |  |  | 	// Add content disposition.
 | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 	w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filepath.Base(object))) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-29 06:13:15 +08:00
										 |  |  | 	objInfo, err := web.ObjectAPI.GetObjectInfo(bucket, object) | 
					
						
							| 
									
										
											  
											
												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
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-04-30 05:24:10 +08:00
										 |  |  | 		writeWebErrorResponse(w, err) | 
					
						
							| 
									
										
											  
											
												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
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-29 06:13:15 +08:00
										 |  |  | 	offset := int64(0) | 
					
						
							|  |  |  | 	err = web.ObjectAPI.GetObject(bucket, object, offset, objInfo.Size, w) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
											  
											
												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
										 |  |  | 		/// No need to print error, response writer already written to.
 | 
					
						
							|  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // writeWebErrorResponse - set HTTP status code and write error description to the body.
 | 
					
						
							|  |  |  | func writeWebErrorResponse(w http.ResponseWriter, err error) { | 
					
						
							| 
									
										
										
										
											2016-04-08 14:44:08 +08:00
										 |  |  | 	// Handle invalid token as a special case.
 | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 	if err == errInvalidToken { | 
					
						
							|  |  |  | 		w.WriteHeader(http.StatusForbidden) | 
					
						
							|  |  |  | 		w.Write([]byte(err.Error())) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-08 14:44:08 +08:00
										 |  |  | 	// Convert error type to api error code.
 | 
					
						
							|  |  |  | 	var apiErrCode APIErrorCode | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 	switch err.(type) { | 
					
						
							| 
									
										
										
										
											2016-04-19 17:42:10 +08:00
										 |  |  | 	case StorageFull: | 
					
						
							|  |  |  | 		apiErrCode = ErrStorageFull | 
					
						
							| 
									
										
											  
											
												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
										 |  |  | 	case BucketNotFound: | 
					
						
							| 
									
										
										
										
											2016-04-08 14:44:08 +08:00
										 |  |  | 		apiErrCode = ErrNoSuchBucket | 
					
						
							| 
									
										
											  
											
												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
										 |  |  | 	case BucketNameInvalid: | 
					
						
							| 
									
										
										
										
											2016-04-08 14:44:08 +08:00
										 |  |  | 		apiErrCode = ErrInvalidBucketName | 
					
						
							| 
									
										
											  
											
												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
										 |  |  | 	case BadDigest: | 
					
						
							| 
									
										
										
										
											2016-04-08 14:44:08 +08:00
										 |  |  | 		apiErrCode = ErrBadDigest | 
					
						
							| 
									
										
											  
											
												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
										 |  |  | 	case IncompleteBody: | 
					
						
							| 
									
										
										
										
											2016-04-08 14:44:08 +08:00
										 |  |  | 		apiErrCode = ErrIncompleteBody | 
					
						
							| 
									
										
										
										
											2016-05-09 03:36:16 +08:00
										 |  |  | 	case ObjectExistsAsDirectory: | 
					
						
							|  |  |  | 		apiErrCode = ErrObjectExistsAsDirectory | 
					
						
							| 
									
										
											  
											
												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
										 |  |  | 	case ObjectNotFound: | 
					
						
							| 
									
										
										
										
											2016-04-08 14:44:08 +08:00
										 |  |  | 		apiErrCode = ErrNoSuchKey | 
					
						
							| 
									
										
											  
											
												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
										 |  |  | 	case ObjectNameInvalid: | 
					
						
							| 
									
										
										
										
											2016-04-08 14:44:08 +08:00
										 |  |  | 		apiErrCode = ErrNoSuchKey | 
					
						
							| 
									
										
										
										
											2016-05-06 11:24:29 +08:00
										 |  |  | 	case InsufficientWriteQuorum: | 
					
						
							|  |  |  | 		apiErrCode = ErrWriteQuorum | 
					
						
							|  |  |  | 	case InsufficientReadQuorum: | 
					
						
							|  |  |  | 		apiErrCode = ErrReadQuorum | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2016-04-08 14:44:08 +08:00
										 |  |  | 		apiErrCode = ErrInternalError | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-04-08 14:44:08 +08:00
										 |  |  | 	apiErr := getAPIError(apiErrCode) | 
					
						
							|  |  |  | 	w.WriteHeader(apiErr.HTTPStatusCode) | 
					
						
							|  |  |  | 	w.Write([]byte(apiErr.Description)) | 
					
						
							| 
									
										
										
										
											2016-03-31 21:57:29 +08:00
										 |  |  | } |