IndexHtmlRequestHandler extends JavaScriptBootstrapHandler { @Override public boolean synchronizedHandleRequest(VaadinSession session, VaadinRequest request, VaadinResponse response) throws IOException { DeploymentConfiguration config = session.getConfiguration(); Document indexDocument = config.isProductionMode() ? getCachedIndexHtmlDocument(request.getService()) : getIndexHtmlDocument(request.getService()); prependBaseHref(request, indexDocument); JsonObject initialJson = Json.createObject(); if (request.getService().getBootstrapInitialPredicate() .includeInitialUidl(request)) { includeInitialUidl(initialJson, session, request, response); indexHtmlResponse = new IndexHtmlResponse(request, response, indexDocument, UI.getCurrent()); session.setAttribute(SERVER_ROUTING, Boolean.TRUE); } else { indexHtmlResponse = new IndexHtmlResponse(request, response, indexDocument); } addInitialFlow(initialJson, indexDocument, session); configureErrorDialogStyles(indexDocument); showWebpackErrors(indexDocument); response.setContentType(CONTENT_TYPE_TEXT_HTML_UTF_8); VaadinContext context = session.getService().getContext(); AppShellRegistry registry = AppShellRegistry.getInstance(context); if (!config.isProductionMode()) { UsageStatisticsExporter.exportUsageStatisticsToDocument(indexDocument); } setupPwa(indexDocument, session.getService()); registry.modifyIndexHtml(indexDocument, request); request.getService().modifyIndexHtmlResponse(indexHtmlResponse); if (config.isDevModeLiveReloadEnabled()) { addDevmodeGizmo(indexDocument, session, request); } try { response.getOutputStream() .write(indexDocument.html().getBytes(UTF_8)); } catch (IOException e) { getLogger().error("Error writing 'index.html' to response", e); return false; } return true; }  @Override boolean synchronizedHandleRequest(VaadinSession session,
            VaadinRequest request, VaadinResponse response);  }
@Test public void serveIndexHtml_requestWithRootPath_serveContentFromTemplate() throws IOException { indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/"), response); String indexHtml = responseOutput .toString(StandardCharsets.UTF_8.name()); Assert.assertTrue( "Response should have content from the index.html template", indexHtml.contains("index.html template content")); Assert.assertTrue( "Response should have styles for the system-error dialogs", indexHtml.contains(".v-system-error")); }
@Test public void serveNotFoundIndexHtml_requestWithRootPath_failsWithIOException() throws IOException { VaadinServletRequest vaadinServletRequest = createVaadinRequest("/"); VaadinService vaadinService = vaadinServletRequest.getService(); 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 { 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 { boolean renamed = indexHtmlFileTmp.renameTo(indexHtmlFile); assertTrue(renamed); } }
@Test public void serveIndexHtml_requestWithRootPath_hasBaseHrefElement() throws IOException { indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/"), response); String indexHtml = responseOutput .toString(StandardCharsets.UTF_8.name()); Assert.assertTrue("Response should have correct base href", indexHtml.contains("<base href=\".\"")); }
@Test public void serveIndexHtml_requestWithSomePath_hasBaseHrefElement() throws IOException { indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/some/path"), response); String indexHtml = responseOutput .toString(StandardCharsets.UTF_8.name()); Assert.assertTrue("Response should have correct base href", indexHtml.contains("<base href=\"./..\"")); }
@Test public void bootstrapListener_addListener_responseIsModified() throws IOException { service.addIndexHtmlRequestListener(evt -> evt.getDocument().head() .getElementsByTag("script").remove()); service.addIndexHtmlRequestListener(evt -> { evt.getDocument().head().appendElement("script").attr("src", "testing.1"); }); service.addIndexHtmlRequestListener(evt -> evt.getDocument().head() .appendElement("script").attr("src", "testing.2")); indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/"), response); String indexHtml = responseOutput .toString(StandardCharsets.UTF_8.name()); Document document = Jsoup.parse(indexHtml); Elements scripts = document.head().getElementsByTag("script"); Assert.assertEquals(2, scripts.size()); Assert.assertEquals("testing.1", scripts.get(0).attr("src")); Assert.assertEquals("testing.2", scripts.get(1).attr("src")); }
@Test public void should_add_initialUidl_when_includeInitialBootstrapUidl() throws IOException { deploymentConfiguration.setEagerServerLoad(true); indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/"), response); String indexHtml = responseOutput .toString(StandardCharsets.UTF_8.name()); Document document = Jsoup.parse(indexHtml); Elements scripts = document.head().getElementsByTag("script"); Assert.assertEquals(1, scripts.size()); Assert.assertEquals("", scripts.get(0).attr("initial")); Assert.assertTrue( scripts.get(0).toString().contains("Could not navigate")); Mockito.verify(session, Mockito.times(1)).setAttribute(SERVER_ROUTING, Boolean.TRUE); }
@Test public void should_not_add_initialUidl_when_not_includeInitialBootstrapUidl() throws IOException { indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/"), response); String indexHtml = responseOutput .toString(StandardCharsets.UTF_8.name()); Document document = Jsoup.parse(indexHtml); Elements scripts = document.head().getElementsByTag("script"); Assert.assertEquals(1, scripts.size()); Assert.assertEquals("window.Vaadin = {TypeScript: {}};", scripts.get(0).childNode(0).toString()); Assert.assertEquals("", scripts.get(0).attr("initial")); Mockito.verify(session, Mockito.times(0)).setAttribute(SERVER_ROUTING, Boolean.TRUE); }
@Test public void should_initialize_UI_and_add_initialUidl_when_valid_route() throws IOException { deploymentConfiguration.setEagerServerLoad(true); service.setBootstrapInitialPredicate(request -> { return request.getPathInfo().equals("/"); }); indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/"), response); String indexHtml = responseOutput .toString(StandardCharsets.UTF_8.name()); Document document = Jsoup.parse(indexHtml); Elements scripts = document.head().getElementsByTag("script"); Assert.assertEquals(1, scripts.size()); Assert.assertEquals("", scripts.get(0).attr("initial")); String scriptContent = scripts.get(0).toString(); Assert.assertTrue(scriptContent.contains("Could not navigate")); Assert.assertFalse("Initial object content should not be escaped", scriptContent.contains("&lt;") || scriptContent.contains("&gt;")); Assert.assertNotNull(UI.getCurrent()); }
@Test public void should_not_initialize_UI_and_add_initialUidl_when_invalid_route() throws IOException { deploymentConfiguration.setEagerServerLoad(true); service.setBootstrapInitialPredicate(request -> { return request.getPathInfo().equals("/"); }); indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/foo"), response); String indexHtml = responseOutput .toString(StandardCharsets.UTF_8.name()); Document document = Jsoup.parse(indexHtml); Elements scripts = document.head().getElementsByTag("script"); Assert.assertEquals(1, scripts.size()); Assert.assertEquals("window.Vaadin = {TypeScript: {}};", scripts.get(0).childNode(0).toString()); Assert.assertEquals("", scripts.get(0).attr("initial")); Assert.assertNull(UI.getCurrent()); }
@Test public void should_include_token_in_dom_when_return_not_null_csrfToken_in_session() throws IOException { Mockito.when(session.getCsrfToken()).thenReturn("foo"); indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/"), response); String indexHtml = responseOutput .toString(StandardCharsets.UTF_8.name()); Document document = Jsoup.parse(indexHtml); Elements scripts = document.head().getElementsByTag("script"); Assert.assertEquals(1, scripts.size()); Assert.assertTrue(scripts.get(0).childNode(0).toString().contains( "window.Vaadin = {TypeScript: {\"csrfToken\":\"foo\"")); Assert.assertEquals("", scripts.get(0).attr("initial")); }
@Test public void should_not_include_token_in_dom_when_return_null_csrfToken_in_session() throws IOException { indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/"), response); String indexHtml = responseOutput .toString(StandardCharsets.UTF_8.name()); Document document = Jsoup.parse(indexHtml); Elements scripts = document.head().getElementsByTag("script"); Assert.assertEquals(1, scripts.size()); Assert.assertFalse(scripts.get(0).childNode(0).toString() .contains("window.Vaadin = {Flow: {\"csrfToken\":")); Assert.assertEquals("", scripts.get(0).attr("initial")); }
@Test public void should_use_client_routing_when_there_is_a_router_call() throws IOException { deploymentConfiguration.setEagerServerLoad(true); indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/"), response); Mockito.verify(session, Mockito.times(1)).setAttribute(SERVER_ROUTING, Boolean.TRUE); Mockito.verify(session, Mockito.times(0)).setAttribute(SERVER_ROUTING, Boolean.FALSE); ((JavaScriptBootstrapUI) UI.getCurrent()).connectClient("foo", "bar", "/foo"); Mockito.verify(session, Mockito.times(1)).setAttribute(SERVER_ROUTING, Boolean.FALSE); }
@Test public void should_attachWebpackErrors() throws Exception { File npmFolder = temporaryFolder.getRoot(); String baseDir = npmFolder.getAbsolutePath(); new File(baseDir, FrontendUtils.WEBPACK_CONFIG).createNewFile(); createStubWebpackServer("Failed to compile", 300, baseDir); deploymentConfiguration.setEnableDevServer(true); deploymentConfiguration.setProductionMode(false); DevModeHandler handler = DevModeHandler.start(0, deploymentConfiguration, npmFolder, CompletableFuture.completedFuture(null)); Method join = DevModeHandler.class.getDeclaredMethod("join"); join.setAccessible(true); join.invoke(handler); Method runningPort = DevModeHandler.class .getDeclaredMethod("getRunningDevServerPort", File.class); runningPort.setAccessible(true); int port = (Integer) runningPort.invoke(handler, npmFolder); httpServer = createStubWebpackTcpListener(port, 200, "<html></html>"); indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/"), response); String indexHtml = responseOutput .toString(StandardCharsets.UTF_8.name()); Assert.assertTrue("Should have a system error dialog", indexHtml.contains( "<div class=\"v-system-error\" onclick=\"this.parentElement.removeChild(this)\">")); Assert.assertTrue("Should show webpack failure error", indexHtml.contains("Failed to compile")); }
@Test public void should_not_add_metaElements_when_not_appShellPresent() throws Exception { indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/"), response); String indexHtml = responseOutput .toString(StandardCharsets.UTF_8.name()); Document document = Jsoup.parse(indexHtml); Elements elements = document.head().getElementsByTag("meta"); assertEquals(2, elements.size()); }
@Test public void should_add_metaAndPwa_Inline_Elements_when_appShellPresent() throws Exception { AppShellRegistry registry = new AppShellRegistry(); registry.setShell(AppShellWithPWA.class); mocks.setAppShellRegistry(registry); indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/"), response); String indexHtml = responseOutput .toString(StandardCharsets.UTF_8.name()); Document document = Jsoup.parse(indexHtml); Elements elements = document.head().getElementsByTag("meta"); assertEquals(5, elements.size()); assertEquals("viewport", elements.get(1).attr("name")); assertEquals("my-viewport", elements.get(1).attr("content")); assertEquals("apple-mobile-web-app-capable", elements.get(2).attr("name")); assertEquals("yes", elements.get(2).attr("content")); assertEquals("theme-color", elements.get(3).attr("name")); assertEquals("#ffffff", elements.get(3).attr("content")); assertEquals("apple-mobile-web-app-status-bar-style", elements.get(4).attr("name")); assertEquals("#ffffff", elements.get(4).attr("content")); Elements headInlineAndStyleElements = document.head() .getElementsByTag("style"); assertEquals(2, headInlineAndStyleElements.size()); assertEquals("text/css", headInlineAndStyleElements.get(1).attr("type")); assertEquals("body,#outlet{width:my-width;height:my-height;}", headInlineAndStyleElements.get(1).childNode(0).toString()); }
@Test public void should_add_elements_when_appShellWithConfigurator() throws Exception { AppShellRegistry registry = new AppShellRegistry(); registry.setShell(MyAppShellWithConfigurator.class); mocks.setAppShellRegistry(registry); indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/"), response); String indexHtml = responseOutput .toString(StandardCharsets.UTF_8.name()); Document document = Jsoup.parse(indexHtml); Elements elements = document.head().getElementsByTag("meta"); assertEquals(4, elements.size()); assertEquals("UTF-8", elements.get(0).attr("charset")); assertEquals("viewport", elements.get(1).attr("name")); assertEquals("my-viewport", elements.get(1).attr("content")); assertEquals("foo", elements.get(2).attr("name")); assertEquals("bar", elements.get(2).attr("content")); assertEquals("lorem", elements.get(3).attr("name")); assertEquals("ipsum", elements.get(3).attr("content")); assertEquals("my-title", document.head().getElementsByTag("title") .get(0).childNode(0).toString()); Elements headInlineAndStyleElements = document.head() .getElementsByTag("style"); assertEquals(3, headInlineAndStyleElements.size()); assertEquals("text/css", headInlineAndStyleElements.get(2).attr("type")); assertEquals("body,#outlet{width:my-width;height:my-height;}", headInlineAndStyleElements.get(2).childNode(0).toString()); Elements bodyInlineElements = document.body() .getElementsByTag("script"); assertEquals(3, bodyInlineElements.size()); }
@Test public void should_export_usage_statistics_in_development_mode() throws IOException { deploymentConfiguration.setProductionMode(false); indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/"), response); String indexHtml = responseOutput .toString(StandardCharsets.UTF_8.name()); Document document = Jsoup.parse(indexHtml); Elements bodyInlineElements = document.body() .getElementsByTag("script"); assertEquals(2, bodyInlineElements.size()); String entries = UsageStatistics.getEntries().map(entry -> { JsonObject json = Json.createObject(); json.put("is", entry.getName()); json.put("version", entry.getVersion()); return json.toString(); }).collect(Collectors.joining(",")); String expected = StringUtil .normaliseWhitespace("window.Vaadin = window.Vaadin || {}; " + "window.Vaadin.registrations = window.Vaadin.registrations || [];\n" + "window.Vaadin.registrations.push(" + entries + ");"); assertEquals(StringUtil.normaliseWhitespace(expected), bodyInlineElements.get(1).childNode(0).outerHtml()); }
@Test public void should_NOT_export_usage_statistics_in_production_mode() throws IOException { deploymentConfiguration.setProductionMode(true); indexHtmlRequestHandler.synchronizedHandleRequest(session, createVaadinRequest("/"), response); String indexHtml = responseOutput .toString(StandardCharsets.UTF_8.name()); Document document = Jsoup.parse(indexHtml); Elements bodyInlineElements = document.body() .getElementsByTag("script"); assertEquals(1, bodyInlineElements.size()); }