From fa73366ff098dca0eb5f9658f70f1fe8045a1cec Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Tue, 20 Oct 2026 14:58:09 +0200 Subject: [PATCH] fix(webdav): read the generator's status before the writer's error A 429 or 413 is answered without reading the body, which breaks the write; the broken pipe used to mask the status. A 413 now reads as too large, not as a server fault. --- .../webdav/pkg/thumbnail/workflow/workflow.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/services/webdav/pkg/thumbnail/workflow/workflow.go b/services/webdav/pkg/thumbnail/workflow/workflow.go index 4f39a04f0f..01bcb2d54c 100644 --- a/services/webdav/pkg/thumbnail/workflow/workflow.go +++ b/services/webdav/pkg/thumbnail/workflow/workflow.go @@ -643,11 +643,9 @@ func (w *ThumbnailWorkflow) postToGenerator(ctx context.Context, url string, img } defer httpRsp.Body.Close() - if writeErr := <-errCh; writeErr != nil { - io.Copy(io.Discard, httpRsp.Body) - return nil, "", writeErr - } - + // The status is read before the writer's error. A generator that refuses the + // request without reading it, which is what 429 and 413 do, breaks the write + // half way, and that broken pipe must not mask the answer the client needs. if httpRsp.StatusCode < 200 || httpRsp.StatusCode >= 300 { respBody, _ := io.ReadAll(httpRsp.Body) // The generator reports ErrMaxResolutionExceeded (422) when the decoded @@ -661,6 +659,12 @@ func (w *ThumbnailWorkflow) postToGenerator(ctx context.Context, url string, img retryAfter, _ := strconv.Atoi(httpRsp.Header.Get("Retry-After")) return nil, "", &GeneratorBusyError{RetryAfterSeconds: retryAfter} } + // The generator bounds the whole request body, this service only the file, + // so the multipart framing can push a file just under the limit over it. + // Either way the source is too large, which is a 403, not a server fault. + if httpRsp.StatusCode == http.StatusRequestEntityTooLarge { + return nil, "", ErrImageTooLarge + } // A source the generator cannot read is a missing preview, not a server // fault, so the client gets a 404 and stops asking. A 400 means this @@ -671,6 +675,11 @@ func (w *ThumbnailWorkflow) postToGenerator(ctx context.Context, url string, img return nil, "", fmt.Errorf("generator returned status %d: %s", httpRsp.StatusCode, string(respBody)) } + if writeErr := <-errCh; writeErr != nil { + io.Copy(io.Discard, httpRsp.Body) + return nil, "", writeErr + } + rspData, err := io.ReadAll(httpRsp.Body) if err != nil { return nil, "", fmt.Errorf("read generator response: %w", err)