diff --git a/cmd/object-multipart-handlers.go b/cmd/object-multipart-handlers.go index 5ead362c4..32a480b5f 100644 --- a/cmd/object-multipart-handlers.go +++ b/cmd/object-multipart-handlers.go @@ -19,8 +19,6 @@ package cmd import ( "bufio" - "context" - "encoding/xml" "io" "net/http" "net/url" @@ -941,24 +939,6 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite if api.CacheAPI() != nil { completeMultiPartUpload = api.CacheAPI().CompleteMultipartUpload } - // This code is specifically to handle the requirements for slow - // complete multipart upload operations on FS mode. - writeErrorResponseWithoutXMLHeader := func(ctx context.Context, w http.ResponseWriter, err APIError, reqURL *url.URL) { - switch err.Code { - case "SlowDown", "XMinioServerNotInitialized", "XMinioReadQuorum", "XMinioWriteQuorum": - // Set retxry-after header to indicate user-agents to retry request after 120secs. - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After - w.Header().Set(xhttp.RetryAfter, "120") - } - - // Generate error response. - errorResponse := getAPIErrorResponse(ctx, err, reqURL.Path, - w.Header().Get(xhttp.AmzRequestID), globalDeploymentID) - encodedErrorResponse, _ := xml.Marshal(errorResponse) - setCommonHeaders(w) - w.Header().Set(xhttp.ContentType, string(mimeXML)) - w.Write(encodedErrorResponse) - } versioned := globalBucketVersioningSys.PrefixEnabled(bucket, object) suspended := globalBucketVersioningSys.PrefixSuspended(bucket, object) @@ -998,36 +978,12 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite multipartETag := etag.Multipart(completeETags...) opts.UserDefined["etag"] = multipartETag.String() - w = &whiteSpaceWriter{ResponseWriter: w, Flusher: w.(http.Flusher)} - completeDoneCh := sendWhiteSpace(ctx, w) objInfo, err := completeMultiPartUpload(ctx, bucket, object, uploadID, complMultipartUpload.Parts, opts) - // Stop writing white spaces to the client. Note that close(doneCh) style is not used as it - // can cause white space to be written after we send XML response in a race condition. - headerWritten := <-completeDoneCh if err != nil { - if headerWritten { - writeErrorResponseWithoutXMLHeader(ctx, w, toAPIError(ctx, err), r.URL) - } else { - writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) - } + writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) return } - // Get object location. - location := getObjectLocation(r, globalDomainNames, bucket, object) - // Generate complete multipart response. - response := generateCompleteMultpartUploadResponse(bucket, object, location, objInfo) - var encodedSuccessResponse []byte - if !headerWritten { - encodedSuccessResponse = encodeResponse(response) - } else { - encodedSuccessResponse, err = xml.Marshal(response) - if err != nil { - writeErrorResponseWithoutXMLHeader(ctx, w, toAPIError(ctx, err), r.URL) - return - } - } - opts.EncryptFn, err = objInfo.metadataEncryptFn(r.Header) if err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) @@ -1050,6 +1006,12 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite defer globalReplicationStats.UpdateReplicaStat(bucket, actualSize) } + // Get object location. + location := getObjectLocation(r, globalDomainNames, bucket, object) + // Generate complete multipart response. + response := generateCompleteMultpartUploadResponse(bucket, object, location, objInfo) + encodedSuccessResponse := encodeResponse(response) + // Write success response. writeSuccessResponseXML(w, encodedSuccessResponse) @@ -1194,63 +1156,3 @@ func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *ht // Write success response. writeSuccessResponseXML(w, encodedSuccessResponse) } - -type whiteSpaceWriter struct { - http.ResponseWriter - http.Flusher - written bool -} - -func (w *whiteSpaceWriter) Write(b []byte) (n int, err error) { - n, err = w.ResponseWriter.Write(b) - w.written = true - return -} - -func (w *whiteSpaceWriter) WriteHeader(statusCode int) { - if !w.written { - w.ResponseWriter.WriteHeader(statusCode) - } -} - -// Send empty whitespaces every 10 seconds to the client till completeMultiPartUpload() is -// done so that the client does not time out. Downside is we might send 200 OK and -// then send error XML. But accoording to S3 spec the client is supposed to check -// for error XML even if it received 200 OK. But for erasure this is not a problem -// as completeMultiPartUpload() is quick. Even For FS, it would not be an issue as -// we do background append as and when the parts arrive and completeMultiPartUpload -// is quick. Only in a rare case where parts would be out of order will -// FS:completeMultiPartUpload() take a longer time. -func sendWhiteSpace(ctx context.Context, w http.ResponseWriter) <-chan bool { - doneCh := make(chan bool) - go func() { - defer close(doneCh) - ticker := time.NewTicker(time.Second * 10) - defer ticker.Stop() - headerWritten := false - for { - select { - case <-ticker.C: - // Write header if not written yet. - if !headerWritten { - _, err := w.Write([]byte(xml.Header)) - headerWritten = err == nil - } - - // Once header is written keep writing empty spaces - // which are ignored by client SDK XML parsers. - // This occurs when server takes long time to completeMultiPartUpload() - _, err := w.Write([]byte(" ")) - if err != nil { - return - } - w.(http.Flusher).Flush() - case doneCh <- headerWritten: - return - case <-ctx.Done(): - return - } - } - }() - return doneCh -}