Implement S3 Style ErrorCodes and Response

This patchset also brings in lot of cleanup in terms of minioapi codebase
master
Harshavardhana 10 years ago
parent 6e73ccec75
commit ac4f07906c
  1. 32
      pkg/api/minioapi/contenttype.go
  2. 170
      pkg/api/minioapi/errors.go
  3. 90
      pkg/api/minioapi/handler.go
  4. 61
      pkg/api/minioapi/headers.go
  5. 346
      pkg/api/minioapi/minioapi.go
  6. 1
      pkg/api/minioapi/minioapi_test.go
  7. 2
      pkg/storage/inmemory/inmemory.go

@ -0,0 +1,32 @@
package minioapi
import (
"net/http"
)
type contentType int
const (
xmlType contentType = iota
jsonType
)
var typeToString = map[contentType]string{
xmlType: "application/xml",
jsonType: "application/json",
}
var acceptToType = map[string]contentType{
"application/xml": xmlType,
"application/json": jsonType,
}
func getContentType(req *http.Request) contentType {
if accept := req.Header.Get("Accept"); accept != "" {
return acceptToType[accept]
}
return xmlType
}
func getContentString(content contentType) string {
return typeToString[content]
}

@ -0,0 +1,170 @@
package minioapi
import (
"encoding/xml"
"net/http"
)
type Error struct {
Code string
Description string
HttpStatusCode int
}
type ErrorResponse struct {
XMLName xml.Name `xml:"Error" json:"-"`
Code string
Message string
Resource string
RequestId string
}
/// Error codes, non exhaustive list
const (
AccessDenied = iota
BadDigest
BucketAlreadyExists
EntityTooSmall
EntityTooLarge
IncompleteBody
InternalError
InvalidAccessKeyId
InvalidBucketName
InvalidDigest
InvalidRange
MalformedXML
MissingContentLength
MissingRequestBodyError
NoSuchBucket
NoSuchKey
NoSuchUpload
NotImplemented
RequestTimeTooSkewed
SignatureDoesNotMatch
TooManyBuckets
)
var errorCodeResponse = map[int]Error{
AccessDenied: {
Code: "AccessDenied",
Description: "Access Denied",
HttpStatusCode: http.StatusForbidden,
},
BadDigest: {
Code: "BadDigest",
Description: "The Content-MD5 you specified did not match what we received.",
HttpStatusCode: http.StatusBadRequest,
},
BucketAlreadyExists: {
Code: "BucketAlreadyExists",
Description: "The requested bucket name is not available.",
HttpStatusCode: http.StatusConflict,
},
EntityTooSmall: {
Code: "EntityTooSmall",
Description: "Your proposed upload is smaller than the minimum allowed object size.",
HttpStatusCode: http.StatusBadRequest,
},
EntityTooLarge: {
Code: "EntityTooLarge",
Description: "Your proposed upload exceeds the maximum allowed object size.",
HttpStatusCode: http.StatusBadRequest,
},
IncompleteBody: {
Code: "IncompleteBody",
Description: "You did not provide the number of bytes specified by the Content-Length HTTP header",
HttpStatusCode: http.StatusBadRequest,
},
InternalError: {
Code: "InternalError",
Description: "We encountered an internal error, please try again.",
HttpStatusCode: http.StatusInternalServerError,
},
InvalidAccessKeyId: {
Code: "InvalidAccessKeyId",
Description: "The access key Id you provided does not exist in our records.",
HttpStatusCode: http.StatusForbidden,
},
InvalidBucketName: {
Code: "InvalidBucketName",
Description: "The specified bucket is not valid.",
HttpStatusCode: http.StatusBadRequest,
},
InvalidDigest: {
Code: "InvalidDigest",
Description: "The Content-MD5 you specified is not valid.",
HttpStatusCode: http.StatusBadRequest,
},
InvalidRange: {
Code: "InvalidRange",
Description: "The requested range cannot be satisfied.",
HttpStatusCode: http.StatusRequestedRangeNotSatisfiable,
},
MalformedXML: {
Code: "MalformedXML",
Description: "The XML you provided was not well-formed or did not validate against our published schema.",
HttpStatusCode: http.StatusBadRequest,
},
MissingContentLength: {
Code: "MissingContentLength",
Description: "You must provide the Content-Length HTTP header.",
HttpStatusCode: http.StatusLengthRequired,
},
MissingRequestBodyError: {
Code: "MissingRequestBodyError",
Description: "Request body is empty.",
HttpStatusCode: http.StatusLengthRequired,
},
NoSuchBucket: {
Code: "NoSuchBucket",
Description: "The specified bucket does not exist.",
HttpStatusCode: http.StatusNotFound,
},
NoSuchKey: {
Code: "NoSuchKey",
Description: "The specified key does not exist.",
HttpStatusCode: http.StatusNotFound,
},
NoSuchUpload: {
Code: "NoSuchUpload",
Description: "The specified multipart upload does not exist.",
HttpStatusCode: http.StatusNotFound,
},
NotImplemented: {
Code: "NotImplemented",
Description: "A header you provided implies functionality that is not implemented.",
HttpStatusCode: http.StatusNotImplemented,
},
RequestTimeTooSkewed: {
Code: "RequestTimeTooSkewed",
Description: "The difference between the request time and the server's time is too large.",
HttpStatusCode: http.StatusForbidden,
},
SignatureDoesNotMatch: {
Code: "SignatureDoesNotMatch",
Description: "The request signature we calculated does not match the signature you provided.",
HttpStatusCode: http.StatusForbidden,
},
TooManyBuckets: {
Code: "TooManyBuckets",
Description: "You have attempted to create more buckets than allowed.",
HttpStatusCode: http.StatusBadRequest,
},
}
// errorCodeError provides errorCode to Error. It returns empty if
// the code provided is unknown
func errorCodeError(code int) Error {
return errorCodeResponse[code]
}
func getErrorResponse(err Error, resource string) ErrorResponse {
var data = ErrorResponse{}
data.Code = err.Code
data.Message = err.Description
data.Resource = resource
// TODO implement this in future
data.RequestId = "3LI37"
return data
}

@ -0,0 +1,90 @@
package minioapi
import (
"net/http"
"strings"
"github.com/minio-io/minio/pkg/utils/config"
"github.com/minio-io/minio/pkg/utils/crypto/signers"
)
type vHandler struct {
conf config.Config
handler http.Handler
}
// 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)
}
})
}
//// 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
}

@ -0,0 +1,61 @@
package minioapi
import (
"bytes"
"encoding/json"
"encoding/xml"
"net/http"
"strconv"
"time"
mstorage "github.com/minio-io/minio/pkg/storage"
)
// Write Common Header helpers
func writeCommonHeaders(w http.ResponseWriter, acceptsType string) {
w.Header().Set("Server", "Minio")
w.Header().Set("Content-Type", acceptsType)
}
func writeErrorResponse(w http.ResponseWriter, response interface{}, acceptsType contentType) []byte {
var bytesBuffer bytes.Buffer
var encoder encoder
// write common headers
writeCommonHeaders(w, getContentString(acceptsType))
switch acceptsType {
case xmlType:
encoder = xml.NewEncoder(&bytesBuffer)
case jsonType:
encoder = json.NewEncoder(&bytesBuffer)
}
encoder.Encode(response)
return bytesBuffer.Bytes()
}
// Write Object Header helper
func writeObjectHeaders(w http.ResponseWriter, metadata mstorage.ObjectMetadata) {
lastModified := metadata.Created.Format(time.RFC1123)
// write common headers
writeCommonHeaders(w, metadata.ContentType)
w.Header().Set("ETag", metadata.ETag)
w.Header().Set("Last-Modified", lastModified)
w.Header().Set("Content-Length", strconv.FormatInt(metadata.Size, 10))
w.Header().Set("Connection", "close")
}
func writeObjectHeadersAndResponse(w http.ResponseWriter, response interface{}, acceptsType contentType) []byte {
var bytesBuffer bytes.Buffer
var encoder encoder
// write common headers
writeCommonHeaders(w, getContentString(acceptsType))
switch acceptsType {
case xmlType:
encoder = xml.NewEncoder(&bytesBuffer)
case jsonType:
encoder = json.NewEncoder(&bytesBuffer)
}
w.Header().Set("Connection", "close")
encoder.Encode(response)
return bytesBuffer.Bytes()
}

@ -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"]
err := server.storage.StoreObject(bucket, object, "", req.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
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)
acceptsContentType := getContentType(req)
err := server.storage.StoreObject(bucket, object, "", req.Body)
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")
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

@ -46,6 +46,7 @@ func (s *MySuite) TestNonExistantObject(c *C) {
response, err := http.Get(testServer.URL + "/bucket/object")
c.Assert(err, IsNil)
c.Log(response.StatusCode)
c.Assert(response.StatusCode, Equals, http.StatusNotFound)
}

@ -21,6 +21,7 @@ import (
"crypto/sha256"
"fmt"
"io"
"log"
"sort"
"strings"
"time"
@ -51,6 +52,7 @@ func (storage *storage) CopyObjectToWriter(w io.Writer, bucket string, object st
if val, ok := storage.objectdata[key]; ok {
objectBuffer := bytes.NewBuffer(val.data)
written, err := io.Copy(w, objectBuffer)
log.Println("I am here")
return written, err
} else {
return 0, mstorage.ObjectNotFound{Bucket: bucket, Object: object}

Loading…
Cancel
Save