Skip to content

Commit d4c5e1b

Browse files
authored
Fail request when error occurs during download (#3214)
1 parent 96923ae commit d4c5e1b

File tree

3 files changed

+30
-16
lines changed

3 files changed

+30
-16
lines changed

api/admin_inspect.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ func processInspectResponse(params *inspectApi.InspectParams, k []byte, r io.Rea
120120

121121
_, err := io.Copy(w, r)
122122
if err != nil {
123-
LogError("Unable to write all the data: %v", err)
123+
LogError("unable to write all the data: %v", err)
124124
}
125125
}
126126
}

api/user_objects.go

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,8 @@ func getDownloadObjectResponse(session *models.Principal, params objectApi.Downl
448448
// override filename is set
449449
decodeOverride, err := base64.StdEncoding.DecodeString(*params.OverrideFileName)
450450
if err != nil {
451+
fmtError := ErrorWithContext(ctx, fmt.Errorf("unable to decode OverrideFileName: %v", err))
452+
http.Error(rw, fmtError.APIError.DetailedMessage, http.StatusBadRequest)
451453
return
452454
}
453455

@@ -472,16 +474,16 @@ func getDownloadObjectResponse(session *models.Principal, params objectApi.Downl
472474
stat, err := resp.Stat()
473475
if err != nil {
474476
minErr := minio.ToErrorResponse(err)
475-
rw.WriteHeader(minErr.StatusCode)
476-
ErrorWithContext(ctx, fmt.Errorf("Failed to get Stat() response from server for %s (version %s): %v", prefix, opts.VersionID, minErr.Error()))
477+
fmtError := ErrorWithContext(ctx, fmt.Errorf("failed to get Stat() response from server for %s (version %s): %v", prefix, opts.VersionID, minErr.Error()))
478+
http.Error(rw, fmtError.APIError.DetailedMessage, http.StatusInternalServerError)
477479
return
478480
}
479481

480482
// if we are getting a Range Request (video) handle that specially
481483
ranges, err := parseRange(params.HTTPRequest.Header.Get("Range"), stat.Size)
482484
if err != nil {
483-
ErrorWithContext(ctx, fmt.Errorf("Unable to parse range header input %s: %v", params.HTTPRequest.Header.Get("Range"), err))
484-
rw.WriteHeader(400)
485+
fmtError := ErrorWithContext(ctx, fmt.Errorf("unable to parse range header input %s: %v", params.HTTPRequest.Header.Get("Range"), err))
486+
http.Error(rw, fmtError.APIError.DetailedMessage, http.StatusInternalServerError)
485487
return
486488
}
487489
contentType := stat.ContentType
@@ -510,8 +512,8 @@ func getDownloadObjectResponse(session *models.Principal, params objectApi.Downl
510512

511513
_, err = resp.Seek(start, io.SeekStart)
512514
if err != nil {
513-
ErrorWithContext(ctx, fmt.Errorf("Unable to seek at offset %d: %v", start, err))
514-
rw.WriteHeader(400)
515+
fmtError := ErrorWithContext(ctx, fmt.Errorf("unable to seek at offset %d: %v", start, err))
516+
http.Error(rw, fmtError.APIError.DetailedMessage, http.StatusInternalServerError)
515517
return
516518
}
517519

@@ -524,7 +526,9 @@ func getDownloadObjectResponse(session *models.Principal, params objectApi.Downl
524526
rw.Header().Set("Content-Length", fmt.Sprintf("%d", length))
525527
_, err = io.Copy(rw, io.LimitReader(resp, length))
526528
if err != nil {
527-
ErrorWithContext(ctx, fmt.Errorf("Unable to write all data to client: %v", err))
529+
ErrorWithContext(ctx, fmt.Errorf("unable to write all data to client: %v", err))
530+
// You can't change headers after you already started writing the body.
531+
// Handle incomplete write in client.
528532
return
529533
}
530534
}), nil
@@ -610,7 +614,8 @@ func getDownloadFolderResponse(session *models.Principal, params objectApi.Downl
610614
encodedPrefix := SanitizeEncodedPrefix(params.Prefix)
611615
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
612616
if err != nil {
613-
ErrorWithContext(ctx, fmt.Errorf("Unable to parse encoded prefix %s: %v", encodedPrefix, err))
617+
fmtError := ErrorWithContext(ctx, fmt.Errorf("unable to parse encoded prefix %s: %v", encodedPrefix, err))
618+
http.Error(rw, fmtError.APIError.DetailedMessage, http.StatusInternalServerError)
614619
return
615620
}
616621

@@ -632,7 +637,10 @@ func getDownloadFolderResponse(session *models.Principal, params objectApi.Downl
632637
// Copy the stream
633638
_, err := io.Copy(rw, resp)
634639
if err != nil {
635-
ErrorWithContext(ctx, fmt.Errorf("Unable to write all the requested data: %v", err))
640+
ErrorWithContext(ctx, fmt.Errorf("unable to write all the requested data: %v", err))
641+
// You can't change headers after you already started writing the body.
642+
// Handle incomplete write in client.
643+
return
636644
}
637645
}), nil
638646
}
@@ -754,7 +762,10 @@ func getMultipleFilesDownloadResponse(session *models.Principal, params objectAp
754762
// Copy the stream
755763
_, err := io.Copy(rw, resp)
756764
if err != nil {
757-
ErrorWithContext(ctx, fmt.Errorf("Unable to write all the requested data: %v", err))
765+
ErrorWithContext(ctx, fmt.Errorf("unable to write all the requested data: %v", err))
766+
// You can't change headers after you already started writing the body.
767+
// Handle incomplete write in client.
768+
return
758769
}
759770
}), nil
760771
}

web-app/src/screens/Console/Buckets/ListBuckets/Objects/utils.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import store from "../../../../../store";
2121
import { ContentType, PermissionResource } from "api/consoleApi";
2222
import { api } from "../../../../../api";
2323
import { setErrorSnackMessage } from "../../../../../systemSlice";
24-
24+
import { StatusCodes } from "http-status-codes";
2525
const downloadWithLink = (href: string, downloadFileName: string) => {
2626
const link = document.createElement("a");
2727
link.href = href;
@@ -78,6 +78,7 @@ export const download = (
7878
toastCallback: () => void,
7979
) => {
8080
let basename = document.baseURI.replace(window.location.origin, "");
81+
let bytesLoaded = 0;
8182
const state = store.getState();
8283
const anonymousMode = state.system.anonymousMode;
8384

@@ -106,7 +107,7 @@ export const download = (
106107
"progress",
107108
function (evt) {
108109
let percentComplete = Math.round((evt.loaded / fileSize) * 100);
109-
110+
bytesLoaded = evt.loaded;
110111
if (progressCallback) {
111112
progressCallback(percentComplete);
112113
}
@@ -116,8 +117,10 @@ export const download = (
116117

117118
req.responseType = "blob";
118119
req.onreadystatechange = () => {
119-
if (req.readyState === 4) {
120-
if (req.status === 200) {
120+
if (req.readyState === XMLHttpRequest.DONE) {
121+
let completeDownload = bytesLoaded === fileSize;
122+
123+
if (req.status === StatusCodes.OK && completeDownload) {
121124
const rspHeader = req.getResponseHeader("Content-Disposition");
122125

123126
let filename = "download";
@@ -143,7 +146,7 @@ export const download = (
143146
return;
144147
}
145148
}
146-
errorCallback(`Unexpected response status code (${req.status}).`);
149+
errorCallback(`Unexpected response, download incomplete.`);
147150
}
148151
}
149152
};

0 commit comments

Comments
 (0)