diff --git a/flow-server/src/main/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandler.java b/flow-server/src/main/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandler.java index e7a901df2bd..b34dd19fbe2 100644 --- a/flow-server/src/main/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandler.java +++ b/flow-server/src/main/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandler.java @@ -39,6 +39,8 @@ import elemental.json.JsonObject; import elemental.json.impl.JsonUtil; +import java.io.File; + import static com.vaadin.flow.component.internal.JavaScriptBootstrapUI.SERVER_ROUTING; import static com.vaadin.flow.shared.ApplicationConstants.CONTENT_TYPE_TEXT_HTML_UTF_8; import static com.vaadin.flow.shared.ApplicationConstants.CSRF_TOKEN; @@ -172,10 +174,16 @@ private static Document getIndexHtmlDocument(VaadinRequest request) } String frontendDir = FrontendUtils.getProjectFrontendDir( request.getService().getDeploymentConfiguration()); + String indexHtmlFilePath; + if(frontendDir.endsWith(File.separator)) { + indexHtmlFilePath = frontendDir + "index.html"; + } else { + indexHtmlFilePath = frontendDir + File.separatorChar + "index.html"; + } String message = String - .format("Failed to load content of '%1$sindex.html'." - + "It is required to have '%1$sindex.html' file when " - + "using client side bootstrapping.", frontendDir); + .format("Failed to load content of '%1$s'. " + + "It is required to have '%1$s' file when " + + "using client side bootstrapping.", indexHtmlFilePath); throw new IOException(message); } diff --git a/flow-server/src/test/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandlerTest.java b/flow-server/src/test/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandlerTest.java index 11caa930571..9a455f1dfe6 100644 --- a/flow-server/src/test/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandlerTest.java +++ b/flow-server/src/test/java/com/vaadin/flow/server/communication/IndexHtmlRequestHandlerTest.java @@ -21,6 +21,7 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.Method; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Optional; import java.util.concurrent.CompletableFuture; @@ -36,6 +37,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.mockito.Mockito; @@ -46,6 +48,7 @@ import com.vaadin.flow.server.DevModeHandler; import com.vaadin.flow.server.MockServletServiceSessionSetup; import com.vaadin.flow.server.VaadinResponse; +import com.vaadin.flow.server.VaadinService; import com.vaadin.flow.server.VaadinServletRequest; import com.vaadin.flow.server.VaadinSession; import com.vaadin.flow.server.frontend.FrontendUtils; @@ -57,9 +60,13 @@ import elemental.json.JsonObject; import static com.vaadin.flow.component.internal.JavaScriptBootstrapUI.SERVER_ROUTING; +import static com.vaadin.flow.server.Constants.VAADIN_SERVLET_RESOURCES; import static com.vaadin.flow.server.DevModeHandlerTest.createStubWebpackTcpListener; +import static com.vaadin.flow.server.frontend.FrontendUtils.INDEX_HTML; import static com.vaadin.flow.server.frontend.NodeUpdateTestUtil.createStubWebpackServer; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; public class IndexHtmlRequestHandlerTest { private MockServletServiceSessionSetup mocks; @@ -73,6 +80,8 @@ public class IndexHtmlRequestHandlerTest { @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Rule + public ExpectedException exceptionRule = ExpectedException.none(); @Before public void setUp() throws Exception { @@ -103,6 +112,41 @@ public void serveIndexHtml_requestWithRootPath_serveContentFromTemplate() indexHtml.contains(".v-system-error")); } + @Test + public void serveNotFoundIndexHtml_requestWithRootPath_failsWithIOException() + throws IOException { + VaadinServletRequest vaadinServletRequest = createVaadinRequest("/"); + VaadinService vaadinService = vaadinServletRequest.getService(); + + // Finding index.html URL + String indexHtmlPathInProductionMode = VAADIN_SERVLET_RESOURCES + + INDEX_HTML; + URL url = vaadinService.getClassLoader().getResource(indexHtmlPathInProductionMode); + + assertNotNull(url); + File indexHtmlFile = new File(url.getPath()); + File indexHtmlFileTmp = new File(url.getPath() + "_tmp"); + try { + // Renaming file to simulate the absence of index.html + boolean renamed = indexHtmlFile.renameTo(indexHtmlFileTmp); + assertTrue(renamed); + + String expectedError = "Failed to load content of './frontend/index.html'. " + + "It is required to have './frontend/index.html' file " + + "when using client side bootstrapping."; + + exceptionRule.expect(IOException.class); + exceptionRule.expectMessage(expectedError); + + indexHtmlRequestHandler.synchronizedHandleRequest(session, + vaadinServletRequest, response); + } finally { + // Restoring index.html + boolean renamed = indexHtmlFileTmp.renameTo(indexHtmlFile); + assertTrue(renamed); + } + } + @Test public void serveIndexHtml_requestWithRootPath_hasBaseHrefElement() throws IOException {