Skip to content

Commit fceb680

Browse files
authored
fix: Set Content-Length header in InputStreamDownloadHandler (#22558) (#22564)
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)
1 parent 258ab1a commit fceb680

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
@@ -88,6 +88,7 @@ public void handleDownloadRequest(DownloadEvent downloadEvent)
8888
? getContentType(downloadName, response)
8989
: download.getContentType();
9090
downloadEvent.setContentType(contentType);
91+
downloadEvent.setContentLength(download.getContentLength());
9192

9293
if (isInline()) {
9394
downloadEvent.inline(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
@@ -41,6 +41,8 @@
4141
import com.vaadin.flow.server.VaadinSession;
4242

4343
public class InputStreamDownloadHandlerTest {
44+
private static final int SIMULATED_DOWNLOAD_SIZE = 165000;
45+
4446
private VaadinRequest request;
4547
private VaadinResponse response;
4648
private VaadinSession session;
@@ -92,7 +94,8 @@ public void transferProgressListener_addListener_listenersInvoked()
9294
}, new TransferProgressListener() {
9395
@Override
9496
public void onStart(TransferContext context) {
95-
Assert.assertEquals(-1, context.contentLength());
97+
Assert.assertEquals(SIMULATED_DOWNLOAD_SIZE,
98+
context.contentLength());
9699
Assert.assertEquals("download", context.fileName());
97100
invocations.add("onStart");
98101
}
@@ -101,16 +104,17 @@ public void onStart(TransferContext context) {
101104
public void onProgress(TransferContext context,
102105
long transferredBytes, long totalBytes) {
103106
transferredBytesRecords.add(transferredBytes);
104-
Assert.assertEquals(-1, totalBytes);
107+
Assert.assertEquals(SIMULATED_DOWNLOAD_SIZE, totalBytes);
105108
Assert.assertEquals("download", context.fileName());
106109
invocations.add("onProgress");
107110
}
108111

109112
@Override
110113
public void onComplete(TransferContext context,
111114
long transferredBytes) {
112-
Assert.assertEquals(-1, context.contentLength());
113-
Assert.assertEquals(165000, transferredBytes);
115+
Assert.assertEquals(SIMULATED_DOWNLOAD_SIZE,
116+
context.contentLength());
117+
Assert.assertEquals(SIMULATED_DOWNLOAD_SIZE, transferredBytes);
114118
Assert.assertEquals("download", context.fileName());
115119
invocations.add("onComplete");
116120
}
@@ -149,7 +153,6 @@ public void transferProgressListener_addListener_errorOccurred_errorListenerInvo
149153
AtomicReference<Boolean> whenCompleteResult = new AtomicReference<>();
150154
InvocationTrackingTransferProgressListener transferListener = new InvocationTrackingTransferProgressListener();
151155
DownloadHandler handler = DownloadHandler.fromInputStream(req -> {
152-
// Simulate a download of 165000 bytes
153156
byte[] data = getBytes();
154157
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
155158
return new DownloadResponse(inputStream, "download",
@@ -447,9 +450,29 @@ public void handleSetToInline_contentDispositionIsInlineWithFilename()
447450
"inline; filename=\"download\"");
448451
}
449452

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

0 commit comments

Comments
 (0)