Merge pull request #202 from harshavardhana/pr_out_update_minioapi_documentation

master
Harshavardhana 10 years ago
commit 2b8adef454
  1. 29
      pkg/api/minioapi/bucket_handlers.go
  2. 5
      pkg/api/minioapi/contenttype.go
  3. 7
      pkg/api/minioapi/definitions.go
  4. 10
      pkg/api/minioapi/error_response.go
  5. 23
      pkg/api/minioapi/generic_handlers.go
  6. 12
      pkg/api/minioapi/headers.go
  7. 9
      pkg/api/minioapi/muxer.go
  8. 26
      pkg/api/minioapi/object_handlers.go
  9. 24
      pkg/api/minioapi/policy_handlers.go
  10. 18
      pkg/api/minioapi/resources.go
  11. 27
      pkg/api/minioapi/response.go

@ -1,3 +1,19 @@
/*
* Mini Object Storage, (C) 2015 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.
*/
package minioapi package minioapi
import ( import (
@ -8,6 +24,12 @@ import (
mstorage "github.com/minio-io/minio/pkg/storage" mstorage "github.com/minio-io/minio/pkg/storage"
) )
// GET Bucket (List Objects)
// -------------------------
// This implementation of the GET operation returns some or all (up to 1000)
// of the objects in a bucket. You can use the request parameters as selection
// criteria to return a subset of the objects in a bucket.
//
func (server *minioApi) listObjectsHandler(w http.ResponseWriter, req *http.Request) { func (server *minioApi) listObjectsHandler(w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req) vars := mux.Vars(req)
bucket := vars["bucket"] bucket := vars["bucket"]
@ -59,6 +81,10 @@ func (server *minioApi) listObjectsHandler(w http.ResponseWriter, req *http.Requ
} }
} }
// GET Service
// -----------
// This implementation of the GET operation returns a list of all buckets
// owned by the authenticated sender of the request.
func (server *minioApi) listBucketsHandler(w http.ResponseWriter, req *http.Request) { func (server *minioApi) listBucketsHandler(w http.ResponseWriter, req *http.Request) {
acceptsContentType := getContentType(req) acceptsContentType := getContentType(req)
buckets, err := server.storage.ListBuckets() buckets, err := server.storage.ListBuckets()
@ -87,6 +113,9 @@ func (server *minioApi) listBucketsHandler(w http.ResponseWriter, req *http.Requ
} }
} }
// PUT Bucket
// ----------
// This implementation of the PUT operation creates a new bucket for authenticated request
func (server *minioApi) putBucketHandler(w http.ResponseWriter, req *http.Request) { func (server *minioApi) putBucketHandler(w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req) vars := mux.Vars(req)
bucket := vars["bucket"] bucket := vars["bucket"]

@ -27,15 +27,19 @@ const (
jsonType jsonType
) )
// content-type to human readable map
var typeToString = map[contentType]string{ var typeToString = map[contentType]string{
xmlType: "application/xml", xmlType: "application/xml",
jsonType: "application/json", jsonType: "application/json",
} }
// human readbale to content-type map
var acceptToType = map[string]contentType{ var acceptToType = map[string]contentType{
"application/xml": xmlType, "application/xml": xmlType,
"application/json": jsonType, "application/json": jsonType,
} }
// Get content type requested from 'Accept' header
func getContentType(req *http.Request) contentType { func getContentType(req *http.Request) contentType {
if accept := req.Header.Get("Accept"); accept != "" { if accept := req.Header.Get("Accept"); accept != "" {
return acceptToType[accept] return acceptToType[accept]
@ -43,6 +47,7 @@ func getContentType(req *http.Request) contentType {
return xmlType return xmlType
} }
// Content type to human readable string
func getContentString(content contentType) string { func getContentString(content contentType) string {
return typeToString[content] return typeToString[content]
} }

@ -20,10 +20,12 @@ import (
"encoding/xml" "encoding/xml"
) )
// Limit number of objects in a given response
const ( const (
MAX_OBJECT_LIST = 1000 MAX_OBJECT_LIST = 1000
) )
// Object list response format
type ObjectListResponse struct { type ObjectListResponse struct {
XMLName xml.Name `xml:"ListBucketResult" json:"-"` XMLName xml.Name `xml:"ListBucketResult" json:"-"`
Name string Name string
@ -33,6 +35,7 @@ type ObjectListResponse struct {
Contents []*Item `xml:,innerxml` Contents []*Item `xml:,innerxml`
} }
// Bucket list response format
type BucketListResponse struct { type BucketListResponse struct {
XMLName xml.Name `xml:"ListAllMyBucketsResult" json:"-"` XMLName xml.Name `xml:"ListAllMyBucketsResult" json:"-"`
Owner Owner Owner Owner
@ -41,11 +44,13 @@ type BucketListResponse struct {
} `xml:,innerxml` // Buckets are nested } `xml:,innerxml` // Buckets are nested
} }
// Bucket struct
type Bucket struct { type Bucket struct {
Name string Name string
CreationDate string CreationDate string
} }
// Object struct
type Item struct { type Item struct {
Key string Key string
LastModified string LastModified string
@ -60,6 +65,7 @@ type Owner struct {
DisplayName string DisplayName string
} }
// List of not implemented bucket queries
var unimplementedBucketResourceNames = map[string]bool{ var unimplementedBucketResourceNames = map[string]bool{
"acl": true, "acl": true,
"cors": true, "cors": true,
@ -75,6 +81,7 @@ var unimplementedBucketResourceNames = map[string]bool{
"uploads": true, "uploads": true,
} }
// List of not implemented object queries
var unimplementedObjectResourceNames = map[string]bool{ var unimplementedObjectResourceNames = map[string]bool{
"uploadId": true, "uploadId": true,
"acl": true, "acl": true,

@ -21,12 +21,14 @@ import (
"net/http" "net/http"
) )
// Error structure
type Error struct { type Error struct {
Code string Code string
Description string Description string
HttpStatusCode int HttpStatusCode int
} }
// Error response format
type ErrorResponse struct { type ErrorResponse struct {
XMLName xml.Name `xml:"Error" json:"-"` XMLName xml.Name `xml:"Error" json:"-"`
Code string Code string
@ -36,7 +38,7 @@ type ErrorResponse struct {
HostId string HostId string
} }
/// Error codes, non exhaustive list // Error codes, non exhaustive list
const ( const (
AccessDenied = iota AccessDenied = iota
BadDigest BadDigest
@ -63,6 +65,7 @@ const (
NoSuchBucketPolicy NoSuchBucketPolicy
) )
// Error code to Error structure map
var errorCodeResponse = map[int]Error{ var errorCodeResponse = map[int]Error{
AccessDenied: { AccessDenied: {
Code: "AccessDenied", Code: "AccessDenied",
@ -181,12 +184,13 @@ var errorCodeResponse = map[int]Error{
}, },
} }
// errorCodeError provides errorCode to Error. It returns empty if // errorCodeError provides errorCode to Error. It returns empty if the code provided is unknown
// the code provided is unknown
func errorCodeError(code int) Error { func errorCodeError(code int) Error {
return errorCodeResponse[code] return errorCodeResponse[code]
} }
// getErrorResponse gets in standard error and resource value and
// provides a encodable populated response values
func getErrorResponse(err Error, resource string) ErrorResponse { func getErrorResponse(err Error, resource string) ErrorResponse {
var data = ErrorResponse{} var data = ErrorResponse{}
data.Code = err.Code data.Code = err.Code

@ -33,7 +33,7 @@ type rHandler struct {
handler http.Handler handler http.Handler
} }
// grab AccessKey from authorization header // strip AccessKey from authorization header
func stripAccessKey(r *http.Request) string { func stripAccessKey(r *http.Request) string {
fields := strings.Fields(r.Header.Get("Authorization")) fields := strings.Fields(r.Header.Get("Authorization"))
if len(fields) < 2 { if len(fields) < 2 {
@ -46,6 +46,8 @@ func stripAccessKey(r *http.Request) string {
return splits[0] return splits[0]
} }
// Validate handler is wrapper handler used for API request validation with authorization header.
// Current authorization layer supports S3's standard HMAC based signature request.
func validateHandler(conf config.Config, h http.Handler) http.Handler { func validateHandler(conf config.Config, h http.Handler) http.Handler {
return vHandler{conf, h} return vHandler{conf, h}
} }
@ -72,16 +74,23 @@ func (h vHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
} }
} else { } else {
//No access key found, handle this more appropriately // Control reaches when no access key is found, ideally we would
//TODO: Remove this after adding tests to support signature // like to throw back `403`. But for now with our tests lacking
//request // this functionality it is better for us to be serving anonymous
// requests as well.
// We should remove this after adding tests to support signature request
h.handler.ServeHTTP(w, r) h.handler.ServeHTTP(w, r)
//Add this line, to reply back for invalid requests // ## Uncommented below links of code after disabling anonymous requests
//w.WriteHeader(http.StatusUnauthorized) // error := errorCodeError(AccessDenied)
//w.Write([]byte("Authorization header malformed") // errorResponse := getErrorResponse(error, "")
// w.WriteHeader(error.HttpStatusCode)
// w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
} }
} }
// Ignore resources handler is wrapper handler used for API request resource validation
// Since we do not support all the S3 queries, it is necessary for us to throw back a
// valid error message indicating such a feature to have been not implemented.
func ignoreResourcesHandler(h http.Handler) http.Handler { func ignoreResourcesHandler(h http.Handler) http.Handler {
return rHandler{h} return rHandler{h}
} }

@ -32,12 +32,15 @@ type encoder interface {
Encode(v interface{}) error Encode(v interface{}) error
} }
// Write Common Header helpers //// helpers
// Write http common headers
func writeCommonHeaders(w http.ResponseWriter, acceptsType string) { func writeCommonHeaders(w http.ResponseWriter, acceptsType string) {
w.Header().Set("Server", "Minio") w.Header().Set("Server", "Minio")
w.Header().Set("Content-Type", acceptsType) w.Header().Set("Content-Type", acceptsType)
} }
// Write error response headers
func writeErrorResponse(w http.ResponseWriter, response interface{}, acceptsType contentType) []byte { func writeErrorResponse(w http.ResponseWriter, response interface{}, acceptsType contentType) []byte {
var bytesBuffer bytes.Buffer var bytesBuffer bytes.Buffer
var encoder encoder var encoder encoder
@ -53,10 +56,10 @@ func writeErrorResponse(w http.ResponseWriter, response interface{}, acceptsType
return bytesBuffer.Bytes() return bytesBuffer.Bytes()
} }
// Write Object Header helper // Write object header
func writeObjectHeaders(w http.ResponseWriter, metadata mstorage.ObjectMetadata) { func writeObjectHeaders(w http.ResponseWriter, metadata mstorage.ObjectMetadata) {
lastModified := metadata.Created.Format(time.RFC1123) lastModified := metadata.Created.Format(time.RFC1123)
// write common headers // common headers
writeCommonHeaders(w, metadata.ContentType) writeCommonHeaders(w, metadata.ContentType)
w.Header().Set("ETag", metadata.ETag) w.Header().Set("ETag", metadata.ETag)
w.Header().Set("Last-Modified", lastModified) w.Header().Set("Last-Modified", lastModified)
@ -64,10 +67,11 @@ func writeObjectHeaders(w http.ResponseWriter, metadata mstorage.ObjectMetadata)
w.Header().Set("Connection", "close") w.Header().Set("Connection", "close")
} }
// Write object header and response
func writeObjectHeadersAndResponse(w http.ResponseWriter, response interface{}, acceptsType contentType) []byte { func writeObjectHeadersAndResponse(w http.ResponseWriter, response interface{}, acceptsType contentType) []byte {
var bytesBuffer bytes.Buffer var bytesBuffer bytes.Buffer
var encoder encoder var encoder encoder
// write common headers // common headers
writeCommonHeaders(w, getContentString(acceptsType)) writeCommonHeaders(w, getContentString(acceptsType))
switch acceptsType { switch acceptsType {
case xmlType: case xmlType:

@ -25,15 +25,13 @@ import (
"github.com/minio-io/minio/pkg/utils/config" "github.com/minio-io/minio/pkg/utils/config"
) )
const ( // private use
dateFormat = "2006-01-02T15:04:05.000Z"
)
type minioApi struct { type minioApi struct {
domain string domain string
storage mstorage.Storage storage mstorage.Storage
} }
// Path based routing
func pathMux(api minioApi, mux *x.Router) *x.Router { func pathMux(api minioApi, mux *x.Router) *x.Router {
mux.HandleFunc("/", api.listBucketsHandler).Methods("GET") mux.HandleFunc("/", api.listBucketsHandler).Methods("GET")
mux.HandleFunc("/{bucket}", api.listObjectsHandler).Methods("GET") mux.HandleFunc("/{bucket}", api.listObjectsHandler).Methods("GET")
@ -45,6 +43,7 @@ func pathMux(api minioApi, mux *x.Router) *x.Router {
return mux return mux
} }
// Domain based routing
func domainMux(api minioApi, mux *x.Router) *x.Router { func domainMux(api minioApi, mux *x.Router) *x.Router {
mux.HandleFunc("/", mux.HandleFunc("/",
api.listObjectsHandler).Host("{bucket}" + "." + api.domain).Methods("GET") api.listObjectsHandler).Host("{bucket}" + "." + api.domain).Methods("GET")
@ -60,6 +59,7 @@ func domainMux(api minioApi, mux *x.Router) *x.Router {
return mux return mux
} }
// Get proper router based on domain availability
func getMux(api minioApi, mux *x.Router) *x.Router { func getMux(api minioApi, mux *x.Router) *x.Router {
switch true { switch true {
case api.domain == "": case api.domain == "":
@ -71,6 +71,7 @@ func getMux(api minioApi, mux *x.Router) *x.Router {
return nil return nil
} }
// Http wrapper handler
func HttpHandler(domain string, storage mstorage.Storage) http.Handler { func HttpHandler(domain string, storage mstorage.Storage) http.Handler {
var mux *x.Router var mux *x.Router
var api = minioApi{} var api = minioApi{}

@ -1,3 +1,19 @@
/*
* Mini Object Storage, (C) 2015 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.
*/
package minioapi package minioapi
import ( import (
@ -8,6 +24,10 @@ import (
mstorage "github.com/minio-io/minio/pkg/storage" mstorage "github.com/minio-io/minio/pkg/storage"
) )
// GET Object
// ----------
// This implementation of the GET operation retrieves object. To use GET,
// you must have READ access to the object.
func (server *minioApi) getObjectHandler(w http.ResponseWriter, req *http.Request) { func (server *minioApi) getObjectHandler(w http.ResponseWriter, req *http.Request) {
var object, bucket string var object, bucket string
@ -59,6 +79,9 @@ func (server *minioApi) getObjectHandler(w http.ResponseWriter, req *http.Reques
} }
} }
// HEAD Object
// -----------
// The HEAD operation retrieves metadata from an object without returning the object itself.
func (server *minioApi) headObjectHandler(w http.ResponseWriter, req *http.Request) { func (server *minioApi) headObjectHandler(w http.ResponseWriter, req *http.Request) {
var object, bucket string var object, bucket string
acceptsContentType := getContentType(req) acceptsContentType := getContentType(req)
@ -96,6 +119,9 @@ func (server *minioApi) headObjectHandler(w http.ResponseWriter, req *http.Reque
} }
} }
// PUT Object
// ----------
// This implementation of the PUT operation adds an object to a bucket.
func (server *minioApi) putObjectHandler(w http.ResponseWriter, req *http.Request) { func (server *minioApi) putObjectHandler(w http.ResponseWriter, req *http.Request) {
var object, bucket string var object, bucket string
vars := mux.Vars(req) vars := mux.Vars(req)

@ -1,3 +1,19 @@
/*
* Mini Object Storage, (C) 2015 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.
*/
package minioapi package minioapi
import ( import (
@ -10,6 +26,10 @@ import (
"github.com/minio-io/minio/pkg/utils/policy" "github.com/minio-io/minio/pkg/utils/policy"
) )
// PUT Bucket policy
// -----------------
// This implementation of the PUT operation uses the policy subresource
// to add to or replace a policy on a bucket
func (server *minioApi) putBucketPolicyHandler(w http.ResponseWriter, req *http.Request) { func (server *minioApi) putBucketPolicyHandler(w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req) vars := mux.Vars(req)
bucket := vars["bucket"] bucket := vars["bucket"]
@ -58,6 +78,10 @@ func (server *minioApi) putBucketPolicyHandler(w http.ResponseWriter, req *http.
} }
} }
// GET Bucket policy
// -----------------
// This implementation of the GET operation uses the policy subresource
// to return the policy of a specified bucket.
func (server *minioApi) getBucketPolicyHandler(w http.ResponseWriter, req *http.Request) { func (server *minioApi) getBucketPolicyHandler(w http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req) vars := mux.Vars(req)
bucket := vars["bucket"] bucket := vars["bucket"]

@ -1,3 +1,19 @@
/*
* Mini Object Storage, (C) 2015 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.
*/
package minioapi package minioapi
import ( import (
@ -5,6 +21,7 @@ import (
"strconv" "strconv"
) )
// support bucket resources go here
type bucketResources struct { type bucketResources struct {
prefix string prefix string
marker string marker string
@ -14,6 +31,7 @@ type bucketResources struct {
// uploads bool - TODO implemented with multipart support // uploads bool - TODO implemented with multipart support
} }
// parse bucket url queries
func getBucketResources(values url.Values) (v bucketResources) { func getBucketResources(values url.Values) (v bucketResources) {
for key, value := range values { for key, value := range values {
switch true { switch true {

@ -1,9 +1,36 @@
/*
* Mini Object Storage, (C) 2015 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.
*/
package minioapi package minioapi
import ( import (
mstorage "github.com/minio-io/minio/pkg/storage" mstorage "github.com/minio-io/minio/pkg/storage"
) )
// Reply date format
const (
dateFormat = "2006-01-02T15:04:05.000Z"
)
// takes an array of Bucketmetadata information for serialization
// input:
// array of bucket metadata
//
// output:
// populated struct that can be serialized to match xml and json api spec output
func generateBucketsListResult(buckets []mstorage.BucketMetadata) BucketListResponse { func generateBucketsListResult(buckets []mstorage.BucketMetadata) BucketListResponse {
var listbuckets []*Bucket var listbuckets []*Bucket
var data = BucketListResponse{} var data = BucketListResponse{}

Loading…
Cancel
Save