]> source.dussan.org Git - vaadin-framework.git/commitdiff
URL encode file name in GlobalResourceHandler (#10751)
authorMarco Collovati <mcollovati@gmail.com>
Wed, 9 May 2018 08:47:58 +0000 (10:47 +0200)
committerIlia Motornyi <elmot@vaadin.com>
Wed, 9 May 2018 08:47:58 +0000 (11:47 +0300)
Fixes #10747

server/src/main/java/com/vaadin/server/GlobalResourceHandler.java
server/src/test/java/com/vaadin/server/GlobalResourceHandlerTest.java [new file with mode: 0644]

index 4e07bdcd3be5ce13e8b491e575aad0453f3938fe..1d1ffc3467b904b015e56b0c9d44d5c546278e7c 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.vaadin.server;
 
+import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -26,8 +27,6 @@ import java.util.logging.Logger;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import javax.servlet.http.HttpServletResponse;
-
 import com.vaadin.shared.ApplicationConstants;
 import com.vaadin.ui.LegacyComponent;
 import com.vaadin.ui.UI;
@@ -96,7 +95,7 @@ public class GlobalResourceHandler implements RequestHandler {
             oldInstances = CurrentInstance.setCurrent(ui);
             ConnectorResource resource;
             if (LEGACY_TYPE.equals(type)) {
-                resource = legacyResources.get(key);
+                resource = legacyResources.get(urlEncodedKey(key));
             } else {
                 return error(request, response, "Unknown global resource type "
                         + type + " in requested path " + pathInfo);
@@ -123,6 +122,12 @@ public class GlobalResourceHandler implements RequestHandler {
         return true;
     }
 
+
+    private String urlEncodedKey(String key) {
+        // getPathInfo return path decoded but without decoding plus as spaces
+        return ResourceReference.encodeFileName(key.replace("+", " "));
+    }
+
     /**
      * Registers a resource to be served with a global URL.
      * <p>
@@ -147,7 +152,7 @@ public class GlobalResourceHandler implements RequestHandler {
                         + Integer.toString(nextLegacyId++);
                 String filename = connectorResource.getFilename();
                 if (filename != null && !filename.isEmpty()) {
-                    uri += '/' + filename;
+                    uri += '/' + ResourceReference.encodeFileName(filename);
                 }
                 legacyResourceKeys.put(connectorResource, uri);
                 legacyResources.put(uri, connectorResource);
diff --git a/server/src/test/java/com/vaadin/server/GlobalResourceHandlerTest.java b/server/src/test/java/com/vaadin/server/GlobalResourceHandlerTest.java
new file mode 100644 (file)
index 0000000..5566314
--- /dev/null
@@ -0,0 +1,68 @@
+package com.vaadin.server;
+
+import java.io.IOException;
+
+import com.vaadin.tests.util.MockUI;
+import com.vaadin.ui.LegacyComponent;
+import com.vaadin.ui.UI;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.endsWith;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class GlobalResourceHandlerTest {
+
+    @Test
+    public void globalResourceHandlerShouldWorkWithEncodedFilename() throws IOException {
+        assertEncodedFilenameIsHandled("simple.txt", "simple.txt");
+        assertEncodedFilenameIsHandled("with spaces.txt", "with+spaces.txt");
+        assertEncodedFilenameIsHandled("with # hash.txt", "with+%23+hash.txt");
+        assertEncodedFilenameIsHandled("with ; semicolon.txt", "with+%3B+semicolon.txt");
+        assertEncodedFilenameIsHandled("with , comma.txt", "with+%2C+comma.txt");
+
+        // ResourceReference.encodeFileName does not encode slashes and backslashes
+        // See comment inside2 method for more details
+        assertEncodedFilenameIsHandled("with \\ backslash.txt", "with+\\+backslash.txt");
+        assertEncodedFilenameIsHandled("with / slash.txt", "with+/+slash.txt");
+    }
+
+    private void assertEncodedFilenameIsHandled(String filename, String expectedFilename) throws IOException {
+        DownloadStream stream = mock(DownloadStream.class);
+        ConnectorResource resource = mock(ConnectorResource.class);
+        when(resource.getFilename()).thenReturn(filename);
+        when(resource.getStream()).thenReturn(stream);
+
+        UI ui = new MockUI() {
+            @Override
+            public int getUIId() {
+                return 0;
+            }
+        };
+        ClientConnector connector = mock(LegacyComponent.class);
+        when(connector.getUI()).thenReturn(ui);
+
+        GlobalResourceHandler handler = new GlobalResourceHandler();
+        handler.register(resource, connector);
+
+        // Verify that file name has been encoded
+        String uri = handler.getUri(connector, resource);
+        assertThat(uri, endsWith("/" + expectedFilename));
+
+        VaadinSession session = mock(VaadinSession.class);
+        VaadinRequest request = mock(VaadinRequest.class);
+        VaadinResponse response = mock(VaadinResponse.class);
+
+        // getPathInfo return path decoded but without decoding plus as spaces
+        when(request.getPathInfo()).thenReturn("APP/global/0/legacy/0/"+ filename.replace(" ", "+"));
+        when(session.getUIById(anyInt())).thenReturn(ui);
+
+        // Verify that decoded path info is correctly handled
+        assertTrue("Request not handled", handler.handleRequest(session, request, response));
+        verify(stream).writeResponse(request, response);
+    }
+}