diff --git a/internal/web/uploads.go b/internal/web/uploads.go index fa48d5b..1558523 100644 --- a/internal/web/uploads.go +++ b/internal/web/uploads.go @@ -95,7 +95,7 @@ func (a *App) handleUploadPost(w http.ResponseWriter, r *http.Request) { } if err != nil { var tooBig *http.MaxBytesError - if a.abandoned(r) { + if a.abandoned(w, r) { return } if !errors.As(err, &tooBig) { @@ -113,6 +113,9 @@ func (a *App) handleUploadPost(w http.ResponseWriter, r *http.Request) { case "title": b, err := io.ReadAll(io.LimitReader(part, maxTitleRunes*utf8.UTFMax)) if err != nil { + if a.abandoned(w, r) { + return + } a.renderUploadsPage(w, r, userID, http.StatusBadRequest, "That upload could not be read. Please try again.") return } @@ -133,14 +136,17 @@ func (a *App) handleUploadPost(w http.ResponseWriter, r *http.Request) { a.renderUploadsPage(w, r, userID, http.StatusBadRequest, "Choose an audio file to upload.") } -// abandoned reports, and logs at INFO, that the client has already gone: its request +// abandoned reports, logs at INFO and answers 499, because the client has already gone: its request // context is cancelled, so there is nobody to answer and rendering a page would run its // database read on a dead context and log a false ERROR. A hang-up mid-upload is routine. -func (a *App) abandoned(r *http.Request) bool { +func (a *App) abandoned(w http.ResponseWriter, r *http.Request) bool { if r.Context().Err() == nil { return false } a.logger().Info("upload abandoned", "reason", "client_gone") + // 499 (the "client closed request" convention): a client that half-closed after sending + // can still read, and an unwritten status would reach it as a false, empty 200. + w.WriteHeader(499) return true } @@ -148,7 +154,7 @@ func (a *App) abandoned(r *http.Request) bool { // rejection is the user's to fix (422, or 413 for size) and carries its own // message; anything else is ours. func (a *App) uploadFailure(w http.ResponseWriter, r *http.Request, userID string, err error) { - if a.abandoned(r) { + if a.abandoned(w, r) { return } var rej *uploads.RejectError