From ac4f07906cbd67011760e1149806df7c0db3663e Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Wed, 11 Feb 2015 03:23:15 -0800 Subject: [PATCH] Implement S3 Style ErrorCodes and Response This patchset also brings in lot of cleanup in terms of minioapi codebase --- pkg/api/minioapi/contenttype.go | 32 +++ pkg/api/minioapi/errors.go | 170 +++++++++++++++ pkg/api/minioapi/handler.go | 90 ++++++++ pkg/api/minioapi/headers.go | 61 ++++++ pkg/api/minioapi/minioapi.go | 346 +++++++++++++++--------------- pkg/api/minioapi/minioapi_test.go | 1 + pkg/storage/inmemory/inmemory.go | 2 + 7 files changed, 532 insertions(+), 170 deletions(-) create mode 100644 pkg/api/minioapi/contenttype.go create mode 100644 pkg/api/minioapi/errors.go create mode 100644 pkg/api/minioapi/handler.go create mode 100644 pkg/api/minioapi/headers.go diff --git a/pkg/api/minioapi/contenttype.go b/pkg/api/minioapi/contenttype.go new file mode 100644 index 000000000..819a53ded --- /dev/null +++ b/pkg/api/minioapi/contenttype.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] +} diff --git a/pkg/api/minioapi/errors.go b/pkg/api/minioapi/errors.go new file mode 100644 index 000000000..cfc17f105 --- /dev/null +++ b/pkg/api/minioapi/errors.go @@ -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 +} diff --git a/pkg/api/minioapi/handler.go b/pkg/api/minioapi/handler.go new file mode 100644 index 000000000..623c70513 --- /dev/null +++ b/pkg/api/minioapi/handler.go @@ -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 +} diff --git a/pkg/api/minioapi/headers.go b/pkg/api/minioapi/headers.go new file mode 100644 index 000000000..dfcd43a1c --- /dev/null +++ b/pkg/api/minioapi/headers.go @@ -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() +} diff --git a/pkg/api/minioapi/minioapi.go b/pkg/api/minioapi/minioapi.go index c619b8db2..b26e3281c 100644 --- a/pkg/api/minioapi/minioapi.go +++ b/pkg/api/minioapi/minioapi.go @@ -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 diff --git a/pkg/api/minioapi/minioapi_test.go b/pkg/api/minioapi/minioapi_test.go index d65751f3c..de0c37bee 100644 --- a/pkg/api/minioapi/minioapi_test.go +++ b/pkg/api/minioapi/minioapi_test.go @@ -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) } diff --git a/pkg/storage/inmemory/inmemory.go b/pkg/storage/inmemory/inmemory.go index b9b03e826..a1f63e058 100644 --- a/pkg/storage/inmemory/inmemory.go +++ b/pkg/storage/inmemory/inmemory.go @@ -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}