mirror of
https://github.com/minio/minio.git
synced 2025-11-26 04:26:12 -05:00
Implement S3 Style ErrorCodes and Response
This patchset also brings in lot of cleanup in terms of minioapi codebase
This commit is contained in:
@@ -17,26 +17,12 @@
|
||||
package minioapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
mstorage "github.com/minio-io/minio/pkg/storage"
|
||||
"github.com/minio-io/minio/pkg/utils/config"
|
||||
"github.com/minio-io/minio/pkg/utils/crypto/signers"
|
||||
)
|
||||
|
||||
type contentType int
|
||||
|
||||
const (
|
||||
xmlType contentType = iota
|
||||
jsonType
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -47,11 +33,6 @@ type minioApi struct {
|
||||
storage mstorage.Storage
|
||||
}
|
||||
|
||||
type vHandler struct {
|
||||
conf config.Config
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
// No encoder interface exists, so we create one.
|
||||
type encoder interface {
|
||||
Encode(v interface{}) error
|
||||
@@ -77,59 +58,6 @@ func HttpHandler(storage mstorage.Storage) http.Handler {
|
||||
return validateHandler(conf, ignoreUnimplementedResources(mux))
|
||||
}
|
||||
|
||||
// grab AccessKey from authorization header
|
||||
func stripAccessKey(r *http.Request) string {
|
||||
fields := strings.Fields(r.Header.Get("Authorization"))
|
||||
if len(fields) < 2 {
|
||||
return ""
|
||||
}
|
||||
splits := strings.Split(fields[1], ":")
|
||||
if len(splits) < 2 {
|
||||
return ""
|
||||
}
|
||||
return splits[0]
|
||||
}
|
||||
|
||||
func validateHandler(conf config.Config, h http.Handler) http.Handler {
|
||||
return vHandler{conf, h}
|
||||
}
|
||||
|
||||
func (h vHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
accessKey := stripAccessKey(r)
|
||||
if accessKey != "" {
|
||||
if err := h.conf.ReadConfig(); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
} else {
|
||||
user := h.conf.GetKey(accessKey)
|
||||
ok, err := signers.ValidateRequest(user, r)
|
||||
if ok {
|
||||
h.handler.ServeHTTP(w, r)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte(err.Error()))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//No access key found, handle this more appropriately
|
||||
//TODO: Remove this after adding tests to support signature
|
||||
//request
|
||||
h.handler.ServeHTTP(w, r)
|
||||
//Add this line, to reply back for invalid requests
|
||||
//w.WriteHeader(http.StatusUnauthorized)
|
||||
//w.Write([]byte("Authorization header malformed")
|
||||
}
|
||||
}
|
||||
|
||||
func ignoreUnimplementedResources(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if ignoreUnImplementedObjectResources(r) || ignoreUnImplementedBucketResources(r) {
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
} else {
|
||||
h.ServeHTTP(w, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (server *minioApi) listBucketsHandler(w http.ResponseWriter, req *http.Request) {
|
||||
vars := mux.Vars(req)
|
||||
prefix, ok := vars["prefix"]
|
||||
@@ -139,13 +67,36 @@ func (server *minioApi) listBucketsHandler(w http.ResponseWriter, req *http.Requ
|
||||
|
||||
acceptsContentType := getContentType(req)
|
||||
buckets, err := server.storage.ListBuckets(prefix)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
{
|
||||
response := generateBucketsListResult(buckets)
|
||||
w.Write(writeObjectHeadersAndResponse(w, response, acceptsContentType))
|
||||
}
|
||||
case mstorage.BucketNameInvalid:
|
||||
{
|
||||
error := errorCodeError(InvalidBucketName)
|
||||
errorResponse := getErrorResponse(error, prefix)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
case mstorage.ImplementationError:
|
||||
{
|
||||
log.Println(err)
|
||||
error := errorCodeError(InternalError)
|
||||
errorResponse := getErrorResponse(error, prefix)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
case mstorage.BackendCorrupted:
|
||||
{
|
||||
log.Println(err)
|
||||
error := errorCodeError(InternalError)
|
||||
errorResponse := getErrorResponse(error, prefix)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
}
|
||||
response := generateBucketsListResult(buckets)
|
||||
w.Write(writeObjectHeadersAndResponse(w, response, acceptsContentType))
|
||||
}
|
||||
|
||||
func (server *minioApi) listObjectsHandler(w http.ResponseWriter, req *http.Request) {
|
||||
@@ -161,19 +112,40 @@ func (server *minioApi) listObjectsHandler(w http.ResponseWriter, req *http.Requ
|
||||
objects, isTruncated, err := server.storage.ListObjects(bucket, prefix, 1000)
|
||||
switch err := err.(type) {
|
||||
case nil: // success
|
||||
response := generateObjectsListResult(bucket, objects, isTruncated)
|
||||
w.Write(writeObjectHeadersAndResponse(w, response, acceptsContentType))
|
||||
{
|
||||
response := generateObjectsListResult(bucket, objects, isTruncated)
|
||||
w.Write(writeObjectHeadersAndResponse(w, response, acceptsContentType))
|
||||
}
|
||||
case mstorage.BucketNotFound:
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte(err.Error()))
|
||||
{
|
||||
error := errorCodeError(NoSuchBucket)
|
||||
errorResponse := getErrorResponse(error, bucket)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
case mstorage.ImplementationError:
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(err.Error()))
|
||||
default:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
{
|
||||
// Embed error log on server side
|
||||
log.Println(err)
|
||||
error := errorCodeError(InternalError)
|
||||
errorResponse := getErrorResponse(error, bucket)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
case mstorage.BucketNameInvalid:
|
||||
{
|
||||
error := errorCodeError(InvalidBucketName)
|
||||
errorResponse := getErrorResponse(error, bucket)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
case mstorage.ObjectNameInvalid:
|
||||
{
|
||||
error := errorCodeError(NoSuchKey)
|
||||
errorResponse := getErrorResponse(error, prefix)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,16 +153,38 @@ func (server *minioApi) putBucketHandler(w http.ResponseWriter, req *http.Reques
|
||||
vars := mux.Vars(req)
|
||||
bucket := vars["bucket"]
|
||||
err := server.storage.StoreBucket(bucket)
|
||||
|
||||
acceptsContentType := getContentType(req)
|
||||
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
w.Header().Set("Server", "Minio")
|
||||
w.Header().Set("Connection", "close")
|
||||
{
|
||||
w.Header().Set("Server", "Minio")
|
||||
w.Header().Set("Connection", "close")
|
||||
}
|
||||
case mstorage.BucketNameInvalid:
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
{
|
||||
error := errorCodeError(InvalidBucketName)
|
||||
errorResponse := getErrorResponse(error, bucket)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
case mstorage.BucketExists:
|
||||
w.WriteHeader(http.StatusConflict)
|
||||
w.Write([]byte(err.Error()))
|
||||
{
|
||||
error := errorCodeError(BucketAlreadyExists)
|
||||
errorResponse := getErrorResponse(error, bucket)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
case mstorage.ImplementationError:
|
||||
{
|
||||
// Embed errors log on serve side
|
||||
log.Println(err)
|
||||
error := errorCodeError(InternalError)
|
||||
errorResponse := getErrorResponse(error, bucket)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,6 +193,8 @@ func (server *minioApi) getObjectHandler(w http.ResponseWriter, req *http.Reques
|
||||
bucket := vars["bucket"]
|
||||
object := vars["object"]
|
||||
|
||||
acceptsContentType := getContentType(req)
|
||||
|
||||
metadata, err := server.storage.GetObjectMetadata(bucket, object)
|
||||
switch err := err.(type) {
|
||||
case nil: // success
|
||||
@@ -211,15 +207,33 @@ func (server *minioApi) getObjectHandler(w http.ResponseWriter, req *http.Reques
|
||||
}
|
||||
case mstorage.ObjectNotFound:
|
||||
{
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
w.Write([]byte(err.Error()))
|
||||
error := errorCodeError(NoSuchKey)
|
||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
default:
|
||||
case mstorage.ObjectNameInvalid:
|
||||
{
|
||||
error := errorCodeError(NoSuchKey)
|
||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
case mstorage.BucketNameInvalid:
|
||||
{
|
||||
error := errorCodeError(InvalidBucketName)
|
||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
case mstorage.ImplementationError:
|
||||
{
|
||||
// Embed errors log on serve side
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(err.Error()))
|
||||
error := errorCodeError(InternalError)
|
||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -229,16 +243,35 @@ func (server *minioApi) headObjectHandler(w http.ResponseWriter, req *http.Reque
|
||||
bucket := vars["bucket"]
|
||||
object := vars["object"]
|
||||
|
||||
acceptsContentType := getContentType(req)
|
||||
|
||||
metadata, err := server.storage.GetObjectMetadata(bucket, object)
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
writeObjectHeaders(w, metadata)
|
||||
case mstorage.ObjectNotFound:
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
default:
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
{
|
||||
error := errorCodeError(NoSuchKey)
|
||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
case mstorage.ObjectNameInvalid:
|
||||
{
|
||||
error := errorCodeError(NoSuchKey)
|
||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
case mstorage.ImplementationError:
|
||||
{
|
||||
// Embed error log on server side
|
||||
log.Println(err)
|
||||
error := errorCodeError(InternalError)
|
||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,41 +279,46 @@ func (server *minioApi) putObjectHandler(w http.ResponseWriter, req *http.Reques
|
||||
vars := mux.Vars(req)
|
||||
bucket := vars["bucket"]
|
||||
object := vars["object"]
|
||||
|
||||
acceptsContentType := getContentType(req)
|
||||
|
||||
err := server.storage.StoreObject(bucket, object, "", req.Body)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
w.Header().Set("Server", "Minio")
|
||||
w.Header().Set("Connection", "close")
|
||||
case mstorage.ImplementationError:
|
||||
{
|
||||
// Embed error log on server side
|
||||
log.Println(err)
|
||||
error := errorCodeError(InternalError)
|
||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
case mstorage.BucketNotFound:
|
||||
{
|
||||
error := errorCodeError(NoSuchBucket)
|
||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
case mstorage.BucketNameInvalid:
|
||||
{
|
||||
error := errorCodeError(InvalidBucketName)
|
||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
case mstorage.ObjectExists:
|
||||
{
|
||||
error := errorCodeError(NotImplemented)
|
||||
errorResponse := getErrorResponse(error, "/"+bucket+"/"+object)
|
||||
w.WriteHeader(error.HttpStatusCode)
|
||||
w.Write(writeErrorResponse(w, errorResponse, acceptsContentType))
|
||||
}
|
||||
}
|
||||
w.Header().Set("Server", "Minio")
|
||||
w.Header().Set("Connection", "close")
|
||||
}
|
||||
|
||||
func writeObjectHeadersAndResponse(w http.ResponseWriter, response interface{}, acceptsType contentType) []byte {
|
||||
var bytesBuffer bytes.Buffer
|
||||
var encoder encoder
|
||||
if acceptsType == xmlType {
|
||||
w.Header().Set("Content-Type", "application/xml")
|
||||
encoder = xml.NewEncoder(&bytesBuffer)
|
||||
} else if acceptsType == jsonType {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
encoder = json.NewEncoder(&bytesBuffer)
|
||||
}
|
||||
w.Header().Set("Server", "Minio")
|
||||
w.Header().Set("Connection", "close")
|
||||
encoder.Encode(response)
|
||||
return bytesBuffer.Bytes()
|
||||
}
|
||||
|
||||
// Write Object Header helper
|
||||
func writeObjectHeaders(w http.ResponseWriter, metadata mstorage.ObjectMetadata) {
|
||||
lastModified := metadata.Created.Format(time.RFC1123)
|
||||
w.Header().Set("ETag", metadata.ETag)
|
||||
w.Header().Set("Server", "Minio")
|
||||
w.Header().Set("Last-Modified", lastModified)
|
||||
w.Header().Set("Content-Length", strconv.FormatInt(metadata.Size, 10))
|
||||
w.Header().Set("Content-Type", metadata.ContentType)
|
||||
w.Header().Set("Connection", "close")
|
||||
}
|
||||
|
||||
func generateBucketsListResult(buckets []mstorage.BucketMetadata) BucketListResponse {
|
||||
@@ -304,38 +342,6 @@ func generateBucketsListResult(buckets []mstorage.BucketMetadata) BucketListResp
|
||||
return data
|
||||
}
|
||||
|
||||
//// helpers
|
||||
|
||||
// Checks requests for unimplemented resources
|
||||
func ignoreUnImplementedBucketResources(req *http.Request) bool {
|
||||
q := req.URL.Query()
|
||||
for name := range q {
|
||||
if unimplementedBucketResourceNames[name] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ignoreUnImplementedObjectResources(req *http.Request) bool {
|
||||
q := req.URL.Query()
|
||||
for name := range q {
|
||||
if unimplementedObjectResourceNames[name] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getContentType(req *http.Request) contentType {
|
||||
if _, ok := req.Header["Accept"]; ok {
|
||||
if req.Header["Accept"][0] == "application/json" {
|
||||
return jsonType
|
||||
}
|
||||
}
|
||||
return xmlType
|
||||
}
|
||||
|
||||
// takes a set of objects and prepares the objects for serialization
|
||||
// input:
|
||||
// bucket name
|
||||
|
||||
Reference in New Issue
Block a user