From: Marco Collovati Date: Wed, 9 May 2018 08:47:58 +0000 (+0200) Subject: URL encode file name in GlobalResourceHandler (#10751) X-Git-Tag: 8.5.0.alpha2~39 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=16e394956f6150928f7eeb20c4650ee504602401;p=vaadin-framework.git URL encode file name in GlobalResourceHandler (#10751) Fixes #10747 --- diff --git a/server/src/main/java/com/vaadin/server/GlobalResourceHandler.java b/server/src/main/java/com/vaadin/server/GlobalResourceHandler.java index 4e07bdcd3b..1d1ffc3467 100644 --- a/server/src/main/java/com/vaadin/server/GlobalResourceHandler.java +++ b/server/src/main/java/com/vaadin/server/GlobalResourceHandler.java @@ -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. *

@@ -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 index 0000000000..55663148d9 --- /dev/null +++ b/server/src/test/java/com/vaadin/server/GlobalResourceHandlerTest.java @@ -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); + } +}