diff --git a/cmd/admin-handlers.go b/cmd/admin-handlers.go index f80ec826f..f3e34b8dc 100644 --- a/cmd/admin-handlers.go +++ b/cmd/admin-handlers.go @@ -35,19 +35,19 @@ const ( func (adminAPI adminAPIHandlers) ServiceStatusHandler(w http.ResponseWriter, r *http.Request) { adminAPIErr := checkRequestAuthType(r, "", "", "") if adminAPIErr != ErrNone { - writeErrorResponse(w, r, adminAPIErr, r.URL.Path) + writeErrorResponse(w, adminAPIErr, r.URL) return } storageInfo := newObjectLayerFn().StorageInfo() jsonBytes, err := json.Marshal(storageInfo) if err != nil { - writeErrorResponseNoHeader(w, r, ErrInternalError, r.URL.Path) + writeErrorResponse(w, ErrInternalError, r.URL) errorIf(err, "Failed to marshal storage info into json.") return } // Reply with storage information (across nodes in a // distributed setup) as json. - writeSuccessResponse(w, jsonBytes) + writeSuccessResponseJSON(w, jsonBytes) } // ServiceStopHandler - POST /?service @@ -58,11 +58,13 @@ func (adminAPI adminAPIHandlers) ServiceStatusHandler(w http.ResponseWriter, r * func (adminAPI adminAPIHandlers) ServiceStopHandler(w http.ResponseWriter, r *http.Request) { adminAPIErr := checkRequestAuthType(r, "", "", "") if adminAPIErr != ErrNone { - writeErrorResponse(w, r, adminAPIErr, r.URL.Path) + writeErrorResponse(w, adminAPIErr, r.URL) return } + // Reply to the client before stopping minio server. w.WriteHeader(http.StatusOK) + sendServiceCmd(globalAdminPeers, serviceStop) } @@ -74,11 +76,13 @@ func (adminAPI adminAPIHandlers) ServiceStopHandler(w http.ResponseWriter, r *ht func (adminAPI adminAPIHandlers) ServiceRestartHandler(w http.ResponseWriter, r *http.Request) { adminAPIErr := checkRequestAuthType(r, "", "", "") if adminAPIErr != ErrNone { - writeErrorResponse(w, r, adminAPIErr, r.URL.Path) + writeErrorResponse(w, adminAPIErr, r.URL) return } + // Reply to the client before restarting minio server. w.WriteHeader(http.StatusOK) + sendServiceCmd(globalAdminPeers, serviceRestart) } @@ -131,14 +135,14 @@ func validateLockQueryParams(vars url.Values) (string, string, time.Duration, AP func (adminAPI adminAPIHandlers) ListLocksHandler(w http.ResponseWriter, r *http.Request) { adminAPIErr := checkRequestAuthType(r, "", "", "") if adminAPIErr != ErrNone { - writeErrorResponse(w, r, adminAPIErr, r.URL.Path) + writeErrorResponse(w, adminAPIErr, r.URL) return } vars := r.URL.Query() bucket, prefix, relTime, adminAPIErr := validateLockQueryParams(vars) if adminAPIErr != ErrNone { - writeErrorResponse(w, r, adminAPIErr, r.URL.Path) + writeErrorResponse(w, adminAPIErr, r.URL) return } @@ -146,7 +150,7 @@ func (adminAPI adminAPIHandlers) ListLocksHandler(w http.ResponseWriter, r *http // are available since relTime. volLocks, err := listPeerLocksInfo(globalAdminPeers, bucket, prefix, relTime) if err != nil { - writeErrorResponse(w, r, ErrInternalError, r.URL.Path) + writeErrorResponse(w, ErrInternalError, r.URL) errorIf(err, "Failed to fetch lock information from remote nodes.") return } @@ -154,14 +158,14 @@ func (adminAPI adminAPIHandlers) ListLocksHandler(w http.ResponseWriter, r *http // Marshal list of locks as json. jsonBytes, err := json.Marshal(volLocks) if err != nil { - writeErrorResponseNoHeader(w, r, ErrInternalError, r.URL.Path) + writeErrorResponse(w, ErrInternalError, r.URL) errorIf(err, "Failed to marshal lock information into json.") return } // Reply with list of locks held on bucket, matching prefix // older than relTime supplied, as json. - writeSuccessResponse(w, jsonBytes) + writeSuccessResponseJSON(w, jsonBytes) } // ClearLocksHandler - POST /?lock&bucket=mybucket&prefix=myprefix&older-than=relTime @@ -173,14 +177,14 @@ func (adminAPI adminAPIHandlers) ListLocksHandler(w http.ResponseWriter, r *http func (adminAPI adminAPIHandlers) ClearLocksHandler(w http.ResponseWriter, r *http.Request) { adminAPIErr := checkRequestAuthType(r, "", "", "") if adminAPIErr != ErrNone { - writeErrorResponse(w, r, adminAPIErr, r.URL.Path) + writeErrorResponse(w, adminAPIErr, r.URL) return } vars := r.URL.Query() bucket, prefix, relTime, adminAPIErr := validateLockQueryParams(vars) if adminAPIErr != ErrNone { - writeErrorResponse(w, r, adminAPIErr, r.URL.Path) + writeErrorResponse(w, adminAPIErr, r.URL) return } @@ -188,7 +192,7 @@ func (adminAPI adminAPIHandlers) ClearLocksHandler(w http.ResponseWriter, r *htt // are available since relTime. volLocks, err := listPeerLocksInfo(globalAdminPeers, bucket, prefix, relTime) if err != nil { - writeErrorResponseNoHeader(w, r, ErrInternalError, r.URL.Path) + writeErrorResponse(w, ErrInternalError, r.URL) errorIf(err, "Failed to fetch lock information from remote nodes.") return } @@ -196,14 +200,16 @@ func (adminAPI adminAPIHandlers) ClearLocksHandler(w http.ResponseWriter, r *htt // Marshal list of locks as json. jsonBytes, err := json.Marshal(volLocks) if err != nil { - writeErrorResponseNoHeader(w, r, ErrInternalError, r.URL.Path) + writeErrorResponse(w, ErrInternalError, r.URL) errorIf(err, "Failed to marshal lock information into json.") return } + // Remove lock matching bucket/prefix older than relTime. for _, volLock := range volLocks { globalNSMutex.ForceUnlock(volLock.Bucket, volLock.Object) } + // Reply with list of locks cleared, as json. - writeSuccessResponse(w, jsonBytes) + writeSuccessResponseJSON(w, jsonBytes) } diff --git a/cmd/api-response.go b/cmd/api-response.go index 420e7a608..42d2730d4 100644 --- a/cmd/api-response.go +++ b/cmd/api-response.go @@ -19,6 +19,7 @@ package cmd import ( "encoding/xml" "net/http" + "net/url" "path" "time" ) @@ -482,51 +483,67 @@ func generateMultiDeleteResponse(quiet bool, deletedObjects []ObjectIdentifier, return deleteResp } -func writeResponse(w http.ResponseWriter, statusCode int, response []byte) { +func writeResponse(w http.ResponseWriter, statusCode int, response []byte, mType mimeType) { setCommonHeaders(w) - w.WriteHeader(statusCode) - if response == nil { - return + if mType != mimeNone { + w.Header().Set("Content-Type", string(mType)) + } + w.WriteHeader(statusCode) + if response != nil { + w.Write(response) + w.(http.Flusher).Flush() } - w.Write(response) - w.(http.Flusher).Flush() } -// writeSuccessResponse writes success headers and response if any. -func writeSuccessResponse(w http.ResponseWriter, response []byte) { - writeResponse(w, http.StatusOK, response) +// mimeType represents various MIME type used API responses. +type mimeType string + +const ( + // Means no response type. + mimeNone mimeType = "" + // Means response type is JSON. + mimeJSON mimeType = "application/json" + // Means response type is XML. + mimeXML mimeType = "application/xml" +) + +// writeSuccessResponseJSON writes success headers and response if any, +// with content-type set to `application/json`. +func writeSuccessResponseJSON(w http.ResponseWriter, response []byte) { + writeResponse(w, http.StatusOK, response, mimeJSON) +} + +// writeSuccessResponseXML writes success headers and response if any, +// with content-type set to `application/xml`. +func writeSuccessResponseXML(w http.ResponseWriter, response []byte) { + writeResponse(w, http.StatusOK, response, mimeXML) } // writeSuccessNoContent writes success headers with http status 204 func writeSuccessNoContent(w http.ResponseWriter) { - writeResponse(w, http.StatusNoContent, nil) + writeResponse(w, http.StatusNoContent, nil, mimeNone) } // writeRedirectSeeOther writes Location header with http status 303 func writeRedirectSeeOther(w http.ResponseWriter, location string) { w.Header().Set("Location", location) - writeResponse(w, http.StatusSeeOther, nil) + writeResponse(w, http.StatusSeeOther, nil, mimeNone) +} + +func writeSuccessResponseHeadersOnly(w http.ResponseWriter) { + writeResponse(w, http.StatusOK, nil, mimeNone) } // writeErrorRespone writes error headers -func writeErrorResponse(w http.ResponseWriter, req *http.Request, errorCode APIErrorCode, resource string) { - apiError := getAPIError(errorCode) - // set common headers - setCommonHeaders(w) - // write Header - w.WriteHeader(apiError.HTTPStatusCode) - writeErrorResponseNoHeader(w, req, errorCode, resource) -} - -func writeErrorResponseNoHeader(w http.ResponseWriter, req *http.Request, errorCode APIErrorCode, resource string) { +func writeErrorResponse(w http.ResponseWriter, errorCode APIErrorCode, reqURL *url.URL) { apiError := getAPIError(errorCode) // Generate error response. - errorResponse := getAPIErrorResponse(apiError, resource) + errorResponse := getAPIErrorResponse(apiError, reqURL.Path) encodedErrorResponse := encodeResponse(errorResponse) - // HEAD should have no body, do not attempt to write to it - if req.Method != "HEAD" { - // write error body - w.Write(encodedErrorResponse) - w.(http.Flusher).Flush() - } + writeResponse(w, apiError.HTTPStatusCode, encodedErrorResponse, mimeXML) +} + +func writeErrorResponseHeadersOnly(w http.ResponseWriter, errorCode APIErrorCode) { + apiError := getAPIError(errorCode) + writeResponse(w, apiError.HTTPStatusCode, nil, mimeNone) } diff --git a/cmd/auth-handler.go b/cmd/auth-handler.go index c048698bb..afb8330a8 100644 --- a/cmd/auth-handler.go +++ b/cmd/auth-handler.go @@ -231,5 +231,5 @@ func (a authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { a.handler.ServeHTTP(w, r) return } - writeErrorResponse(w, r, ErrSignatureVersionNotSupported, r.URL.Path) + writeErrorResponse(w, ErrSignatureVersionNotSupported, r.URL) } diff --git a/cmd/bucket-handlers-listobjects.go b/cmd/bucket-handlers-listobjects.go index 2bdbe9698..81db8b65c 100644 --- a/cmd/bucket-handlers-listobjects.go +++ b/cmd/bucket-handlers-listobjects.go @@ -66,12 +66,12 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, bucket, "s3:ListBucket", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -88,7 +88,7 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http // Validate the query params before beginning to serve the request. // fetch-owner is not validated since it is a boolean if s3Error := validateListObjectsArgs(prefix, marker, delimiter, maxKeys); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } // Inititate a list objects operation based on the input params. @@ -97,15 +97,14 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http listObjectsInfo, err := objectAPI.ListObjects(bucket, prefix, marker, delimiter, maxKeys) if err != nil { errorIf(err, "Unable to list objects.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } response := generateListObjectsV2Response(bucket, prefix, token, startAfter, delimiter, fetchOwner, maxKeys, listObjectsInfo) - // Write headers - setCommonHeaders(w) + // Write success response. - writeSuccessResponse(w, encodeResponse(response)) + writeSuccessResponseXML(w, encodeResponse(response)) } // ListObjectsV1Handler - GET Bucket (List Objects) Version 1. @@ -120,12 +119,12 @@ func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, bucket, "s3:ListBucket", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -134,7 +133,7 @@ func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http // Validate all the query params before beginning to serve the request. if s3Error := validateListObjectsArgs(prefix, marker, delimiter, maxKeys); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -144,12 +143,11 @@ func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http listObjectsInfo, err := objectAPI.ListObjects(bucket, prefix, marker, delimiter, maxKeys) if err != nil { errorIf(err, "Unable to list objects.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } response := generateListObjectsV1Response(bucket, prefix, marker, delimiter, maxKeys, listObjectsInfo) - // Write headers - setCommonHeaders(w) + // Write success response. - writeSuccessResponse(w, encodeResponse(response)) + writeSuccessResponseXML(w, encodeResponse(response)) } diff --git a/cmd/bucket-handlers.go b/cmd/bucket-handlers.go index 7a9e12b4b..5aa99885c 100644 --- a/cmd/bucket-handlers.go +++ b/cmd/bucket-handlers.go @@ -80,18 +80,18 @@ func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r * objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, bucket, "s3:GetBucketLocation", "us-east-1"); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } if _, err := objectAPI.GetBucketInfo(bucket); err != nil { errorIf(err, "Unable to fetch bucket info.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } @@ -104,8 +104,9 @@ func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r * Location: region, }) } - setCommonHeaders(w) // Write headers. - writeSuccessResponse(w, encodedSuccessResponse) + + // Write success response. + writeSuccessResponseXML(w, encodedSuccessResponse) } // ListMultipartUploadsHandler - GET Bucket (List Multipart uploads) @@ -122,24 +123,24 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter, objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, bucket, "s3:ListBucketMultipartUploads", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } prefix, keyMarker, uploadIDMarker, delimiter, maxUploads, _ := getBucketMultipartResources(r.URL.Query()) if maxUploads < 0 { - writeErrorResponse(w, r, ErrInvalidMaxUploads, r.URL.Path) + writeErrorResponse(w, ErrInvalidMaxUploads, r.URL) return } if keyMarker != "" { // Marker not common with prefix is not implemented. if !strings.HasPrefix(keyMarker, prefix) { - writeErrorResponse(w, r, ErrNotImplemented, r.URL.Path) + writeErrorResponse(w, ErrNotImplemented, r.URL) return } } @@ -147,16 +148,15 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter, listMultipartsInfo, err := objectAPI.ListMultipartUploads(bucket, prefix, keyMarker, uploadIDMarker, delimiter, maxUploads) if err != nil { errorIf(err, "Unable to list multipart uploads.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } // generate response response := generateListMultipartUploadsResponse(bucket, listMultipartsInfo) encodedSuccessResponse := encodeResponse(response) - // write headers. - setCommonHeaders(w) + // write success response. - writeSuccessResponse(w, encodedSuccessResponse) + writeSuccessResponseXML(w, encodedSuccessResponse) } // ListBucketsHandler - GET Service. @@ -166,7 +166,7 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter, func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.Request) { objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } @@ -177,24 +177,23 @@ func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.R s3Error = checkRequestAuthType(r, "", "", serverConfig.GetRegion()) } if s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } // Invoke the list buckets. bucketsInfo, err := objectAPI.ListBuckets() if err != nil { errorIf(err, "Unable to list buckets.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } // Generate response. response := generateListBucketsResponse(bucketsInfo) encodedSuccessResponse := encodeResponse(response) - // Write headers. - setCommonHeaders(w) + // Write response. - writeSuccessResponse(w, encodedSuccessResponse) + writeSuccessResponseXML(w, encodedSuccessResponse) } // DeleteMultipleObjectsHandler - deletes multiple objects. @@ -204,26 +203,26 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter, objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, bucket, "s3:DeleteObject", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } // Content-Length is required and should be non-zero // http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html if r.ContentLength <= 0 { - writeErrorResponse(w, r, ErrMissingContentLength, r.URL.Path) + writeErrorResponse(w, ErrMissingContentLength, r.URL) return } // Content-Md5 is requied should be set // http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html if _, ok := r.Header["Content-Md5"]; !ok { - writeErrorResponse(w, r, ErrMissingContentMD5, r.URL.Path) + writeErrorResponse(w, ErrMissingContentMD5, r.URL) return } @@ -233,7 +232,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter, // Read incoming body XML bytes. if _, err := io.ReadFull(r.Body, deleteXMLBytes); err != nil { errorIf(err, "Unable to read HTTP body.") - writeErrorResponse(w, r, ErrInternalError, r.URL.Path) + writeErrorResponse(w, ErrInternalError, r.URL) return } @@ -241,7 +240,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter, deleteObjects := &DeleteObjectsRequest{} if err := xml.Unmarshal(deleteXMLBytes, deleteObjects); err != nil { errorIf(err, "Unable to unmarshal delete objects request XML.") - writeErrorResponse(w, r, ErrMalformedXML, r.URL.Path) + writeErrorResponse(w, ErrMalformedXML, r.URL) return } @@ -289,10 +288,9 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter, // Generate response response := generateMultiDeleteResponse(deleteObjects.Quiet, deletedObjects, deleteErrors) encodedSuccessResponse := encodeResponse(response) - // Write headers - setCommonHeaders(w) + // Write success response. - writeSuccessResponse(w, encodedSuccessResponse) + writeSuccessResponseXML(w, encodedSuccessResponse) // Notify deleted event for objects. for _, dobj := range deletedObjects { @@ -315,13 +313,13 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter, func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Request) { objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } // PutBucket does not have any bucket action. if s3Error := checkRequestAuthType(r, "", "", "us-east-1"); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -331,7 +329,7 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req // Validate if incoming location constraint is valid, reject // requests which do not follow valid region requirements. if s3Error := isValidLocationConstraint(r); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -343,12 +341,14 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req err := objectAPI.MakeBucket(bucket) if err != nil { errorIf(err, "Unable to create a bucket.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } + // Make sure to add Location information here only for bucket w.Header().Set("Location", getLocation(r)) - writeSuccessResponse(w, nil) + + writeSuccessResponseHeadersOnly(w) } // PostPolicyBucketHandler - POST policy @@ -358,7 +358,7 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) { objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } @@ -367,14 +367,14 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h reader, err := r.MultipartReader() if err != nil { errorIf(err, "Unable to initialize multipart reader.") - writeErrorResponse(w, r, ErrMalformedPOSTRequest, r.URL.Path) + writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL) return } fileBody, fileName, formValues, err := extractPostPolicyFormValues(reader) if err != nil { errorIf(err, "Unable to parse form values.") - writeErrorResponse(w, r, ErrMalformedPOSTRequest, r.URL.Path) + writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL) return } bucket := mux.Vars(r)["bucket"] @@ -390,25 +390,25 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h // Verify policy signature. apiErr := doesPolicySignatureMatch(formValues) if apiErr != ErrNone { - writeErrorResponse(w, r, apiErr, r.URL.Path) + writeErrorResponse(w, apiErr, r.URL) return } policyBytes, err := base64.StdEncoding.DecodeString(formValues["Policy"]) if err != nil { - writeErrorResponse(w, r, ErrMalformedPOSTRequest, r.URL.Path) + writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL) return } postPolicyForm, err := parsePostPolicyForm(string(policyBytes)) if err != nil { - writeErrorResponse(w, r, ErrMalformedPOSTRequest, r.URL.Path) + writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL) return } // Make sure formValues adhere to policy restrictions. if apiErr = checkPostPolicy(formValues, postPolicyForm); apiErr != ErrNone { - writeErrorResponse(w, r, apiErr, r.URL.Path) + writeErrorResponse(w, apiErr, r.URL) return } @@ -442,7 +442,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h objInfo, err := objectAPI.PutObject(bucket, object, -1, fileBody, metadata, sha256sum) if err != nil { errorIf(err, "Unable to create object.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } w.Header().Set("ETag", "\""+objInfo.MD5Sum+"\"") @@ -471,10 +471,9 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h ETag: "\"" + objInfo.MD5Sum + "\"", Location: getObjectLocation(bucket, object), }) - writeResponse(w, http.StatusCreated, resp) - + writeResponse(w, http.StatusCreated, resp, "application/xml") case "200": - writeSuccessResponse(w, nil) + writeSuccessResponseHeadersOnly(w) default: writeSuccessNoContent(w) } @@ -504,12 +503,12 @@ func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Re objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponseHeadersOnly(w, ErrServerNotInitialized) return } if s3Error := checkRequestAuthType(r, bucket, "s3:ListBucket", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponseHeadersOnly(w, s3Error) return } @@ -519,23 +518,24 @@ func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Re if _, err := objectAPI.GetBucketInfo(bucket); err != nil { errorIf(err, "Unable to fetch bucket info.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponseHeadersOnly(w, toAPIErrorCode(err)) return } - writeSuccessResponse(w, nil) + + writeSuccessResponseHeadersOnly(w) } // DeleteBucketHandler - Delete bucket func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) { objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } // DeleteBucket does not have any bucket action. if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -549,7 +549,7 @@ func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http. // Attempt to delete bucket. if err := objectAPI.DeleteBucket(bucket); err != nil { errorIf(err, "Unable to delete a bucket.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } diff --git a/cmd/bucket-notification-handlers.go b/cmd/bucket-notification-handlers.go index b7d7414e5..fb9c026ef 100644 --- a/cmd/bucket-notification-handlers.go +++ b/cmd/bucket-notification-handlers.go @@ -42,12 +42,12 @@ const ( func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Request) { objAPI := api.ObjectAPI() if objAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -57,7 +57,7 @@ func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter, _, err := objAPI.GetBucketInfo(bucket) if err != nil { errorIf(err, "Unable to find bucket info.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } @@ -65,7 +65,7 @@ func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter, nConfig, err := loadNotificationConfig(bucket, objAPI) if err != nil && err != errNoSuchNotifications { errorIf(err, "Unable to read notification configuration.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } // For no notifications we write a dummy XML. @@ -77,11 +77,12 @@ func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter, if err != nil { // For any marshalling failure. errorIf(err, "Unable to marshal notification configuration into XML.", err) - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } + // Success. - writeSuccessResponse(w, notificationBytes) + writeSuccessResponseXML(w, notificationBytes) } // PutBucketNotificationHandler - Minio notification feature enables @@ -95,12 +96,12 @@ func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter, func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Request) { objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -110,7 +111,7 @@ func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter, _, err := objectAPI.GetBucketInfo(bucket) if err != nil { errorIf(err, "Unable to find bucket info.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } @@ -118,7 +119,7 @@ func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter, // always needs a Content-Length if incoming request is not chunked. if !contains(r.TransferEncoding, "chunked") { if r.ContentLength == -1 { - writeErrorResponse(w, r, ErrMissingContentLength, r.URL.Path) + writeErrorResponse(w, ErrMissingContentLength, r.URL) return } } @@ -132,7 +133,7 @@ func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter, } if err != nil { errorIf(err, "Unable to read incoming body.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } @@ -141,25 +142,25 @@ func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter, notificationConfigBytes := buffer.Bytes() if err = xml.Unmarshal(notificationConfigBytes, ¬ificationCfg); err != nil { errorIf(err, "Unable to parse notification configuration XML.") - writeErrorResponse(w, r, ErrMalformedXML, r.URL.Path) + writeErrorResponse(w, ErrMalformedXML, r.URL) return } // Successfully marshalled notification configuration. // Validate unmarshalled bucket notification configuration. if s3Error := validateNotificationConfig(notificationCfg); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } // Put bucket notification config. err = PutBucketNotificationConfig(bucket, ¬ificationCfg, objectAPI) if err != nil { - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } // Success. - writeSuccessResponse(w, nil) + writeSuccessResponseHeadersOnly(w) } // PutBucketNotificationConfig - Put a new notification config for a @@ -249,12 +250,12 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit // Validate if bucket exists. objAPI := api.ObjectAPI() if objAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -265,19 +266,19 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit prefixes, suffixes, events := getListenBucketNotificationResources(r.URL.Query()) if err := validateFilterValues(prefixes); err != ErrNone { - writeErrorResponse(w, r, err, r.URL.Path) + writeErrorResponse(w, err, r.URL) return } if err := validateFilterValues(suffixes); err != ErrNone { - writeErrorResponse(w, r, err, r.URL.Path) + writeErrorResponse(w, err, r.URL) return } // Validate all the resource events. for _, event := range events { if errCode := checkEvent(event); errCode != ErrNone { - writeErrorResponse(w, r, errCode, r.URL.Path) + writeErrorResponse(w, errCode, r.URL) return } } @@ -285,7 +286,7 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit _, err := objAPI.GetBucketInfo(bucket) if err != nil { errorIf(err, "Unable to get bucket info.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } @@ -338,7 +339,7 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit // Add channel for listener events if err = globalEventNotifier.AddListenerChan(accountARN, nEventCh); err != nil { errorIf(err, "Error adding a listener!") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } // Remove listener channel after the writer has closed or the @@ -355,7 +356,7 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit err = AddBucketListenerConfig(bucket, &lc, objAPI) if err != nil { - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } defer RemoveBucketListenerConfig(bucket, &lc, objAPI) diff --git a/cmd/bucket-policy-handlers.go b/cmd/bucket-policy-handlers.go index 50589059f..32dd0aac4 100644 --- a/cmd/bucket-policy-handlers.go +++ b/cmd/bucket-policy-handlers.go @@ -121,12 +121,12 @@ func bucketPolicyConditionMatch(conditions map[string]set.StringSet, statement p func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { objAPI := api.ObjectAPI() if objAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -137,7 +137,7 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht _, err := objAPI.GetBucketInfo(bucket) if err != nil { errorIf(err, "Unable to find bucket info.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } @@ -146,12 +146,12 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht // incoming request is not chunked. if !contains(r.TransferEncoding, "chunked") { if r.ContentLength == -1 || r.ContentLength == 0 { - writeErrorResponse(w, r, ErrMissingContentLength, r.URL.Path) + writeErrorResponse(w, ErrMissingContentLength, r.URL) return } // If Content-Length is greater than maximum allowed policy size. if r.ContentLength > maxAccessPolicySize { - writeErrorResponse(w, r, ErrEntityTooLarge, r.URL.Path) + writeErrorResponse(w, ErrEntityTooLarge, r.URL) return } } @@ -162,13 +162,13 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht policyBytes, err := ioutil.ReadAll(io.LimitReader(r.Body, maxAccessPolicySize)) if err != nil { errorIf(err, "Unable to read from client.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } // Parse validate and save bucket policy. if s3Error := parseAndPersistBucketPolicy(bucket, policyBytes, objAPI); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -183,12 +183,12 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { objAPI := api.ObjectAPI() if objAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -199,7 +199,7 @@ func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r _, err := objAPI.GetBucketInfo(bucket) if err != nil { errorIf(err, "Unable to find bucket info.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } @@ -208,9 +208,9 @@ func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r if err := persistAndNotifyBucketPolicyChange(bucket, policyChange{true, nil}, objAPI); err != nil { switch err.(type) { case BucketPolicyNotFound: - writeErrorResponse(w, r, ErrNoSuchBucketPolicy, r.URL.Path) + writeErrorResponse(w, ErrNoSuchBucketPolicy, r.URL) default: - writeErrorResponse(w, r, ErrInternalError, r.URL.Path) + writeErrorResponse(w, ErrInternalError, r.URL) } return } @@ -226,12 +226,12 @@ func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { objAPI := api.ObjectAPI() if objAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -242,7 +242,7 @@ func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *ht _, err := objAPI.GetBucketInfo(bucket) if err != nil { errorIf(err, "Unable to find bucket info.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } @@ -252,9 +252,9 @@ func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *ht errorIf(err, "Unable to read bucket policy.") switch err.(type) { case BucketPolicyNotFound: - writeErrorResponse(w, r, ErrNoSuchBucketPolicy, r.URL.Path) + writeErrorResponse(w, ErrNoSuchBucketPolicy, r.URL) default: - writeErrorResponse(w, r, ErrInternalError, r.URL.Path) + writeErrorResponse(w, ErrInternalError, r.URL) } return } diff --git a/cmd/generic-handlers.go b/cmd/generic-handlers.go index 6e17fbea9..2ce0a878f 100644 --- a/cmd/generic-handlers.go +++ b/cmd/generic-handlers.go @@ -169,7 +169,7 @@ func setPrivateBucketHandler(h http.Handler) http.Handler { func (h minioPrivateBucketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // For all non browser requests, reject access to 'reservedBucket'. if !guessIsBrowserReq(r) && path.Clean(r.URL.Path) == reservedBucket { - writeErrorResponse(w, r, ErrAllAccessDisabled, r.URL.Path) + writeErrorResponse(w, ErrAllAccessDisabled, r.URL) return } h.handler.ServeHTTP(w, r) @@ -231,14 +231,14 @@ func (h timeValidityHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // All our internal APIs are sensitive towards Date // header, for all requests where Date header is not // present we will reject such clients. - writeErrorResponse(w, r, apiErr, r.URL.Path) + writeErrorResponse(w, apiErr, r.URL) return } // Verify if the request date header is shifted by less than globalMaxSkewTime parameter in the past // or in the future, reject request otherwise. curTime := time.Now().UTC() if curTime.Sub(amzDate) > globalMaxSkewTime || amzDate.Sub(curTime) > globalMaxSkewTime { - writeErrorResponse(w, r, ErrRequestTimeTooSkewed, r.URL.Path) + writeErrorResponse(w, ErrRequestTimeTooSkewed, r.URL) return } } @@ -327,20 +327,20 @@ func (h resourceHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // If bucketName is present and not objectName check for bucket level resource queries. if bucketName != "" && objectName == "" { if ignoreNotImplementedBucketResources(r) { - writeErrorResponse(w, r, ErrNotImplemented, r.URL.Path) + writeErrorResponse(w, ErrNotImplemented, r.URL) return } } // If bucketName and objectName are present check for its resource queries. if bucketName != "" && objectName != "" { if ignoreNotImplementedObjectResources(r) { - writeErrorResponse(w, r, ErrNotImplemented, r.URL.Path) + writeErrorResponse(w, ErrNotImplemented, r.URL) return } } // A put method on path "/" doesn't make sense, ignore it. if r.Method == "PUT" && r.URL.Path == "/" { - writeErrorResponse(w, r, ErrNotImplemented, r.URL.Path) + writeErrorResponse(w, ErrNotImplemented, r.URL) return } diff --git a/cmd/object-handlers-common.go b/cmd/object-handlers-common.go index b79dd5783..46fbe1ec8 100644 --- a/cmd/object-handlers-common.go +++ b/cmd/object-handlers-common.go @@ -59,7 +59,7 @@ func checkCopyObjectPreconditions(w http.ResponseWriter, r *http.Request, objInf if !ifModifiedSince(objInfo.ModTime, ifModifiedSinceHeader) { // If the object is not modified since the specified time. writeHeaders() - writeErrorResponse(w, r, ErrPreconditionFailed, r.URL.Path) + writeErrorResponse(w, ErrPreconditionFailed, r.URL) return true } } @@ -71,7 +71,7 @@ func checkCopyObjectPreconditions(w http.ResponseWriter, r *http.Request, objInf if ifModifiedSince(objInfo.ModTime, ifUnmodifiedSinceHeader) { // If the object is modified since the specified time. writeHeaders() - writeErrorResponse(w, r, ErrPreconditionFailed, r.URL.Path) + writeErrorResponse(w, ErrPreconditionFailed, r.URL) return true } } @@ -83,7 +83,7 @@ func checkCopyObjectPreconditions(w http.ResponseWriter, r *http.Request, objInf if objInfo.MD5Sum != "" && !isETagEqual(objInfo.MD5Sum, ifMatchETagHeader) { // If the object ETag does not match with the specified ETag. writeHeaders() - writeErrorResponse(w, r, ErrPreconditionFailed, r.URL.Path) + writeErrorResponse(w, ErrPreconditionFailed, r.URL) return true } } @@ -95,7 +95,7 @@ func checkCopyObjectPreconditions(w http.ResponseWriter, r *http.Request, objInf if objInfo.MD5Sum != "" && isETagEqual(objInfo.MD5Sum, ifNoneMatchETagHeader) { // If the object ETag matches with the specified ETag. writeHeaders() - writeErrorResponse(w, r, ErrPreconditionFailed, r.URL.Path) + writeErrorResponse(w, ErrPreconditionFailed, r.URL) return true } } @@ -152,7 +152,7 @@ func checkPreconditions(w http.ResponseWriter, r *http.Request, objInfo ObjectIn if ifModifiedSince(objInfo.ModTime, ifUnmodifiedSinceHeader) { // If the object is modified since the specified time. writeHeaders() - writeErrorResponse(w, r, ErrPreconditionFailed, r.URL.Path) + writeErrorResponse(w, ErrPreconditionFailed, r.URL) return true } } @@ -164,7 +164,7 @@ func checkPreconditions(w http.ResponseWriter, r *http.Request, objInfo ObjectIn if !isETagEqual(objInfo.MD5Sum, ifMatchETagHeader) { // If the object ETag does not match with the specified ETag. writeHeaders() - writeErrorResponse(w, r, ErrPreconditionFailed, r.URL.Path) + writeErrorResponse(w, ErrPreconditionFailed, r.URL) return true } } diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index 87382c8a1..831ccf818 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -86,12 +86,12 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req // Fetch object stat info. objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, bucket, "s3:GetObject", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -107,7 +107,7 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req if apiErr == ErrNoSuchKey { apiErr = errAllowableObjectNotFound(bucket, r) } - writeErrorResponse(w, r, apiErr, r.URL.Path) + writeErrorResponse(w, apiErr, r.URL) return } @@ -119,14 +119,13 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req // Handle only errInvalidRange // Ignore other parse error and treat it as regular Get request like Amazon S3. if err == errInvalidRange { - writeErrorResponse(w, r, ErrInvalidRange, r.URL.Path) + writeErrorResponse(w, ErrInvalidRange, r.URL) return } // log the error. errorIf(err, "Invalid request range") } - } // Validate pre-conditions if any. @@ -166,8 +165,7 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req // partial data has already been written before an error // occurred then no point in setting StatusCode and // sending error XML. - apiErr := toAPIErrorCode(err) - writeErrorResponse(w, r, apiErr, r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) } return } @@ -190,12 +188,12 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponseHeadersOnly(w, ErrServerNotInitialized) return } if s3Error := checkRequestAuthType(r, bucket, "s3:GetObject", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponseHeadersOnly(w, s3Error) return } @@ -211,7 +209,7 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re if apiErr == ErrNoSuchKey { apiErr = errAllowableObjectNotFound(bucket, r) } - writeErrorResponse(w, r, apiErr, r.URL.Path) + writeErrorResponseHeadersOnly(w, apiErr) return } @@ -235,11 +233,13 @@ func getCpObjMetadataFromHeader(header http.Header, defaultMeta map[string]strin if isMetadataReplace(header) { return extractMetadataFromHeader(header) } + // if x-amz-metadata-directive says COPY then we // return the default metadata. if isMetadataCopy(header) { return defaultMeta } + // Copy is default behavior if not x-amz-metadata-directive is set. return defaultMeta } @@ -256,12 +256,12 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, dstBucket, "s3:PutObject", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -277,13 +277,13 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re srcBucket, srcObject := path2BucketAndObject(cpSrcPath) // If source object is empty or bucket is empty, reply back invalid copy source. if srcObject == "" || srcBucket == "" { - writeErrorResponse(w, r, ErrInvalidCopySource, r.URL.Path) + writeErrorResponse(w, ErrInvalidCopySource, r.URL) return } // Check if metadata directive is valid. if !isMetadataDirectiveValid(r.Header) { - writeErrorResponse(w, r, ErrInvalidMetadataDirective, r.URL.Path) + writeErrorResponse(w, ErrInvalidMetadataDirective, r.URL) return } @@ -311,7 +311,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re objInfo, err := objectAPI.GetObjectInfo(srcBucket, srcObject) if err != nil { errorIf(err, "Unable to fetch object info.") - writeErrorResponse(w, r, toAPIErrorCode(err), cpSrcPath) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } @@ -322,7 +322,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re /// maximum Upload size for object in a single CopyObject operation. if isMaxObjectSize(objInfo.Size) { - writeErrorResponse(w, r, ErrEntityTooLarge, cpSrcPath) + writeErrorResponse(w, ErrEntityTooLarge, r.URL) return } @@ -339,7 +339,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re if !isMetadataReplace(r.Header) && cpSrcDstSame { // If x-amz-metadata-directive is not set to REPLACE then we need // to error out if source and destination are same. - writeErrorResponse(w, r, ErrInvalidCopyDest, r.URL.Path) + writeErrorResponse(w, ErrInvalidCopyDest, r.URL) return } @@ -347,17 +347,16 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re // object is same then only metadata is updated. objInfo, err = objectAPI.CopyObject(srcBucket, srcObject, dstBucket, dstObject, newMetadata) if err != nil { - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } md5Sum := objInfo.MD5Sum response := generateCopyObjectResponse(md5Sum, objInfo.ModTime) encodedSuccessResponse := encodeResponse(response) - // write headers - setCommonHeaders(w) - // write success response. - writeSuccessResponse(w, encodedSuccessResponse) + + // Write success response. + writeSuccessResponseXML(w, encodedSuccessResponse) // Notify object created event. eventNotify(eventData{ @@ -376,13 +375,13 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Request) { objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } // X-Amz-Copy-Source shouldn't be set for this call. if _, ok := r.Header["X-Amz-Copy-Source"]; ok { - writeErrorResponse(w, r, ErrInvalidCopySource, r.URL.Path) + writeErrorResponse(w, ErrInvalidCopySource, r.URL) return } @@ -394,7 +393,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req md5Bytes, err := checkValidMD5(r.Header.Get("Content-Md5")) if err != nil { errorIf(err, "Unable to validate content-md5 format.") - writeErrorResponse(w, r, ErrInvalidDigest, r.URL.Path) + writeErrorResponse(w, ErrInvalidDigest, r.URL) return } @@ -406,18 +405,18 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req size, err = strconv.ParseInt(sizeStr, 10, 64) if err != nil { errorIf(err, "Unable to parse `x-amz-decoded-content-length` into its integer value", sizeStr) - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } } if size == -1 && !contains(r.TransferEncoding, "chunked") { - writeErrorResponse(w, r, ErrMissingContentLength, r.URL.Path) + writeErrorResponse(w, ErrMissingContentLength, r.URL) return } /// maximum Upload size for objects in a single operation if isMaxObjectSize(size) { - writeErrorResponse(w, r, ErrEntityTooLarge, r.URL.Path) + writeErrorResponse(w, ErrEntityTooLarge, r.URL) return } @@ -437,12 +436,12 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req switch rAuthType { default: // For all unknown auth types return error. - writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path) + writeErrorResponse(w, ErrAccessDenied, r.URL) return case authTypeAnonymous: // http://docs.aws.amazon.com/AmazonS3/latest/dev/using-with-s3-actions.html if s3Error := enforceBucketPolicy(bucket, "s3:PutObject", r.URL); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } // Create anonymous object. @@ -452,7 +451,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req reader, s3Error := newSignV4ChunkedReader(r) if s3Error != ErrNone { errorIf(errSignatureMismatch, dumpRequest(r)) - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } objInfo, err = objectAPI.PutObject(bucket, object, size, reader, metadata, sha256sum) @@ -460,14 +459,14 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req s3Error := isReqAuthenticatedV2(r) if s3Error != ErrNone { errorIf(errSignatureMismatch, dumpRequest(r)) - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } objInfo, err = objectAPI.PutObject(bucket, object, size, r.Body, metadata, sha256sum) case authTypePresigned, authTypeSigned: if s3Error := reqSignatureV4Verify(r); s3Error != ErrNone { errorIf(errSignatureMismatch, dumpRequest(r)) - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } if !skipContentSha256Cksum(r) { @@ -478,11 +477,11 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req } if err != nil { errorIf(err, "Unable to create an object.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } w.Header().Set("ETag", "\""+objInfo.MD5Sum+"\"") - writeSuccessResponse(w, nil) + writeSuccessResponseHeadersOnly(w) // Notify object created event. eventNotify(eventData{ @@ -506,12 +505,12 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, bucket, "s3:PutObject", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -521,16 +520,15 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r uploadID, err := objectAPI.NewMultipartUpload(bucket, object, metadata) if err != nil { errorIf(err, "Unable to initiate new multipart upload id.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } response := generateInitiateMultipartUploadResponse(bucket, object, uploadID) encodedSuccessResponse := encodeResponse(response) - // write headers - setCommonHeaders(w) - // write success response. - writeSuccessResponse(w, encodedSuccessResponse) + + // Write success response. + writeSuccessResponseXML(w, encodedSuccessResponse) } // PutObjectPartHandler - Upload part @@ -541,14 +539,14 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } // get Content-Md5 sent by client and verify if valid md5Bytes, err := checkValidMD5(r.Header.Get("Content-Md5")) if err != nil { - writeErrorResponse(w, r, ErrInvalidDigest, r.URL.Path) + writeErrorResponse(w, ErrInvalidDigest, r.URL) return } @@ -562,18 +560,18 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http size, err = strconv.ParseInt(sizeStr, 10, 64) if err != nil { errorIf(err, "Unable to parse `x-amz-decoded-content-length` into its integer value", sizeStr) - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } } if size == -1 { - writeErrorResponse(w, r, ErrMissingContentLength, r.URL.Path) + writeErrorResponse(w, ErrMissingContentLength, r.URL) return } /// maximum Upload size for multipart objects in a single operation if isMaxObjectSize(size) { - writeErrorResponse(w, r, ErrEntityTooLarge, r.URL.Path) + writeErrorResponse(w, ErrEntityTooLarge, r.URL) return } @@ -582,13 +580,13 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http partID, err := strconv.Atoi(partIDString) if err != nil { - writeErrorResponse(w, r, ErrInvalidPart, r.URL.Path) + writeErrorResponse(w, ErrInvalidPart, r.URL) return } // check partID with maximum part ID for multipart objects if isMaxPartID(partID) { - writeErrorResponse(w, r, ErrInvalidMaxParts, r.URL.Path) + writeErrorResponse(w, ErrInvalidMaxParts, r.URL) return } @@ -598,12 +596,12 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http switch rAuthType { default: // For all unknown auth types return error. - writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path) + writeErrorResponse(w, ErrAccessDenied, r.URL) return case authTypeAnonymous: // http://docs.aws.amazon.com/AmazonS3/latest/dev/mpuAndPermissions.html if s3Error := enforceBucketPolicy(bucket, "s3:PutObject", r.URL); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } // No need to verify signature, anonymous request access is already allowed. @@ -613,7 +611,7 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http reader, s3Error := newSignV4ChunkedReader(r) if s3Error != ErrNone { errorIf(errSignatureMismatch, dumpRequest(r)) - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } partMD5, err = objectAPI.PutObjectPart(bucket, object, uploadID, partID, size, reader, incomingMD5, sha256sum) @@ -621,14 +619,14 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http s3Error := isReqAuthenticatedV2(r) if s3Error != ErrNone { errorIf(errSignatureMismatch, dumpRequest(r)) - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } partMD5, err = objectAPI.PutObjectPart(bucket, object, uploadID, partID, size, r.Body, incomingMD5, sha256sum) case authTypePresigned, authTypeSigned: if s3Error := reqSignatureV4Verify(r); s3Error != ErrNone { errorIf(errSignatureMismatch, dumpRequest(r)) - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -640,13 +638,14 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http if err != nil { errorIf(err, "Unable to create object part.") // Verify if the underlying error is signature mismatch. - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } if partMD5 != "" { w.Header().Set("ETag", "\""+partMD5+"\"") } - writeSuccessResponse(w, nil) + + writeSuccessResponseHeadersOnly(w) } // AbortMultipartUploadHandler - Abort multipart upload @@ -657,19 +656,19 @@ func (api objectAPIHandlers) AbortMultipartUploadHandler(w http.ResponseWriter, objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, bucket, "s3:AbortMultipartUpload", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } uploadID, _, _, _ := getObjectResources(r.URL.Query()) if err := objectAPI.AbortMultipartUpload(bucket, object, uploadID); err != nil { errorIf(err, "Unable to abort multipart upload.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } writeSuccessNoContent(w) @@ -683,36 +682,35 @@ func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *ht objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, bucket, "s3:ListMultipartUploadParts", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } uploadID, partNumberMarker, maxParts, _ := getObjectResources(r.URL.Query()) if partNumberMarker < 0 { - writeErrorResponse(w, r, ErrInvalidPartNumberMarker, r.URL.Path) + writeErrorResponse(w, ErrInvalidPartNumberMarker, r.URL) return } if maxParts < 0 { - writeErrorResponse(w, r, ErrInvalidMaxParts, r.URL.Path) + writeErrorResponse(w, ErrInvalidMaxParts, r.URL) return } listPartsInfo, err := objectAPI.ListObjectParts(bucket, object, uploadID, partNumberMarker, maxParts) if err != nil { errorIf(err, "Unable to list uploaded parts.") - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } response := generateListPartsResponse(listPartsInfo) encodedSuccessResponse := encodeResponse(response) - // Write headers. - setCommonHeaders(w) + // Write success response. - writeSuccessResponse(w, encodedSuccessResponse) + writeSuccessResponseXML(w, encodedSuccessResponse) } // CompleteMultipartUploadHandler - Complete multipart upload. @@ -723,12 +721,12 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, bucket, "s3:PutObject", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return } @@ -739,23 +737,24 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite completeMultipartBytes, err := ioutil.ReadAll(r.Body) if err != nil { errorIf(err, "Unable to complete multipart upload.") - writeErrorResponse(w, r, ErrInternalError, r.URL.Path) + writeErrorResponse(w, ErrInternalError, r.URL) return } complMultipartUpload := &completeMultipartUpload{} if err = xml.Unmarshal(completeMultipartBytes, complMultipartUpload); err != nil { errorIf(err, "Unable to parse complete multipart upload XML.") - writeErrorResponse(w, r, ErrMalformedXML, r.URL.Path) + writeErrorResponse(w, ErrMalformedXML, r.URL) return } if len(complMultipartUpload.Parts) == 0 { - writeErrorResponse(w, r, ErrMalformedXML, r.URL.Path) + writeErrorResponse(w, ErrMalformedXML, r.URL) return } if !sort.IsSorted(completedParts(complMultipartUpload.Parts)) { - writeErrorResponse(w, r, ErrInvalidPartOrder, r.URL.Path) + writeErrorResponse(w, ErrInvalidPartOrder, r.URL) return } + // Complete parts. var completeParts []completePart for _, part := range complMultipartUpload.Parts { @@ -779,7 +778,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite writePartSmallErrorResponse(w, r, oErr) default: // Handle all other generic issues. - writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) + writeErrorResponse(w, toAPIErrorCode(err), r.URL) } return } @@ -791,7 +790,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite encodedSuccessResponse := encodeResponse(response) if err != nil { errorIf(err, "Unable to parse CompleteMultipartUpload response") - writeErrorResponseNoHeader(w, r, ErrInternalError, r.URL.Path) + writeErrorResponse(w, ErrInternalError, r.URL) return } @@ -799,8 +798,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite w.Header().Set("ETag", "\""+md5Sum+"\"") // Write success response. - w.Write(encodedSuccessResponse) - w.(http.Flusher).Flush() + writeSuccessResponseXML(w, encodedSuccessResponse) // Fetch object info for notifications. objInfo, err := objectAPI.GetObjectInfo(bucket, object) @@ -830,12 +828,12 @@ func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http. objectAPI := api.ObjectAPI() if objectAPI == nil { - writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) + writeErrorResponse(w, ErrServerNotInitialized, r.URL) return } if s3Error := checkRequestAuthType(r, bucket, "s3:DeleteObject", serverConfig.GetRegion()); s3Error != ErrNone { - writeErrorResponse(w, r, s3Error, r.URL.Path) + writeErrorResponse(w, s3Error, r.URL) return }