]> source.dussan.org Git - vaadin-framework.git/commitdiff
Encode filenames to UTF-8 in Content-Disposition header. (#16556)
authorSauli Tähkäpää <sauli@vaadin.com>
Tue, 3 Feb 2015 13:02:45 +0000 (15:02 +0200)
committerArtur Signell <artur@vaadin.com>
Mon, 2 Mar 2015 19:51:32 +0000 (21:51 +0200)
Change-Id: Ie2cf41f2176d05105663cdb84934379efa826f03

server/src/com/vaadin/server/DownloadStream.java
server/src/com/vaadin/server/FileDownloader.java
server/tests/src/com/vaadin/server/FileDownloaderTests.java [new file with mode: 0644]

index 681c4389671427aaa33a67d087913016f8cd193f..8b2b933bcc422f66c97ab99f4e0b86d4ba03379f 100644 (file)
@@ -20,6 +20,8 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
@@ -280,16 +282,9 @@ public class DownloadStream implements Serializable {
                     }
                 }
 
-                // suggest local filename from DownloadStream if
-                // Content-Disposition
-                // not explicitly set
-                String contentDispositionValue = getParameter("Content-Disposition");
-                if (contentDispositionValue == null) {
-                    contentDispositionValue = "filename=\"" + getFileName()
-                            + "\"";
-                    response.setHeader("Content-Disposition",
-                            contentDispositionValue);
-                }
+                // Content-Disposition: attachment generally forces download
+                response.setHeader("Content-Disposition",
+                        getContentDispositionValue());
 
                 int bufferSize = getBufferSize();
                 if (bufferSize <= 0 || bufferSize > Constants.MAX_BUFFER_SIZE) {
@@ -317,6 +312,21 @@ public class DownloadStream implements Serializable {
         }
     }
 
+    private String getContentDispositionValue()
+            throws UnsupportedEncodingException {
+        String contentDispositionValue = getParameter("Content-Disposition");
+
+        if (contentDispositionValue == null) {
+            String encodedFilename = URLEncoder.encode(getFileName(), "utf-8");
+
+            contentDispositionValue = String.format(
+                    "attachment; filename=\"%s\"; filename*=utf-8''%s",
+                    encodedFilename, encodedFilename);
+        }
+
+        return contentDispositionValue;
+    }
+
     /**
      * Helper method that tries to close an output stream and ignores any
      * exceptions.
index 42c2f76e1abe6f6d43fc55d1e2bec3f0b5a628ff..bea9922c50388d4d0d55a4b6c3b03b0d3e758300 100644 (file)
@@ -141,12 +141,6 @@ public class FileDownloader extends AbstractExtension {
             }
             stream = ((ConnectorResource) resource).getStream();
 
-            if (stream.getParameter("Content-Disposition") == null) {
-                // Content-Disposition: attachment generally forces download
-                stream.setParameter("Content-Disposition",
-                        "attachment; filename=\"" + stream.getFileName() + "\"");
-            }
-
             // Content-Type to block eager browser plug-ins from hijacking
             // the file
             if (isOverrideContentType()) {
diff --git a/server/tests/src/com/vaadin/server/FileDownloaderTests.java b/server/tests/src/com/vaadin/server/FileDownloaderTests.java
new file mode 100644 (file)
index 0000000..4e9478c
--- /dev/null
@@ -0,0 +1,41 @@
+package com.vaadin.server;
+
+import static org.mockito.Matchers.contains;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLEncoder;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class FileDownloaderTests {
+    private String filename = "日本語.png";
+    private DownloadStream stream;
+
+    @Before
+    public void setup() {
+        stream = new DownloadStream(mock(InputStream.class), "", filename);
+    }
+
+    @Test
+    public void contentDispositionFilenameIsUtf8Encoded() throws IOException {
+        VaadinResponse response = mock(VaadinResponse.class);
+
+        stream.writeResponse(mock(VaadinRequest.class), response);
+
+        verify(response).setHeader(eq("Content-Disposition"),
+                contains("attachment;"));
+        String encodedFileName = URLEncoder.encode(filename, "utf-8");
+        verify(response).setHeader(eq("Content-Disposition"),
+                contains(String.format("filename=\"%s\";", encodedFileName)));
+        verify(response)
+                .setHeader(
+                        eq("Content-Disposition"),
+                        contains(String.format("filename*=utf-8''%s",
+                                encodedFileName)));
+    }
+}