Skip to content

Commit 6ce1500

Browse files
vaadin-botArtur-
andauthored
fix: Set Content-Length header in InputStreamDownloadHandler (#22558) (#22564) (#22587)
InputStreamDownloadHandler now properly sets the Content-Length HTTP header when provided in DownloadResponse, enabling browsers to display download progress and allowing TransferProgressListener implementations to calculate accurate progress percentages. This aligns behavior with FileDownloadHandler. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Artur <artur@vaadin.com>
1 parent 3558909 commit 6ce1500

File tree

2 files changed

+31
-7
lines changed

2 files changed

+31
-7
lines changed

flow-server/src/main/java/com/vaadin/flow/server/streams/InputStreamDownloadHandler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public void handleDownloadRequest(DownloadEvent downloadEvent)
8989
? getContentType(downloadName, response)
9090
: download.getContentType();
9191
downloadEvent.setContentType(contentType);
92+
downloadEvent.setContentLength(download.getContentLength());
9293

9394
if (!isInline()) {
9495
downloadEvent.setFileName(downloadName);

flow-server/src/test/java/com/vaadin/flow/server/streams/InputStreamDownloadHandlerTest.java

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
import com.vaadin.flow.server.VaadinSession;
4343

4444
public class InputStreamDownloadHandlerTest {
45+
private static final int SIMULATED_DOWNLOAD_SIZE = 165000;
46+
4547
private VaadinRequest request;
4648
private VaadinResponse response;
4749
private VaadinSession session;
@@ -93,7 +95,8 @@ public void transferProgressListener_addListener_listenersInvoked()
9395
}, new TransferProgressListener() {
9496
@Override
9597
public void onStart(TransferContext context) {
96-
Assert.assertEquals(-1, context.contentLength());
98+
Assert.assertEquals(SIMULATED_DOWNLOAD_SIZE,
99+
context.contentLength());
97100
Assert.assertEquals("download", context.fileName());
98101
invocations.add("onStart");
99102
}
@@ -102,16 +105,17 @@ public void onStart(TransferContext context) {
102105
public void onProgress(TransferContext context,
103106
long transferredBytes, long totalBytes) {
104107
transferredBytesRecords.add(transferredBytes);
105-
Assert.assertEquals(-1, totalBytes);
108+
Assert.assertEquals(SIMULATED_DOWNLOAD_SIZE, totalBytes);
106109
Assert.assertEquals("download", context.fileName());
107110
invocations.add("onProgress");
108111
}
109112

110113
@Override
111114
public void onComplete(TransferContext context,
112115
long transferredBytes) {
113-
Assert.assertEquals(-1, context.contentLength());
114-
Assert.assertEquals(165000, transferredBytes);
116+
Assert.assertEquals(SIMULATED_DOWNLOAD_SIZE,
117+
context.contentLength());
118+
Assert.assertEquals(SIMULATED_DOWNLOAD_SIZE, transferredBytes);
115119
Assert.assertEquals("download", context.fileName());
116120
invocations.add("onComplete");
117121
}
@@ -150,7 +154,6 @@ public void transferProgressListener_addListener_errorOccurred_errorListenerInvo
150154
AtomicReference<Boolean> whenCompleteResult = new AtomicReference<>();
151155
InvocationTrackingTransferProgressListener transferListener = new InvocationTrackingTransferProgressListener();
152156
DownloadHandler handler = DownloadHandler.fromInputStream(req -> {
153-
// Simulate a download of 165000 bytes
154157
byte[] data = getBytes();
155158
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
156159
return new DownloadResponse(inputStream, "download",
@@ -446,9 +449,29 @@ public void handleSetToInline_contentTypeIsInline() throws IOException {
446449
Mockito.verify(response).setHeader("Content-Disposition", "inline");
447450
}
448451

452+
@Test
453+
public void contentLengthProvided_contentLengthHeaderSet()
454+
throws IOException {
455+
long expectedContentLength = 12345L;
456+
InputStream stream = Mockito.mock(InputStream.class);
457+
Mockito.when(
458+
stream.read(Mockito.any(), Mockito.anyInt(), Mockito.anyInt()))
459+
.thenReturn(-1);
460+
461+
InputStreamDownloadHandler handler = new InputStreamDownloadHandler(
462+
event -> new DownloadResponse(stream, "report.pdf",
463+
"application/pdf", expectedContentLength));
464+
465+
DownloadEvent event = new DownloadEvent(request, response, session,
466+
new Element("div"));
467+
468+
handler.handleDownloadRequest(event);
469+
470+
Mockito.verify(response).setContentLengthLong(expectedContentLength);
471+
}
472+
449473
private static byte[] getBytes() {
450-
// Simulate a download of 165000 bytes
451-
byte[] data = new byte[165000];
474+
byte[] data = new byte[SIMULATED_DOWNLOAD_SIZE];
452475
for (int i = 0; i < data.length; i++) {
453476
data[i] = (byte) (i % 256);
454477
}

0 commit comments

Comments
 (0)