api: Set appropriate content-type for success/error responses. (#3537)

Golang HTTP client automatically detects content-type but
for S3 clients this content-type might be incorrect or
might misbehave.

For example:

```
Content-Type: text/xml; charset=utf-8
```

Should be
```
Content-Type: application/xml
```

Allow this to be set properly.
This commit is contained in:
Harshavardhana 2017-01-06 00:37:00 -08:00 committed by GitHub
parent c8f57133a4
commit 926c75d0b5
10 changed files with 260 additions and 240 deletions

View File

@ -35,19 +35,19 @@ const (
func (adminAPI adminAPIHandlers) ServiceStatusHandler(w http.ResponseWriter, r *http.Request) { func (adminAPI adminAPIHandlers) ServiceStatusHandler(w http.ResponseWriter, r *http.Request) {
adminAPIErr := checkRequestAuthType(r, "", "", "") adminAPIErr := checkRequestAuthType(r, "", "", "")
if adminAPIErr != ErrNone { if adminAPIErr != ErrNone {
writeErrorResponse(w, r, adminAPIErr, r.URL.Path) writeErrorResponse(w, adminAPIErr, r.URL)
return return
} }
storageInfo := newObjectLayerFn().StorageInfo() storageInfo := newObjectLayerFn().StorageInfo()
jsonBytes, err := json.Marshal(storageInfo) jsonBytes, err := json.Marshal(storageInfo)
if err != nil { if err != nil {
writeErrorResponseNoHeader(w, r, ErrInternalError, r.URL.Path) writeErrorResponse(w, ErrInternalError, r.URL)
errorIf(err, "Failed to marshal storage info into json.") errorIf(err, "Failed to marshal storage info into json.")
return return
} }
// Reply with storage information (across nodes in a // Reply with storage information (across nodes in a
// distributed setup) as json. // distributed setup) as json.
writeSuccessResponse(w, jsonBytes) writeSuccessResponseJSON(w, jsonBytes)
} }
// ServiceStopHandler - POST /?service // 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) { func (adminAPI adminAPIHandlers) ServiceStopHandler(w http.ResponseWriter, r *http.Request) {
adminAPIErr := checkRequestAuthType(r, "", "", "") adminAPIErr := checkRequestAuthType(r, "", "", "")
if adminAPIErr != ErrNone { if adminAPIErr != ErrNone {
writeErrorResponse(w, r, adminAPIErr, r.URL.Path) writeErrorResponse(w, adminAPIErr, r.URL)
return return
} }
// Reply to the client before stopping minio server. // Reply to the client before stopping minio server.
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
sendServiceCmd(globalAdminPeers, serviceStop) 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) { func (adminAPI adminAPIHandlers) ServiceRestartHandler(w http.ResponseWriter, r *http.Request) {
adminAPIErr := checkRequestAuthType(r, "", "", "") adminAPIErr := checkRequestAuthType(r, "", "", "")
if adminAPIErr != ErrNone { if adminAPIErr != ErrNone {
writeErrorResponse(w, r, adminAPIErr, r.URL.Path) writeErrorResponse(w, adminAPIErr, r.URL)
return return
} }
// Reply to the client before restarting minio server. // Reply to the client before restarting minio server.
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
sendServiceCmd(globalAdminPeers, serviceRestart) 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) { func (adminAPI adminAPIHandlers) ListLocksHandler(w http.ResponseWriter, r *http.Request) {
adminAPIErr := checkRequestAuthType(r, "", "", "") adminAPIErr := checkRequestAuthType(r, "", "", "")
if adminAPIErr != ErrNone { if adminAPIErr != ErrNone {
writeErrorResponse(w, r, adminAPIErr, r.URL.Path) writeErrorResponse(w, adminAPIErr, r.URL)
return return
} }
vars := r.URL.Query() vars := r.URL.Query()
bucket, prefix, relTime, adminAPIErr := validateLockQueryParams(vars) bucket, prefix, relTime, adminAPIErr := validateLockQueryParams(vars)
if adminAPIErr != ErrNone { if adminAPIErr != ErrNone {
writeErrorResponse(w, r, adminAPIErr, r.URL.Path) writeErrorResponse(w, adminAPIErr, r.URL)
return return
} }
@ -146,7 +150,7 @@ func (adminAPI adminAPIHandlers) ListLocksHandler(w http.ResponseWriter, r *http
// are available since relTime. // are available since relTime.
volLocks, err := listPeerLocksInfo(globalAdminPeers, bucket, prefix, relTime) volLocks, err := listPeerLocksInfo(globalAdminPeers, bucket, prefix, relTime)
if err != nil { 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.") errorIf(err, "Failed to fetch lock information from remote nodes.")
return return
} }
@ -154,14 +158,14 @@ func (adminAPI adminAPIHandlers) ListLocksHandler(w http.ResponseWriter, r *http
// Marshal list of locks as json. // Marshal list of locks as json.
jsonBytes, err := json.Marshal(volLocks) jsonBytes, err := json.Marshal(volLocks)
if err != nil { if err != nil {
writeErrorResponseNoHeader(w, r, ErrInternalError, r.URL.Path) writeErrorResponse(w, ErrInternalError, r.URL)
errorIf(err, "Failed to marshal lock information into json.") errorIf(err, "Failed to marshal lock information into json.")
return return
} }
// Reply with list of locks held on bucket, matching prefix // Reply with list of locks held on bucket, matching prefix
// older than relTime supplied, as json. // older than relTime supplied, as json.
writeSuccessResponse(w, jsonBytes) writeSuccessResponseJSON(w, jsonBytes)
} }
// ClearLocksHandler - POST /?lock&bucket=mybucket&prefix=myprefix&older-than=relTime // 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) { func (adminAPI adminAPIHandlers) ClearLocksHandler(w http.ResponseWriter, r *http.Request) {
adminAPIErr := checkRequestAuthType(r, "", "", "") adminAPIErr := checkRequestAuthType(r, "", "", "")
if adminAPIErr != ErrNone { if adminAPIErr != ErrNone {
writeErrorResponse(w, r, adminAPIErr, r.URL.Path) writeErrorResponse(w, adminAPIErr, r.URL)
return return
} }
vars := r.URL.Query() vars := r.URL.Query()
bucket, prefix, relTime, adminAPIErr := validateLockQueryParams(vars) bucket, prefix, relTime, adminAPIErr := validateLockQueryParams(vars)
if adminAPIErr != ErrNone { if adminAPIErr != ErrNone {
writeErrorResponse(w, r, adminAPIErr, r.URL.Path) writeErrorResponse(w, adminAPIErr, r.URL)
return return
} }
@ -188,7 +192,7 @@ func (adminAPI adminAPIHandlers) ClearLocksHandler(w http.ResponseWriter, r *htt
// are available since relTime. // are available since relTime.
volLocks, err := listPeerLocksInfo(globalAdminPeers, bucket, prefix, relTime) volLocks, err := listPeerLocksInfo(globalAdminPeers, bucket, prefix, relTime)
if err != nil { 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.") errorIf(err, "Failed to fetch lock information from remote nodes.")
return return
} }
@ -196,14 +200,16 @@ func (adminAPI adminAPIHandlers) ClearLocksHandler(w http.ResponseWriter, r *htt
// Marshal list of locks as json. // Marshal list of locks as json.
jsonBytes, err := json.Marshal(volLocks) jsonBytes, err := json.Marshal(volLocks)
if err != nil { if err != nil {
writeErrorResponseNoHeader(w, r, ErrInternalError, r.URL.Path) writeErrorResponse(w, ErrInternalError, r.URL)
errorIf(err, "Failed to marshal lock information into json.") errorIf(err, "Failed to marshal lock information into json.")
return return
} }
// Remove lock matching bucket/prefix older than relTime. // Remove lock matching bucket/prefix older than relTime.
for _, volLock := range volLocks { for _, volLock := range volLocks {
globalNSMutex.ForceUnlock(volLock.Bucket, volLock.Object) globalNSMutex.ForceUnlock(volLock.Bucket, volLock.Object)
} }
// Reply with list of locks cleared, as json. // Reply with list of locks cleared, as json.
writeSuccessResponse(w, jsonBytes) writeSuccessResponseJSON(w, jsonBytes)
} }

View File

@ -19,6 +19,7 @@ package cmd
import ( import (
"encoding/xml" "encoding/xml"
"net/http" "net/http"
"net/url"
"path" "path"
"time" "time"
) )
@ -482,51 +483,67 @@ func generateMultiDeleteResponse(quiet bool, deletedObjects []ObjectIdentifier,
return deleteResp return deleteResp
} }
func writeResponse(w http.ResponseWriter, statusCode int, response []byte) { func writeResponse(w http.ResponseWriter, statusCode int, response []byte, mType mimeType) {
setCommonHeaders(w) setCommonHeaders(w)
w.WriteHeader(statusCode) if mType != mimeNone {
if response == nil { w.Header().Set("Content-Type", string(mType))
return }
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. // mimeType represents various MIME type used API responses.
func writeSuccessResponse(w http.ResponseWriter, response []byte) { type mimeType string
writeResponse(w, http.StatusOK, response)
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 // writeSuccessNoContent writes success headers with http status 204
func writeSuccessNoContent(w http.ResponseWriter) { func writeSuccessNoContent(w http.ResponseWriter) {
writeResponse(w, http.StatusNoContent, nil) writeResponse(w, http.StatusNoContent, nil, mimeNone)
} }
// writeRedirectSeeOther writes Location header with http status 303 // writeRedirectSeeOther writes Location header with http status 303
func writeRedirectSeeOther(w http.ResponseWriter, location string) { func writeRedirectSeeOther(w http.ResponseWriter, location string) {
w.Header().Set("Location", location) 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 // writeErrorRespone writes error headers
func writeErrorResponse(w http.ResponseWriter, req *http.Request, errorCode APIErrorCode, resource string) { func writeErrorResponse(w http.ResponseWriter, errorCode APIErrorCode, reqURL *url.URL) {
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) {
apiError := getAPIError(errorCode) apiError := getAPIError(errorCode)
// Generate error response. // Generate error response.
errorResponse := getAPIErrorResponse(apiError, resource) errorResponse := getAPIErrorResponse(apiError, reqURL.Path)
encodedErrorResponse := encodeResponse(errorResponse) encodedErrorResponse := encodeResponse(errorResponse)
// HEAD should have no body, do not attempt to write to it writeResponse(w, apiError.HTTPStatusCode, encodedErrorResponse, mimeXML)
if req.Method != "HEAD" { }
// write error body
w.Write(encodedErrorResponse) func writeErrorResponseHeadersOnly(w http.ResponseWriter, errorCode APIErrorCode) {
w.(http.Flusher).Flush() apiError := getAPIError(errorCode)
} writeResponse(w, apiError.HTTPStatusCode, nil, mimeNone)
} }

View File

@ -231,5 +231,5 @@ func (a authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
a.handler.ServeHTTP(w, r) a.handler.ServeHTTP(w, r)
return return
} }
writeErrorResponse(w, r, ErrSignatureVersionNotSupported, r.URL.Path) writeErrorResponse(w, ErrSignatureVersionNotSupported, r.URL)
} }

View File

@ -66,12 +66,12 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, bucket, "s3:ListBucket", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, bucket, "s3:ListBucket", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -88,7 +88,7 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
// Validate the query params before beginning to serve the request. // Validate the query params before beginning to serve the request.
// fetch-owner is not validated since it is a boolean // fetch-owner is not validated since it is a boolean
if s3Error := validateListObjectsArgs(prefix, marker, delimiter, maxKeys); s3Error != ErrNone { if s3Error := validateListObjectsArgs(prefix, marker, delimiter, maxKeys); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
// Inititate a list objects operation based on the input params. // 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) listObjectsInfo, err := objectAPI.ListObjects(bucket, prefix, marker, delimiter, maxKeys)
if err != nil { if err != nil {
errorIf(err, "Unable to list objects.") errorIf(err, "Unable to list objects.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
response := generateListObjectsV2Response(bucket, prefix, token, startAfter, delimiter, fetchOwner, maxKeys, listObjectsInfo) response := generateListObjectsV2Response(bucket, prefix, token, startAfter, delimiter, fetchOwner, maxKeys, listObjectsInfo)
// Write headers
setCommonHeaders(w)
// Write success response. // Write success response.
writeSuccessResponse(w, encodeResponse(response)) writeSuccessResponseXML(w, encodeResponse(response))
} }
// ListObjectsV1Handler - GET Bucket (List Objects) Version 1. // ListObjectsV1Handler - GET Bucket (List Objects) Version 1.
@ -120,12 +119,12 @@ func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, bucket, "s3:ListBucket", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, bucket, "s3:ListBucket", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return 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. // Validate all the query params before beginning to serve the request.
if s3Error := validateListObjectsArgs(prefix, marker, delimiter, maxKeys); s3Error != ErrNone { if s3Error := validateListObjectsArgs(prefix, marker, delimiter, maxKeys); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -144,12 +143,11 @@ func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http
listObjectsInfo, err := objectAPI.ListObjects(bucket, prefix, marker, delimiter, maxKeys) listObjectsInfo, err := objectAPI.ListObjects(bucket, prefix, marker, delimiter, maxKeys)
if err != nil { if err != nil {
errorIf(err, "Unable to list objects.") errorIf(err, "Unable to list objects.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
response := generateListObjectsV1Response(bucket, prefix, marker, delimiter, maxKeys, listObjectsInfo) response := generateListObjectsV1Response(bucket, prefix, marker, delimiter, maxKeys, listObjectsInfo)
// Write headers
setCommonHeaders(w)
// Write success response. // Write success response.
writeSuccessResponse(w, encodeResponse(response)) writeSuccessResponseXML(w, encodeResponse(response))
} }

View File

@ -80,18 +80,18 @@ func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, bucket, "s3:GetBucketLocation", "us-east-1"); s3Error != ErrNone { 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 return
} }
if _, err := objectAPI.GetBucketInfo(bucket); err != nil { if _, err := objectAPI.GetBucketInfo(bucket); err != nil {
errorIf(err, "Unable to fetch bucket info.") errorIf(err, "Unable to fetch bucket info.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
@ -104,8 +104,9 @@ func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *
Location: region, Location: region,
}) })
} }
setCommonHeaders(w) // Write headers.
writeSuccessResponse(w, encodedSuccessResponse) // Write success response.
writeSuccessResponseXML(w, encodedSuccessResponse)
} }
// ListMultipartUploadsHandler - GET Bucket (List Multipart uploads) // ListMultipartUploadsHandler - GET Bucket (List Multipart uploads)
@ -122,24 +123,24 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter,
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, bucket, "s3:ListBucketMultipartUploads", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, bucket, "s3:ListBucketMultipartUploads", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
prefix, keyMarker, uploadIDMarker, delimiter, maxUploads, _ := getBucketMultipartResources(r.URL.Query()) prefix, keyMarker, uploadIDMarker, delimiter, maxUploads, _ := getBucketMultipartResources(r.URL.Query())
if maxUploads < 0 { if maxUploads < 0 {
writeErrorResponse(w, r, ErrInvalidMaxUploads, r.URL.Path) writeErrorResponse(w, ErrInvalidMaxUploads, r.URL)
return return
} }
if keyMarker != "" { if keyMarker != "" {
// Marker not common with prefix is not implemented. // Marker not common with prefix is not implemented.
if !strings.HasPrefix(keyMarker, prefix) { if !strings.HasPrefix(keyMarker, prefix) {
writeErrorResponse(w, r, ErrNotImplemented, r.URL.Path) writeErrorResponse(w, ErrNotImplemented, r.URL)
return return
} }
} }
@ -147,16 +148,15 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter,
listMultipartsInfo, err := objectAPI.ListMultipartUploads(bucket, prefix, keyMarker, uploadIDMarker, delimiter, maxUploads) listMultipartsInfo, err := objectAPI.ListMultipartUploads(bucket, prefix, keyMarker, uploadIDMarker, delimiter, maxUploads)
if err != nil { if err != nil {
errorIf(err, "Unable to list multipart uploads.") errorIf(err, "Unable to list multipart uploads.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
// generate response // generate response
response := generateListMultipartUploadsResponse(bucket, listMultipartsInfo) response := generateListMultipartUploadsResponse(bucket, listMultipartsInfo)
encodedSuccessResponse := encodeResponse(response) encodedSuccessResponse := encodeResponse(response)
// write headers.
setCommonHeaders(w)
// write success response. // write success response.
writeSuccessResponse(w, encodedSuccessResponse) writeSuccessResponseXML(w, encodedSuccessResponse)
} }
// ListBucketsHandler - GET Service. // ListBucketsHandler - GET Service.
@ -166,7 +166,7 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter,
func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
@ -177,24 +177,23 @@ func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.R
s3Error = checkRequestAuthType(r, "", "", serverConfig.GetRegion()) s3Error = checkRequestAuthType(r, "", "", serverConfig.GetRegion())
} }
if s3Error != ErrNone { if s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
// Invoke the list buckets. // Invoke the list buckets.
bucketsInfo, err := objectAPI.ListBuckets() bucketsInfo, err := objectAPI.ListBuckets()
if err != nil { if err != nil {
errorIf(err, "Unable to list buckets.") errorIf(err, "Unable to list buckets.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
// Generate response. // Generate response.
response := generateListBucketsResponse(bucketsInfo) response := generateListBucketsResponse(bucketsInfo)
encodedSuccessResponse := encodeResponse(response) encodedSuccessResponse := encodeResponse(response)
// Write headers.
setCommonHeaders(w)
// Write response. // Write response.
writeSuccessResponse(w, encodedSuccessResponse) writeSuccessResponseXML(w, encodedSuccessResponse)
} }
// DeleteMultipleObjectsHandler - deletes multiple objects. // DeleteMultipleObjectsHandler - deletes multiple objects.
@ -204,26 +203,26 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, bucket, "s3:DeleteObject", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, bucket, "s3:DeleteObject", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
// Content-Length is required and should be non-zero // Content-Length is required and should be non-zero
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html // http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
if r.ContentLength <= 0 { if r.ContentLength <= 0 {
writeErrorResponse(w, r, ErrMissingContentLength, r.URL.Path) writeErrorResponse(w, ErrMissingContentLength, r.URL)
return return
} }
// Content-Md5 is requied should be set // Content-Md5 is requied should be set
// http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html // http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html
if _, ok := r.Header["Content-Md5"]; !ok { if _, ok := r.Header["Content-Md5"]; !ok {
writeErrorResponse(w, r, ErrMissingContentMD5, r.URL.Path) writeErrorResponse(w, ErrMissingContentMD5, r.URL)
return return
} }
@ -233,7 +232,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
// Read incoming body XML bytes. // Read incoming body XML bytes.
if _, err := io.ReadFull(r.Body, deleteXMLBytes); err != nil { if _, err := io.ReadFull(r.Body, deleteXMLBytes); err != nil {
errorIf(err, "Unable to read HTTP body.") errorIf(err, "Unable to read HTTP body.")
writeErrorResponse(w, r, ErrInternalError, r.URL.Path) writeErrorResponse(w, ErrInternalError, r.URL)
return return
} }
@ -241,7 +240,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
deleteObjects := &DeleteObjectsRequest{} deleteObjects := &DeleteObjectsRequest{}
if err := xml.Unmarshal(deleteXMLBytes, deleteObjects); err != nil { if err := xml.Unmarshal(deleteXMLBytes, deleteObjects); err != nil {
errorIf(err, "Unable to unmarshal delete objects request XML.") errorIf(err, "Unable to unmarshal delete objects request XML.")
writeErrorResponse(w, r, ErrMalformedXML, r.URL.Path) writeErrorResponse(w, ErrMalformedXML, r.URL)
return return
} }
@ -289,10 +288,9 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
// Generate response // Generate response
response := generateMultiDeleteResponse(deleteObjects.Quiet, deletedObjects, deleteErrors) response := generateMultiDeleteResponse(deleteObjects.Quiet, deletedObjects, deleteErrors)
encodedSuccessResponse := encodeResponse(response) encodedSuccessResponse := encodeResponse(response)
// Write headers
setCommonHeaders(w)
// Write success response. // Write success response.
writeSuccessResponse(w, encodedSuccessResponse) writeSuccessResponseXML(w, encodedSuccessResponse)
// Notify deleted event for objects. // Notify deleted event for objects.
for _, dobj := range deletedObjects { 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) { func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Request) {
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
// PutBucket does not have any bucket action. // PutBucket does not have any bucket action.
if s3Error := checkRequestAuthType(r, "", "", "us-east-1"); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, "", "", "us-east-1"); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -331,7 +329,7 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req
// Validate if incoming location constraint is valid, reject // Validate if incoming location constraint is valid, reject
// requests which do not follow valid region requirements. // requests which do not follow valid region requirements.
if s3Error := isValidLocationConstraint(r); s3Error != ErrNone { if s3Error := isValidLocationConstraint(r); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -343,12 +341,14 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req
err := objectAPI.MakeBucket(bucket) err := objectAPI.MakeBucket(bucket)
if err != nil { if err != nil {
errorIf(err, "Unable to create a bucket.") errorIf(err, "Unable to create a bucket.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
// Make sure to add Location information here only for bucket // Make sure to add Location information here only for bucket
w.Header().Set("Location", getLocation(r)) w.Header().Set("Location", getLocation(r))
writeSuccessResponse(w, nil)
writeSuccessResponseHeadersOnly(w)
} }
// PostPolicyBucketHandler - POST policy // 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) { func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) {
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
@ -367,14 +367,14 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
reader, err := r.MultipartReader() reader, err := r.MultipartReader()
if err != nil { if err != nil {
errorIf(err, "Unable to initialize multipart reader.") errorIf(err, "Unable to initialize multipart reader.")
writeErrorResponse(w, r, ErrMalformedPOSTRequest, r.URL.Path) writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL)
return return
} }
fileBody, fileName, formValues, err := extractPostPolicyFormValues(reader) fileBody, fileName, formValues, err := extractPostPolicyFormValues(reader)
if err != nil { if err != nil {
errorIf(err, "Unable to parse form values.") errorIf(err, "Unable to parse form values.")
writeErrorResponse(w, r, ErrMalformedPOSTRequest, r.URL.Path) writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL)
return return
} }
bucket := mux.Vars(r)["bucket"] bucket := mux.Vars(r)["bucket"]
@ -390,25 +390,25 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
// Verify policy signature. // Verify policy signature.
apiErr := doesPolicySignatureMatch(formValues) apiErr := doesPolicySignatureMatch(formValues)
if apiErr != ErrNone { if apiErr != ErrNone {
writeErrorResponse(w, r, apiErr, r.URL.Path) writeErrorResponse(w, apiErr, r.URL)
return return
} }
policyBytes, err := base64.StdEncoding.DecodeString(formValues["Policy"]) policyBytes, err := base64.StdEncoding.DecodeString(formValues["Policy"])
if err != nil { if err != nil {
writeErrorResponse(w, r, ErrMalformedPOSTRequest, r.URL.Path) writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL)
return return
} }
postPolicyForm, err := parsePostPolicyForm(string(policyBytes)) postPolicyForm, err := parsePostPolicyForm(string(policyBytes))
if err != nil { if err != nil {
writeErrorResponse(w, r, ErrMalformedPOSTRequest, r.URL.Path) writeErrorResponse(w, ErrMalformedPOSTRequest, r.URL)
return return
} }
// Make sure formValues adhere to policy restrictions. // Make sure formValues adhere to policy restrictions.
if apiErr = checkPostPolicy(formValues, postPolicyForm); apiErr != ErrNone { if apiErr = checkPostPolicy(formValues, postPolicyForm); apiErr != ErrNone {
writeErrorResponse(w, r, apiErr, r.URL.Path) writeErrorResponse(w, apiErr, r.URL)
return return
} }
@ -442,7 +442,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
objInfo, err := objectAPI.PutObject(bucket, object, -1, fileBody, metadata, sha256sum) objInfo, err := objectAPI.PutObject(bucket, object, -1, fileBody, metadata, sha256sum)
if err != nil { if err != nil {
errorIf(err, "Unable to create object.") errorIf(err, "Unable to create object.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
w.Header().Set("ETag", "\""+objInfo.MD5Sum+"\"") w.Header().Set("ETag", "\""+objInfo.MD5Sum+"\"")
@ -471,10 +471,9 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
ETag: "\"" + objInfo.MD5Sum + "\"", ETag: "\"" + objInfo.MD5Sum + "\"",
Location: getObjectLocation(bucket, object), Location: getObjectLocation(bucket, object),
}) })
writeResponse(w, http.StatusCreated, resp) writeResponse(w, http.StatusCreated, resp, "application/xml")
case "200": case "200":
writeSuccessResponse(w, nil) writeSuccessResponseHeadersOnly(w)
default: default:
writeSuccessNoContent(w) writeSuccessNoContent(w)
} }
@ -504,12 +503,12 @@ func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Re
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponseHeadersOnly(w, ErrServerNotInitialized)
return return
} }
if s3Error := checkRequestAuthType(r, bucket, "s3:ListBucket", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, bucket, "s3:ListBucket", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponseHeadersOnly(w, s3Error)
return return
} }
@ -519,23 +518,24 @@ func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Re
if _, err := objectAPI.GetBucketInfo(bucket); err != nil { if _, err := objectAPI.GetBucketInfo(bucket); err != nil {
errorIf(err, "Unable to fetch bucket info.") errorIf(err, "Unable to fetch bucket info.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponseHeadersOnly(w, toAPIErrorCode(err))
return return
} }
writeSuccessResponse(w, nil)
writeSuccessResponseHeadersOnly(w)
} }
// DeleteBucketHandler - Delete bucket // DeleteBucketHandler - Delete bucket
func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
// DeleteBucket does not have any bucket action. // DeleteBucket does not have any bucket action.
if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -549,7 +549,7 @@ func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.
// Attempt to delete bucket. // Attempt to delete bucket.
if err := objectAPI.DeleteBucket(bucket); err != nil { if err := objectAPI.DeleteBucket(bucket); err != nil {
errorIf(err, "Unable to delete a bucket.") errorIf(err, "Unable to delete a bucket.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }

View File

@ -42,12 +42,12 @@ const (
func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
objAPI := api.ObjectAPI() objAPI := api.ObjectAPI()
if objAPI == nil { if objAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -57,7 +57,7 @@ func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter,
_, err := objAPI.GetBucketInfo(bucket) _, err := objAPI.GetBucketInfo(bucket)
if err != nil { if err != nil {
errorIf(err, "Unable to find bucket info.") errorIf(err, "Unable to find bucket info.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
@ -65,7 +65,7 @@ func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter,
nConfig, err := loadNotificationConfig(bucket, objAPI) nConfig, err := loadNotificationConfig(bucket, objAPI)
if err != nil && err != errNoSuchNotifications { if err != nil && err != errNoSuchNotifications {
errorIf(err, "Unable to read notification configuration.") errorIf(err, "Unable to read notification configuration.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
// For no notifications we write a dummy XML. // For no notifications we write a dummy XML.
@ -77,11 +77,12 @@ func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter,
if err != nil { if err != nil {
// For any marshalling failure. // For any marshalling failure.
errorIf(err, "Unable to marshal notification configuration into XML.", err) 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 return
} }
// Success. // Success.
writeSuccessResponse(w, notificationBytes) writeSuccessResponseXML(w, notificationBytes)
} }
// PutBucketNotificationHandler - Minio notification feature enables // 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) { func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -110,7 +111,7 @@ func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter,
_, err := objectAPI.GetBucketInfo(bucket) _, err := objectAPI.GetBucketInfo(bucket)
if err != nil { if err != nil {
errorIf(err, "Unable to find bucket info.") errorIf(err, "Unable to find bucket info.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
@ -118,7 +119,7 @@ func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter,
// always needs a Content-Length if incoming request is not chunked. // always needs a Content-Length if incoming request is not chunked.
if !contains(r.TransferEncoding, "chunked") { if !contains(r.TransferEncoding, "chunked") {
if r.ContentLength == -1 { if r.ContentLength == -1 {
writeErrorResponse(w, r, ErrMissingContentLength, r.URL.Path) writeErrorResponse(w, ErrMissingContentLength, r.URL)
return return
} }
} }
@ -132,7 +133,7 @@ func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter,
} }
if err != nil { if err != nil {
errorIf(err, "Unable to read incoming body.") errorIf(err, "Unable to read incoming body.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
@ -141,25 +142,25 @@ func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter,
notificationConfigBytes := buffer.Bytes() notificationConfigBytes := buffer.Bytes()
if err = xml.Unmarshal(notificationConfigBytes, &notificationCfg); err != nil { if err = xml.Unmarshal(notificationConfigBytes, &notificationCfg); err != nil {
errorIf(err, "Unable to parse notification configuration XML.") errorIf(err, "Unable to parse notification configuration XML.")
writeErrorResponse(w, r, ErrMalformedXML, r.URL.Path) writeErrorResponse(w, ErrMalformedXML, r.URL)
return return
} // Successfully marshalled notification configuration. } // Successfully marshalled notification configuration.
// Validate unmarshalled bucket notification configuration. // Validate unmarshalled bucket notification configuration.
if s3Error := validateNotificationConfig(notificationCfg); s3Error != ErrNone { if s3Error := validateNotificationConfig(notificationCfg); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
// Put bucket notification config. // Put bucket notification config.
err = PutBucketNotificationConfig(bucket, &notificationCfg, objectAPI) err = PutBucketNotificationConfig(bucket, &notificationCfg, objectAPI)
if err != nil { if err != nil {
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
// Success. // Success.
writeSuccessResponse(w, nil) writeSuccessResponseHeadersOnly(w)
} }
// PutBucketNotificationConfig - Put a new notification config for a // PutBucketNotificationConfig - Put a new notification config for a
@ -249,12 +250,12 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
// Validate if bucket exists. // Validate if bucket exists.
objAPI := api.ObjectAPI() objAPI := api.ObjectAPI()
if objAPI == nil { if objAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -265,19 +266,19 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
prefixes, suffixes, events := getListenBucketNotificationResources(r.URL.Query()) prefixes, suffixes, events := getListenBucketNotificationResources(r.URL.Query())
if err := validateFilterValues(prefixes); err != ErrNone { if err := validateFilterValues(prefixes); err != ErrNone {
writeErrorResponse(w, r, err, r.URL.Path) writeErrorResponse(w, err, r.URL)
return return
} }
if err := validateFilterValues(suffixes); err != ErrNone { if err := validateFilterValues(suffixes); err != ErrNone {
writeErrorResponse(w, r, err, r.URL.Path) writeErrorResponse(w, err, r.URL)
return return
} }
// Validate all the resource events. // Validate all the resource events.
for _, event := range events { for _, event := range events {
if errCode := checkEvent(event); errCode != ErrNone { if errCode := checkEvent(event); errCode != ErrNone {
writeErrorResponse(w, r, errCode, r.URL.Path) writeErrorResponse(w, errCode, r.URL)
return return
} }
} }
@ -285,7 +286,7 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
_, err := objAPI.GetBucketInfo(bucket) _, err := objAPI.GetBucketInfo(bucket)
if err != nil { if err != nil {
errorIf(err, "Unable to get bucket info.") errorIf(err, "Unable to get bucket info.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
@ -338,7 +339,7 @@ func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWrit
// Add channel for listener events // Add channel for listener events
if err = globalEventNotifier.AddListenerChan(accountARN, nEventCh); err != nil { if err = globalEventNotifier.AddListenerChan(accountARN, nEventCh); err != nil {
errorIf(err, "Error adding a listener!") errorIf(err, "Error adding a listener!")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
// Remove listener channel after the writer has closed or the // 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) err = AddBucketListenerConfig(bucket, &lc, objAPI)
if err != nil { if err != nil {
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
defer RemoveBucketListenerConfig(bucket, &lc, objAPI) defer RemoveBucketListenerConfig(bucket, &lc, objAPI)

View File

@ -121,12 +121,12 @@ func bucketPolicyConditionMatch(conditions map[string]set.StringSet, statement p
func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
objAPI := api.ObjectAPI() objAPI := api.ObjectAPI()
if objAPI == nil { if objAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -137,7 +137,7 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht
_, err := objAPI.GetBucketInfo(bucket) _, err := objAPI.GetBucketInfo(bucket)
if err != nil { if err != nil {
errorIf(err, "Unable to find bucket info.") errorIf(err, "Unable to find bucket info.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
@ -146,12 +146,12 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht
// incoming request is not chunked. // incoming request is not chunked.
if !contains(r.TransferEncoding, "chunked") { if !contains(r.TransferEncoding, "chunked") {
if r.ContentLength == -1 || r.ContentLength == 0 { if r.ContentLength == -1 || r.ContentLength == 0 {
writeErrorResponse(w, r, ErrMissingContentLength, r.URL.Path) writeErrorResponse(w, ErrMissingContentLength, r.URL)
return return
} }
// If Content-Length is greater than maximum allowed policy size. // If Content-Length is greater than maximum allowed policy size.
if r.ContentLength > maxAccessPolicySize { if r.ContentLength > maxAccessPolicySize {
writeErrorResponse(w, r, ErrEntityTooLarge, r.URL.Path) writeErrorResponse(w, ErrEntityTooLarge, r.URL)
return return
} }
} }
@ -162,13 +162,13 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht
policyBytes, err := ioutil.ReadAll(io.LimitReader(r.Body, maxAccessPolicySize)) policyBytes, err := ioutil.ReadAll(io.LimitReader(r.Body, maxAccessPolicySize))
if err != nil { if err != nil {
errorIf(err, "Unable to read from client.") errorIf(err, "Unable to read from client.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
// Parse validate and save bucket policy. // Parse validate and save bucket policy.
if s3Error := parseAndPersistBucketPolicy(bucket, policyBytes, objAPI); s3Error != ErrNone { if s3Error := parseAndPersistBucketPolicy(bucket, policyBytes, objAPI); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -183,12 +183,12 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht
func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
objAPI := api.ObjectAPI() objAPI := api.ObjectAPI()
if objAPI == nil { if objAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -199,7 +199,7 @@ func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r
_, err := objAPI.GetBucketInfo(bucket) _, err := objAPI.GetBucketInfo(bucket)
if err != nil { if err != nil {
errorIf(err, "Unable to find bucket info.") errorIf(err, "Unable to find bucket info.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
@ -208,9 +208,9 @@ func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r
if err := persistAndNotifyBucketPolicyChange(bucket, policyChange{true, nil}, objAPI); err != nil { if err := persistAndNotifyBucketPolicyChange(bucket, policyChange{true, nil}, objAPI); err != nil {
switch err.(type) { switch err.(type) {
case BucketPolicyNotFound: case BucketPolicyNotFound:
writeErrorResponse(w, r, ErrNoSuchBucketPolicy, r.URL.Path) writeErrorResponse(w, ErrNoSuchBucketPolicy, r.URL)
default: default:
writeErrorResponse(w, r, ErrInternalError, r.URL.Path) writeErrorResponse(w, ErrInternalError, r.URL)
} }
return return
} }
@ -226,12 +226,12 @@ func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r
func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
objAPI := api.ObjectAPI() objAPI := api.ObjectAPI()
if objAPI == nil { if objAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, "", "", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -242,7 +242,7 @@ func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *ht
_, err := objAPI.GetBucketInfo(bucket) _, err := objAPI.GetBucketInfo(bucket)
if err != nil { if err != nil {
errorIf(err, "Unable to find bucket info.") errorIf(err, "Unable to find bucket info.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
@ -252,9 +252,9 @@ func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *ht
errorIf(err, "Unable to read bucket policy.") errorIf(err, "Unable to read bucket policy.")
switch err.(type) { switch err.(type) {
case BucketPolicyNotFound: case BucketPolicyNotFound:
writeErrorResponse(w, r, ErrNoSuchBucketPolicy, r.URL.Path) writeErrorResponse(w, ErrNoSuchBucketPolicy, r.URL)
default: default:
writeErrorResponse(w, r, ErrInternalError, r.URL.Path) writeErrorResponse(w, ErrInternalError, r.URL)
} }
return return
} }

View File

@ -169,7 +169,7 @@ func setPrivateBucketHandler(h http.Handler) http.Handler {
func (h minioPrivateBucketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h minioPrivateBucketHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// For all non browser requests, reject access to 'reservedBucket'. // For all non browser requests, reject access to 'reservedBucket'.
if !guessIsBrowserReq(r) && path.Clean(r.URL.Path) == reservedBucket { if !guessIsBrowserReq(r) && path.Clean(r.URL.Path) == reservedBucket {
writeErrorResponse(w, r, ErrAllAccessDisabled, r.URL.Path) writeErrorResponse(w, ErrAllAccessDisabled, r.URL)
return return
} }
h.handler.ServeHTTP(w, r) 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 // All our internal APIs are sensitive towards Date
// header, for all requests where Date header is not // header, for all requests where Date header is not
// present we will reject such clients. // present we will reject such clients.
writeErrorResponse(w, r, apiErr, r.URL.Path) writeErrorResponse(w, apiErr, r.URL)
return return
} }
// Verify if the request date header is shifted by less than globalMaxSkewTime parameter in the past // Verify if the request date header is shifted by less than globalMaxSkewTime parameter in the past
// or in the future, reject request otherwise. // or in the future, reject request otherwise.
curTime := time.Now().UTC() curTime := time.Now().UTC()
if curTime.Sub(amzDate) > globalMaxSkewTime || amzDate.Sub(curTime) > globalMaxSkewTime { if curTime.Sub(amzDate) > globalMaxSkewTime || amzDate.Sub(curTime) > globalMaxSkewTime {
writeErrorResponse(w, r, ErrRequestTimeTooSkewed, r.URL.Path) writeErrorResponse(w, ErrRequestTimeTooSkewed, r.URL)
return 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 is present and not objectName check for bucket level resource queries.
if bucketName != "" && objectName == "" { if bucketName != "" && objectName == "" {
if ignoreNotImplementedBucketResources(r) { if ignoreNotImplementedBucketResources(r) {
writeErrorResponse(w, r, ErrNotImplemented, r.URL.Path) writeErrorResponse(w, ErrNotImplemented, r.URL)
return return
} }
} }
// If bucketName and objectName are present check for its resource queries. // If bucketName and objectName are present check for its resource queries.
if bucketName != "" && objectName != "" { if bucketName != "" && objectName != "" {
if ignoreNotImplementedObjectResources(r) { if ignoreNotImplementedObjectResources(r) {
writeErrorResponse(w, r, ErrNotImplemented, r.URL.Path) writeErrorResponse(w, ErrNotImplemented, r.URL)
return return
} }
} }
// A put method on path "/" doesn't make sense, ignore it. // A put method on path "/" doesn't make sense, ignore it.
if r.Method == "PUT" && r.URL.Path == "/" { if r.Method == "PUT" && r.URL.Path == "/" {
writeErrorResponse(w, r, ErrNotImplemented, r.URL.Path) writeErrorResponse(w, ErrNotImplemented, r.URL)
return return
} }

View File

@ -59,7 +59,7 @@ func checkCopyObjectPreconditions(w http.ResponseWriter, r *http.Request, objInf
if !ifModifiedSince(objInfo.ModTime, ifModifiedSinceHeader) { if !ifModifiedSince(objInfo.ModTime, ifModifiedSinceHeader) {
// If the object is not modified since the specified time. // If the object is not modified since the specified time.
writeHeaders() writeHeaders()
writeErrorResponse(w, r, ErrPreconditionFailed, r.URL.Path) writeErrorResponse(w, ErrPreconditionFailed, r.URL)
return true return true
} }
} }
@ -71,7 +71,7 @@ func checkCopyObjectPreconditions(w http.ResponseWriter, r *http.Request, objInf
if ifModifiedSince(objInfo.ModTime, ifUnmodifiedSinceHeader) { if ifModifiedSince(objInfo.ModTime, ifUnmodifiedSinceHeader) {
// If the object is modified since the specified time. // If the object is modified since the specified time.
writeHeaders() writeHeaders()
writeErrorResponse(w, r, ErrPreconditionFailed, r.URL.Path) writeErrorResponse(w, ErrPreconditionFailed, r.URL)
return true return true
} }
} }
@ -83,7 +83,7 @@ func checkCopyObjectPreconditions(w http.ResponseWriter, r *http.Request, objInf
if objInfo.MD5Sum != "" && !isETagEqual(objInfo.MD5Sum, ifMatchETagHeader) { if objInfo.MD5Sum != "" && !isETagEqual(objInfo.MD5Sum, ifMatchETagHeader) {
// If the object ETag does not match with the specified ETag. // If the object ETag does not match with the specified ETag.
writeHeaders() writeHeaders()
writeErrorResponse(w, r, ErrPreconditionFailed, r.URL.Path) writeErrorResponse(w, ErrPreconditionFailed, r.URL)
return true return true
} }
} }
@ -95,7 +95,7 @@ func checkCopyObjectPreconditions(w http.ResponseWriter, r *http.Request, objInf
if objInfo.MD5Sum != "" && isETagEqual(objInfo.MD5Sum, ifNoneMatchETagHeader) { if objInfo.MD5Sum != "" && isETagEqual(objInfo.MD5Sum, ifNoneMatchETagHeader) {
// If the object ETag matches with the specified ETag. // If the object ETag matches with the specified ETag.
writeHeaders() writeHeaders()
writeErrorResponse(w, r, ErrPreconditionFailed, r.URL.Path) writeErrorResponse(w, ErrPreconditionFailed, r.URL)
return true return true
} }
} }
@ -152,7 +152,7 @@ func checkPreconditions(w http.ResponseWriter, r *http.Request, objInfo ObjectIn
if ifModifiedSince(objInfo.ModTime, ifUnmodifiedSinceHeader) { if ifModifiedSince(objInfo.ModTime, ifUnmodifiedSinceHeader) {
// If the object is modified since the specified time. // If the object is modified since the specified time.
writeHeaders() writeHeaders()
writeErrorResponse(w, r, ErrPreconditionFailed, r.URL.Path) writeErrorResponse(w, ErrPreconditionFailed, r.URL)
return true return true
} }
} }
@ -164,7 +164,7 @@ func checkPreconditions(w http.ResponseWriter, r *http.Request, objInfo ObjectIn
if !isETagEqual(objInfo.MD5Sum, ifMatchETagHeader) { if !isETagEqual(objInfo.MD5Sum, ifMatchETagHeader) {
// If the object ETag does not match with the specified ETag. // If the object ETag does not match with the specified ETag.
writeHeaders() writeHeaders()
writeErrorResponse(w, r, ErrPreconditionFailed, r.URL.Path) writeErrorResponse(w, ErrPreconditionFailed, r.URL)
return true return true
} }
} }

View File

@ -86,12 +86,12 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
// Fetch object stat info. // Fetch object stat info.
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, bucket, "s3:GetObject", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, bucket, "s3:GetObject", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -107,7 +107,7 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
if apiErr == ErrNoSuchKey { if apiErr == ErrNoSuchKey {
apiErr = errAllowableObjectNotFound(bucket, r) apiErr = errAllowableObjectNotFound(bucket, r)
} }
writeErrorResponse(w, r, apiErr, r.URL.Path) writeErrorResponse(w, apiErr, r.URL)
return return
} }
@ -119,14 +119,13 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
// Handle only errInvalidRange // Handle only errInvalidRange
// Ignore other parse error and treat it as regular Get request like Amazon S3. // Ignore other parse error and treat it as regular Get request like Amazon S3.
if err == errInvalidRange { if err == errInvalidRange {
writeErrorResponse(w, r, ErrInvalidRange, r.URL.Path) writeErrorResponse(w, ErrInvalidRange, r.URL)
return return
} }
// log the error. // log the error.
errorIf(err, "Invalid request range") errorIf(err, "Invalid request range")
} }
} }
// Validate pre-conditions if any. // 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 // partial data has already been written before an error
// occurred then no point in setting StatusCode and // occurred then no point in setting StatusCode and
// sending error XML. // sending error XML.
apiErr := toAPIErrorCode(err) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
writeErrorResponse(w, r, apiErr, r.URL.Path)
} }
return return
} }
@ -190,12 +188,12 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponseHeadersOnly(w, ErrServerNotInitialized)
return return
} }
if s3Error := checkRequestAuthType(r, bucket, "s3:GetObject", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, bucket, "s3:GetObject", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponseHeadersOnly(w, s3Error)
return return
} }
@ -211,7 +209,7 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
if apiErr == ErrNoSuchKey { if apiErr == ErrNoSuchKey {
apiErr = errAllowableObjectNotFound(bucket, r) apiErr = errAllowableObjectNotFound(bucket, r)
} }
writeErrorResponse(w, r, apiErr, r.URL.Path) writeErrorResponseHeadersOnly(w, apiErr)
return return
} }
@ -235,11 +233,13 @@ func getCpObjMetadataFromHeader(header http.Header, defaultMeta map[string]strin
if isMetadataReplace(header) { if isMetadataReplace(header) {
return extractMetadataFromHeader(header) return extractMetadataFromHeader(header)
} }
// if x-amz-metadata-directive says COPY then we // if x-amz-metadata-directive says COPY then we
// return the default metadata. // return the default metadata.
if isMetadataCopy(header) { if isMetadataCopy(header) {
return defaultMeta return defaultMeta
} }
// Copy is default behavior if not x-amz-metadata-directive is set. // Copy is default behavior if not x-amz-metadata-directive is set.
return defaultMeta return defaultMeta
} }
@ -256,12 +256,12 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, dstBucket, "s3:PutObject", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, dstBucket, "s3:PutObject", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -277,13 +277,13 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
srcBucket, srcObject := path2BucketAndObject(cpSrcPath) srcBucket, srcObject := path2BucketAndObject(cpSrcPath)
// If source object is empty or bucket is empty, reply back invalid copy source. // If source object is empty or bucket is empty, reply back invalid copy source.
if srcObject == "" || srcBucket == "" { if srcObject == "" || srcBucket == "" {
writeErrorResponse(w, r, ErrInvalidCopySource, r.URL.Path) writeErrorResponse(w, ErrInvalidCopySource, r.URL)
return return
} }
// Check if metadata directive is valid. // Check if metadata directive is valid.
if !isMetadataDirectiveValid(r.Header) { if !isMetadataDirectiveValid(r.Header) {
writeErrorResponse(w, r, ErrInvalidMetadataDirective, r.URL.Path) writeErrorResponse(w, ErrInvalidMetadataDirective, r.URL)
return return
} }
@ -311,7 +311,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
objInfo, err := objectAPI.GetObjectInfo(srcBucket, srcObject) objInfo, err := objectAPI.GetObjectInfo(srcBucket, srcObject)
if err != nil { if err != nil {
errorIf(err, "Unable to fetch object info.") errorIf(err, "Unable to fetch object info.")
writeErrorResponse(w, r, toAPIErrorCode(err), cpSrcPath) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return 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. /// maximum Upload size for object in a single CopyObject operation.
if isMaxObjectSize(objInfo.Size) { if isMaxObjectSize(objInfo.Size) {
writeErrorResponse(w, r, ErrEntityTooLarge, cpSrcPath) writeErrorResponse(w, ErrEntityTooLarge, r.URL)
return return
} }
@ -339,7 +339,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
if !isMetadataReplace(r.Header) && cpSrcDstSame { if !isMetadataReplace(r.Header) && cpSrcDstSame {
// If x-amz-metadata-directive is not set to REPLACE then we need // If x-amz-metadata-directive is not set to REPLACE then we need
// to error out if source and destination are same. // to error out if source and destination are same.
writeErrorResponse(w, r, ErrInvalidCopyDest, r.URL.Path) writeErrorResponse(w, ErrInvalidCopyDest, r.URL)
return return
} }
@ -347,17 +347,16 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
// object is same then only metadata is updated. // object is same then only metadata is updated.
objInfo, err = objectAPI.CopyObject(srcBucket, srcObject, dstBucket, dstObject, newMetadata) objInfo, err = objectAPI.CopyObject(srcBucket, srcObject, dstBucket, dstObject, newMetadata)
if err != nil { if err != nil {
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
md5Sum := objInfo.MD5Sum md5Sum := objInfo.MD5Sum
response := generateCopyObjectResponse(md5Sum, objInfo.ModTime) response := generateCopyObjectResponse(md5Sum, objInfo.ModTime)
encodedSuccessResponse := encodeResponse(response) encodedSuccessResponse := encodeResponse(response)
// write headers
setCommonHeaders(w) // Write success response.
// write success response. writeSuccessResponseXML(w, encodedSuccessResponse)
writeSuccessResponse(w, encodedSuccessResponse)
// Notify object created event. // Notify object created event.
eventNotify(eventData{ 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) { func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
// X-Amz-Copy-Source shouldn't be set for this call. // X-Amz-Copy-Source shouldn't be set for this call.
if _, ok := r.Header["X-Amz-Copy-Source"]; ok { if _, ok := r.Header["X-Amz-Copy-Source"]; ok {
writeErrorResponse(w, r, ErrInvalidCopySource, r.URL.Path) writeErrorResponse(w, ErrInvalidCopySource, r.URL)
return return
} }
@ -394,7 +393,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
md5Bytes, err := checkValidMD5(r.Header.Get("Content-Md5")) md5Bytes, err := checkValidMD5(r.Header.Get("Content-Md5"))
if err != nil { if err != nil {
errorIf(err, "Unable to validate content-md5 format.") errorIf(err, "Unable to validate content-md5 format.")
writeErrorResponse(w, r, ErrInvalidDigest, r.URL.Path) writeErrorResponse(w, ErrInvalidDigest, r.URL)
return return
} }
@ -406,18 +405,18 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
size, err = strconv.ParseInt(sizeStr, 10, 64) size, err = strconv.ParseInt(sizeStr, 10, 64)
if err != nil { if err != nil {
errorIf(err, "Unable to parse `x-amz-decoded-content-length` into its integer value", sizeStr) 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 return
} }
} }
if size == -1 && !contains(r.TransferEncoding, "chunked") { if size == -1 && !contains(r.TransferEncoding, "chunked") {
writeErrorResponse(w, r, ErrMissingContentLength, r.URL.Path) writeErrorResponse(w, ErrMissingContentLength, r.URL)
return return
} }
/// maximum Upload size for objects in a single operation /// maximum Upload size for objects in a single operation
if isMaxObjectSize(size) { if isMaxObjectSize(size) {
writeErrorResponse(w, r, ErrEntityTooLarge, r.URL.Path) writeErrorResponse(w, ErrEntityTooLarge, r.URL)
return return
} }
@ -437,12 +436,12 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
switch rAuthType { switch rAuthType {
default: default:
// For all unknown auth types return error. // For all unknown auth types return error.
writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path) writeErrorResponse(w, ErrAccessDenied, r.URL)
return return
case authTypeAnonymous: case authTypeAnonymous:
// http://docs.aws.amazon.com/AmazonS3/latest/dev/using-with-s3-actions.html // http://docs.aws.amazon.com/AmazonS3/latest/dev/using-with-s3-actions.html
if s3Error := enforceBucketPolicy(bucket, "s3:PutObject", r.URL); s3Error != ErrNone { if s3Error := enforceBucketPolicy(bucket, "s3:PutObject", r.URL); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
// Create anonymous object. // Create anonymous object.
@ -452,7 +451,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
reader, s3Error := newSignV4ChunkedReader(r) reader, s3Error := newSignV4ChunkedReader(r)
if s3Error != ErrNone { if s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
objInfo, err = objectAPI.PutObject(bucket, object, size, reader, metadata, sha256sum) 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) s3Error := isReqAuthenticatedV2(r)
if s3Error != ErrNone { if s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
objInfo, err = objectAPI.PutObject(bucket, object, size, r.Body, metadata, sha256sum) objInfo, err = objectAPI.PutObject(bucket, object, size, r.Body, metadata, sha256sum)
case authTypePresigned, authTypeSigned: case authTypePresigned, authTypeSigned:
if s3Error := reqSignatureV4Verify(r); s3Error != ErrNone { if s3Error := reqSignatureV4Verify(r); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
if !skipContentSha256Cksum(r) { if !skipContentSha256Cksum(r) {
@ -478,11 +477,11 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
} }
if err != nil { if err != nil {
errorIf(err, "Unable to create an object.") errorIf(err, "Unable to create an object.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
w.Header().Set("ETag", "\""+objInfo.MD5Sum+"\"") w.Header().Set("ETag", "\""+objInfo.MD5Sum+"\"")
writeSuccessResponse(w, nil) writeSuccessResponseHeadersOnly(w)
// Notify object created event. // Notify object created event.
eventNotify(eventData{ eventNotify(eventData{
@ -506,12 +505,12 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, bucket, "s3:PutObject", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, bucket, "s3:PutObject", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -521,16 +520,15 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
uploadID, err := objectAPI.NewMultipartUpload(bucket, object, metadata) uploadID, err := objectAPI.NewMultipartUpload(bucket, object, metadata)
if err != nil { if err != nil {
errorIf(err, "Unable to initiate new multipart upload id.") errorIf(err, "Unable to initiate new multipart upload id.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
response := generateInitiateMultipartUploadResponse(bucket, object, uploadID) response := generateInitiateMultipartUploadResponse(bucket, object, uploadID)
encodedSuccessResponse := encodeResponse(response) encodedSuccessResponse := encodeResponse(response)
// write headers
setCommonHeaders(w) // Write success response.
// write success response. writeSuccessResponseXML(w, encodedSuccessResponse)
writeSuccessResponse(w, encodedSuccessResponse)
} }
// PutObjectPartHandler - Upload part // PutObjectPartHandler - Upload part
@ -541,14 +539,14 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
// get Content-Md5 sent by client and verify if valid // get Content-Md5 sent by client and verify if valid
md5Bytes, err := checkValidMD5(r.Header.Get("Content-Md5")) md5Bytes, err := checkValidMD5(r.Header.Get("Content-Md5"))
if err != nil { if err != nil {
writeErrorResponse(w, r, ErrInvalidDigest, r.URL.Path) writeErrorResponse(w, ErrInvalidDigest, r.URL)
return return
} }
@ -562,18 +560,18 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
size, err = strconv.ParseInt(sizeStr, 10, 64) size, err = strconv.ParseInt(sizeStr, 10, 64)
if err != nil { if err != nil {
errorIf(err, "Unable to parse `x-amz-decoded-content-length` into its integer value", sizeStr) 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 return
} }
} }
if size == -1 { if size == -1 {
writeErrorResponse(w, r, ErrMissingContentLength, r.URL.Path) writeErrorResponse(w, ErrMissingContentLength, r.URL)
return return
} }
/// maximum Upload size for multipart objects in a single operation /// maximum Upload size for multipart objects in a single operation
if isMaxObjectSize(size) { if isMaxObjectSize(size) {
writeErrorResponse(w, r, ErrEntityTooLarge, r.URL.Path) writeErrorResponse(w, ErrEntityTooLarge, r.URL)
return return
} }
@ -582,13 +580,13 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
partID, err := strconv.Atoi(partIDString) partID, err := strconv.Atoi(partIDString)
if err != nil { if err != nil {
writeErrorResponse(w, r, ErrInvalidPart, r.URL.Path) writeErrorResponse(w, ErrInvalidPart, r.URL)
return return
} }
// check partID with maximum part ID for multipart objects // check partID with maximum part ID for multipart objects
if isMaxPartID(partID) { if isMaxPartID(partID) {
writeErrorResponse(w, r, ErrInvalidMaxParts, r.URL.Path) writeErrorResponse(w, ErrInvalidMaxParts, r.URL)
return return
} }
@ -598,12 +596,12 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
switch rAuthType { switch rAuthType {
default: default:
// For all unknown auth types return error. // For all unknown auth types return error.
writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path) writeErrorResponse(w, ErrAccessDenied, r.URL)
return return
case authTypeAnonymous: case authTypeAnonymous:
// http://docs.aws.amazon.com/AmazonS3/latest/dev/mpuAndPermissions.html // http://docs.aws.amazon.com/AmazonS3/latest/dev/mpuAndPermissions.html
if s3Error := enforceBucketPolicy(bucket, "s3:PutObject", r.URL); s3Error != ErrNone { if s3Error := enforceBucketPolicy(bucket, "s3:PutObject", r.URL); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
// No need to verify signature, anonymous request access is already allowed. // 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) reader, s3Error := newSignV4ChunkedReader(r)
if s3Error != ErrNone { if s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
partMD5, err = objectAPI.PutObjectPart(bucket, object, uploadID, partID, size, reader, incomingMD5, sha256sum) 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) s3Error := isReqAuthenticatedV2(r)
if s3Error != ErrNone { if s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
partMD5, err = objectAPI.PutObjectPart(bucket, object, uploadID, partID, size, r.Body, incomingMD5, sha256sum) partMD5, err = objectAPI.PutObjectPart(bucket, object, uploadID, partID, size, r.Body, incomingMD5, sha256sum)
case authTypePresigned, authTypeSigned: case authTypePresigned, authTypeSigned:
if s3Error := reqSignatureV4Verify(r); s3Error != ErrNone { if s3Error := reqSignatureV4Verify(r); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -640,13 +638,14 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
if err != nil { if err != nil {
errorIf(err, "Unable to create object part.") errorIf(err, "Unable to create object part.")
// Verify if the underlying error is signature mismatch. // Verify if the underlying error is signature mismatch.
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
if partMD5 != "" { if partMD5 != "" {
w.Header().Set("ETag", "\""+partMD5+"\"") w.Header().Set("ETag", "\""+partMD5+"\"")
} }
writeSuccessResponse(w, nil)
writeSuccessResponseHeadersOnly(w)
} }
// AbortMultipartUploadHandler - Abort multipart upload // AbortMultipartUploadHandler - Abort multipart upload
@ -657,19 +656,19 @@ func (api objectAPIHandlers) AbortMultipartUploadHandler(w http.ResponseWriter,
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, bucket, "s3:AbortMultipartUpload", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, bucket, "s3:AbortMultipartUpload", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
uploadID, _, _, _ := getObjectResources(r.URL.Query()) uploadID, _, _, _ := getObjectResources(r.URL.Query())
if err := objectAPI.AbortMultipartUpload(bucket, object, uploadID); err != nil { if err := objectAPI.AbortMultipartUpload(bucket, object, uploadID); err != nil {
errorIf(err, "Unable to abort multipart upload.") errorIf(err, "Unable to abort multipart upload.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
writeSuccessNoContent(w) writeSuccessNoContent(w)
@ -683,36 +682,35 @@ func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *ht
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, bucket, "s3:ListMultipartUploadParts", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, bucket, "s3:ListMultipartUploadParts", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
uploadID, partNumberMarker, maxParts, _ := getObjectResources(r.URL.Query()) uploadID, partNumberMarker, maxParts, _ := getObjectResources(r.URL.Query())
if partNumberMarker < 0 { if partNumberMarker < 0 {
writeErrorResponse(w, r, ErrInvalidPartNumberMarker, r.URL.Path) writeErrorResponse(w, ErrInvalidPartNumberMarker, r.URL)
return return
} }
if maxParts < 0 { if maxParts < 0 {
writeErrorResponse(w, r, ErrInvalidMaxParts, r.URL.Path) writeErrorResponse(w, ErrInvalidMaxParts, r.URL)
return return
} }
listPartsInfo, err := objectAPI.ListObjectParts(bucket, object, uploadID, partNumberMarker, maxParts) listPartsInfo, err := objectAPI.ListObjectParts(bucket, object, uploadID, partNumberMarker, maxParts)
if err != nil { if err != nil {
errorIf(err, "Unable to list uploaded parts.") errorIf(err, "Unable to list uploaded parts.")
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
response := generateListPartsResponse(listPartsInfo) response := generateListPartsResponse(listPartsInfo)
encodedSuccessResponse := encodeResponse(response) encodedSuccessResponse := encodeResponse(response)
// Write headers.
setCommonHeaders(w)
// Write success response. // Write success response.
writeSuccessResponse(w, encodedSuccessResponse) writeSuccessResponseXML(w, encodedSuccessResponse)
} }
// CompleteMultipartUploadHandler - Complete multipart upload. // CompleteMultipartUploadHandler - Complete multipart upload.
@ -723,12 +721,12 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, bucket, "s3:PutObject", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, bucket, "s3:PutObject", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }
@ -739,23 +737,24 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
completeMultipartBytes, err := ioutil.ReadAll(r.Body) completeMultipartBytes, err := ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
errorIf(err, "Unable to complete multipart upload.") errorIf(err, "Unable to complete multipart upload.")
writeErrorResponse(w, r, ErrInternalError, r.URL.Path) writeErrorResponse(w, ErrInternalError, r.URL)
return return
} }
complMultipartUpload := &completeMultipartUpload{} complMultipartUpload := &completeMultipartUpload{}
if err = xml.Unmarshal(completeMultipartBytes, complMultipartUpload); err != nil { if err = xml.Unmarshal(completeMultipartBytes, complMultipartUpload); err != nil {
errorIf(err, "Unable to parse complete multipart upload XML.") errorIf(err, "Unable to parse complete multipart upload XML.")
writeErrorResponse(w, r, ErrMalformedXML, r.URL.Path) writeErrorResponse(w, ErrMalformedXML, r.URL)
return return
} }
if len(complMultipartUpload.Parts) == 0 { if len(complMultipartUpload.Parts) == 0 {
writeErrorResponse(w, r, ErrMalformedXML, r.URL.Path) writeErrorResponse(w, ErrMalformedXML, r.URL)
return return
} }
if !sort.IsSorted(completedParts(complMultipartUpload.Parts)) { if !sort.IsSorted(completedParts(complMultipartUpload.Parts)) {
writeErrorResponse(w, r, ErrInvalidPartOrder, r.URL.Path) writeErrorResponse(w, ErrInvalidPartOrder, r.URL)
return return
} }
// Complete parts. // Complete parts.
var completeParts []completePart var completeParts []completePart
for _, part := range complMultipartUpload.Parts { for _, part := range complMultipartUpload.Parts {
@ -779,7 +778,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
writePartSmallErrorResponse(w, r, oErr) writePartSmallErrorResponse(w, r, oErr)
default: default:
// Handle all other generic issues. // Handle all other generic issues.
writeErrorResponse(w, r, toAPIErrorCode(err), r.URL.Path) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
} }
return return
} }
@ -791,7 +790,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
encodedSuccessResponse := encodeResponse(response) encodedSuccessResponse := encodeResponse(response)
if err != nil { if err != nil {
errorIf(err, "Unable to parse CompleteMultipartUpload response") errorIf(err, "Unable to parse CompleteMultipartUpload response")
writeErrorResponseNoHeader(w, r, ErrInternalError, r.URL.Path) writeErrorResponse(w, ErrInternalError, r.URL)
return return
} }
@ -799,8 +798,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
w.Header().Set("ETag", "\""+md5Sum+"\"") w.Header().Set("ETag", "\""+md5Sum+"\"")
// Write success response. // Write success response.
w.Write(encodedSuccessResponse) writeSuccessResponseXML(w, encodedSuccessResponse)
w.(http.Flusher).Flush()
// Fetch object info for notifications. // Fetch object info for notifications.
objInfo, err := objectAPI.GetObjectInfo(bucket, object) objInfo, err := objectAPI.GetObjectInfo(bucket, object)
@ -830,12 +828,12 @@ func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.
objectAPI := api.ObjectAPI() objectAPI := api.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeErrorResponse(w, r, ErrServerNotInitialized, r.URL.Path) writeErrorResponse(w, ErrServerNotInitialized, r.URL)
return return
} }
if s3Error := checkRequestAuthType(r, bucket, "s3:DeleteObject", serverConfig.GetRegion()); s3Error != ErrNone { if s3Error := checkRequestAuthType(r, bucket, "s3:DeleteObject", serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, s3Error, r.URL)
return return
} }