diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index 3585d1813..6cb708453 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -31,6 +31,8 @@ import ( "strconv" "strings" + "time" + snappy "github.com/golang/snappy" "github.com/gorilla/mux" miniogo "github.com/minio/minio-go" @@ -2128,6 +2130,36 @@ func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *ht writeSuccessResponseXML(w, encodedSuccessResponse) } +// Send white spaces every 10 seconds to the client till completeMultiPartUpload() is done so that the client does not time out. +// Downside is we might send 200OK and then send error XML. But accoording to S3 spec +// the client is supposed to check for error XML even if it received 200OK. 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 struct{} { + doneCh := make(chan struct{}) + go func() { + ticker := time.NewTicker(time.Second * 10) + for { + select { + case <-ticker.C: + // Write a white space char to the client to prevent client timeouts + // when the server takes a long time to completeMultiPartUpload() + if _, err := w.Write([]byte("\n\r")); err != nil { + logger.LogIf(ctx, err) + return + } + w.(http.Flusher).Flush() + case doneCh <- struct{}{}: + ticker.Stop() + return + } + } + + }() + return doneCh +} + // CompleteMultipartUploadHandler - Complete multipart upload. func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { ctx := newContext(r, w, "CompleteMultipartUpload") @@ -2255,7 +2287,11 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite if api.CacheAPI() != nil { completeMultiPartUpload = api.CacheAPI().CompleteMultipartUpload } + completeDoneCh := sendWhiteSpace(ctx, w) objInfo, err := completeMultiPartUpload(ctx, bucket, object, uploadID, completeParts, 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. + <-completeDoneCh if err != nil { switch oErr := err.(type) { case PartTooSmall: