/* * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.esigate.extension.parallelesi; import java.io.IOException; import java.util.Date; import java.util.concurrent.ExecutionException; import org.apache.http.HttpStatus; import org.apache.http.impl.cookie.BasicClientCookie; import org.esigate.ConfigurationException; import org.esigate.HttpErrorPage; public class IncludeElementTest extends AbstractElementTest { private static final long TEN_SECONDS = 10L * 1000L; @Override protected void setUp() { super.setUp(); addResource("/test", "test"); addResource("http://www.foo.com/test", "test"); } public void testIncludeProvider() throws IOException, HttpErrorPage { String page = "before <esi:include src=\"$(PROVIDER{mock})/test\" /> after"; String result = render(page); assertEquals("before test after", result); } public void testIncludeProvider10() throws IOException, HttpErrorPage { int nb = 10; String page = "before "; for (int i = 0; i < nb; i++) { page = page + "<esi:include src=\"$(PROVIDER{mock})/test\" />"; } page = page + " after"; String result = render(page); String expected = "before "; for (int i = 0; i < nb; i++) { expected = expected + "test"; } expected = expected + " after"; assertEquals(expected, result); } public void testIncludeProviderLegacy() throws IOException, HttpErrorPage { String page = "before <esi:include src=\"$PROVIDER({mock})/test\" /> after"; String result = render(page); assertEquals("before test after", result); } public void testIncludeAbsolute() throws IOException, HttpErrorPage { String page = "before <esi:include src=\"http://www.foo.com/test\" /> after"; String result = render(page); assertEquals("before test after", result); } public void testIncludeFragment() throws IOException, HttpErrorPage { String page = "before <esi:include src=\"$(PROVIDER{mock})/testFragment\" fragment =\"myFragment\" /> after"; addResource("/testFragment", "before fragment " + "<esi:fragment name=\"myFragment\">---fragment content---</esi:fragment>" + " after fragment"); String result = render(page); assertEquals("before ---fragment content--- after", result); } public void testIncludeQueryString() throws IOException, HttpErrorPage { incomingRequest("http://localhost/test?queryparameter1=test&queryparameter2=test2"); String page = "before <esi:include src=\"$(PROVIDER{mock})/test?$(QUERY_STRING)\" /> after"; addResource("/test?queryparameter1=test&queryparameter2=test2", "query OK"); String result = render(page); assertEquals("before query OK after", result); } public void testIncludeQueryStringParameter() throws IOException, HttpErrorPage { incomingRequest("http://localhost/test?queryparameter1=test&queryparameter2=test2"); String page = "before <esi:include src=\"$(PROVIDER{mock})/$(QUERY_STRING{queryparameter2})\" /> after"; addResource("/test2", "queryparameter2 OK"); String result = render(page); assertEquals("before queryparameter2 OK after", result); } public void testIncludeInlineCache() throws IOException, HttpErrorPage { String page = "before <esi:include src='$(PROVIDER{mock})/inline-cache' /> after"; InlineCache.storeFragment("$(PROVIDER{mock})/inline-cache", null, false, null, "---inline cache item---"); String result = render(page); assertEquals("before ---inline cache item--- after", result); InlineCache.storeFragment("$(PROVIDER{mock})/inline-cache", new Date(System.currentTimeMillis() + TEN_SECONDS), false, null, "---updated inline cache item---"); result = render(page); assertEquals("before ---updated inline cache item--- after", result); InlineCache.storeFragment("$(PROVIDER{mock})/inline-cache", new Date(System.currentTimeMillis() - TEN_SECONDS), false, null, "---expired inline cache item---"); addResource("/inline-cache", "---fetched inline cache item---"); result = render(page); assertEquals("before ---fetched inline cache item--- after", result); } public void testIncludeInlineElement() throws IOException, HttpErrorPage { String page = "before <esi:include src='$(PROVIDER{mock})/inline-cache' /> after"; String inline = "<esi:inline name='$(PROVIDER{mock})/inline-cache' " + "fetchable='false'>---inline cache item---</esi:inline>"; addResource("/inline-cache", "---fetched inline cache item---"); String result = render(page); assertEquals("before ---fetched inline cache item--- after", result); result = render(inline); assertEquals("", result); result = render(page); assertEquals("before ---inline cache item--- after", result); // Note: inline and include in the same page may produce unpredictable // results // because of parallel execution } public void testIncludeReplaceElementFragment() throws IOException, HttpErrorPage { String page = "before <esi:include src='$(PROVIDER{mock})/include-replace' >" + "<esi:replace fragment='replaceable-fragment'>" + "<esi:vars>$(HTTP_COOKIE{cookieName})</esi:vars>" + "</esi:replace>" + "</esi:include> after"; String includedPage = "-incl-page-start" + " <esi:fragment name='replaceable-fragment'>replaced content</esi:fragment>" + " <esi:fragment name='untouched-fragment' />" + " incl-page-end-"; getRequestBuilder().addCookie(new BasicClientCookie("cookieName", "fragment replaced")); addResource("/include-replace", includedPage); String result = render(page); assertEquals("before -incl-page-start fragment replaced incl-page-end- after", result); } public void testIncludeReplaceElementRegexp() throws IOException, HttpErrorPage { String page = "before <esi:include src='$(PROVIDER{mock})/include-replace' >" + "<esi:replace regexp='replaceable-regexp'>" + "<esi:vars>$(HTTP_COOKIE{cookieName})</esi:vars>" + "</esi:replace>" + "</esi:include> after"; String includedPage = "-incl-page-start" + " <esi:fragment name='untouched-fragment'>zzz</esi:fragment>" + " replaceable-regexp" + " incl-page-end-"; getRequestBuilder().addCookie(new BasicClientCookie("cookieName", "regexp replaced")); addResource("/include-replace", includedPage); String result = render(page); assertEquals("before -incl-page-start zzz regexp replaced incl-page-end- after", result); } public void testIncludeReplaceElementExpression() throws IOException, HttpErrorPage { String page = "before <esi:include src='$(PROVIDER{mock})/include-replace' >" + "<esi:replace expression='replaceable-regexp'>" + "<esi:vars>$(HTTP_COOKIE{cookieName})</esi:vars>" + "</esi:replace>" + "</esi:include> after"; String includedPage = "-incl-page-start" + " <esi:fragment name='untouched-fragment'>zzz</esi:fragment>" + " replaceable-regexp" + " incl-page-end-"; getRequestBuilder().addCookie(new BasicClientCookie("cookieName", "regexp replaced")); addResource("/include-replace", includedPage); String result = render(page); assertEquals("before -incl-page-start zzz regexp replaced incl-page-end- after", result); } public void testIncludeXpath() throws IOException, HttpErrorPage { String page = "before " + "<esi:include src='$(PROVIDER{mock})/inline-xpath' xpath='//html:body/text()' />" + " after"; addResource("/inline-xpath", "<html><title>The header</title><body>-the body-<br><ul><li>list item</li></ul></body></html>"); String result = render(page); assertEquals("before -the body- after", result); } public void testIncludeXpathSeveralMatchingNodes() throws IOException, HttpErrorPage { String page = "before " + "<esi:include src='$(PROVIDER{mock})/inline-xpath' " + "xpath='//html:body/html:ul/html:li/text()' />" + " after"; addResource("/inline-xpath", "<html><title>The header</title><body>-the body-<br>" + "<ul><li>list item 1</li><li>list item 2</li></ul></body></html>"); String result = render(page); assertEquals("before list item 1list item 2 after", result); } public void testIncludeXSLT() throws IOException, HttpErrorPage { String page = "before " + "<esi:include src='$(PROVIDER{mock})/inline-xslt' " + "stylesheet=\"http://www.foo.com/test.xsl\" />" + " after"; addResource("/inline-xslt", "<html><body>The body<br></body></html>"); addResource("http://www.foo.com/test.xsl", "<?xml version=\"1.0\"?>" + "<xsl:stylesheet version=\"1.0\" xmlns=\"http://www.w3.org/1999/xhtml\" " + "xmlns:html=\"http://www.w3.org/1999/xhtml\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">" + "<xsl:output method=\"xml\" omit-xml-declaration=\"yes\"/>" + "<xsl:template match=\"//html:body\">" + "<xsl:copy-of select=\".\"/>" + "</xsl:template>" + "</xsl:stylesheet>"); String result = render(page); assertEquals("before <body>The body<br /></body> after", result); } public void testIncludeAlt1() throws IOException, HttpErrorPage { String page = "before " + "<esi:include src='$(PROVIDER{mock})/alt-url' alt=\"http://www.foo.com/test\" />" + " after"; addResource("/alt-url", "---fetched alt url---"); String result = render(page); assertEquals("before ---fetched alt url--- after", result); } public void testIncludeAlt2() throws IOException, HttpErrorPage { String page = "before " + "<esi:include src='$(PROVIDER{mock})/not-found' alt=\"http://www.foo.com/test\" />" + " after"; String result = render(page); assertEquals("before test after", result); } /** * Test src + alt + onerror combined. * <p> * See 0000262: Unable to use both alt and onerror in esi:include * <p> * http://www.esigate.org/mantisbt/view.php?id=262 * <p> * See http://www.akamai.com/dl/technical_publications/esi_faq.pdf * <p> * What happens if the src and alt tags cannot be retrieved? If the edge server can fetch neither the src object nor * the alt object, it returns a 404 HTTP error with a simple error message—unless the onerror attribute is present. * The onerror attribute can be used with an src only or with both an src and alt attempt. If onerror=“continue” is * specified and the src and alt fail to fetch the object, ESI deletes the include tag and serves the page without * any object replacing the include statement. * <p> * When onerror=“continue” is set and the fetch fails, the edge server does not serve a default object. Without the * onerror attribute, the edge server attempts to fetch a default object if one is specified in the configuration * file. The default object can be processed by ESI. However, if anything goes wrong during ESI processing, the * result is that the edge server will send an error to the client. If you choose to use ESI in default objects, the * ESI code should be very simple and well-tested. For more information on error handling, see “Exception and Error * Handling” in the ESI Developer’s Guide. For information on using onerror inside ESI’s explicit exception handling * method, see the try block documentation in the same document. * * @throws IOException * @throws HttpErrorPage */ public void testIncludeAltOnError() throws IOException, HttpErrorPage { String page = "before " + "<esi:include src='$(PROVIDER{mock})/not-found' " + "alt=\"$(PROVIDER{mock})/not-found2\" onerror=\"continue\"/>" + " after"; String result = render(page); assertEquals("before after", result); } public void testOnError() throws IOException { String page = "before <esi:include src=\"http://www.foo.com/test-onerror\" /> after"; try { render(page); fail("should throw HttpErrorPage"); } catch (HttpErrorPage e) { assertEquals(HttpStatus.SC_NOT_FOUND, e.getHttpResponse().getStatusLine().getStatusCode()); } } public void testOnErrorContinue() throws IOException, HttpErrorPage { String page = "before <esi:include src=\"http://www.foo.com/test-onerror\" onerror=\"continue\"/> after"; String result = render(page); assertEquals("before after", result); } public void testIncludeTagContentShouldBeRemoved() throws IOException, HttpErrorPage { String page = "before <esi:include src=\"$(PROVIDER{mock})/testFragment\" " + "fragment =\"myFragment\">Content to be removed</esi:include> after"; addResource("/testFragment", "before fragment " + "<esi:fragment name=\"myFragment\">---fragment content---</esi:fragment>" + " after fragment"); String result = render(page); assertEquals("before ---fragment content--- after", result); } public void testIncludeFragmentNested() throws IOException, HttpErrorPage { String page = "before <esi:include src='$(PROVIDER{mock})/fragment' fragment='test' /> after"; String includedPage = "-incl-page-start <esi:fragment name='test'>-" + "<esi:fragment name='test'>content</esi:fragment>" + "-</esi:fragment> incl-page-end-"; addResource("/fragment", includedPage); String result = render(page); assertEquals("before -content- after", result); } public void testIncludeFragmentReplaceNested() throws IOException, HttpErrorPage { String page = "before <esi:include src='$(PROVIDER{mock})/fragment'>" + "<esi:replace fragment='test'>replacement</esi:replace" + "></esi:include> after"; String includedPage = "-incl-page-start <esi:fragment name='test'>-" + "<esi:fragment name='test'>content</esi:fragment>" + "-</esi:fragment> incl-page-end-"; addResource("/fragment", includedPage); String result = render(page); assertEquals("before -incl-page-start replacement incl-page-end- after", result); } public void testIncludeFragmentReplaceFragment() throws IOException, HttpErrorPage { String page = "before <esi:include src='$(PROVIDER{mock})/fragment' fragment='toInclude'>" + "<esi:replace fragment='toReplace'>replacement</esi:replace>" + "</esi:include> after"; String includedPage = "-incl-page-start <esi:fragment name='toInclude'>begin include " + "<esi:fragment name='toReplace'>content to replace</esi:fragment>" + " end include</esi:fragment> incl-page-end-"; addResource("/fragment", includedPage); String result = render(page); assertEquals("before begin include replacement end include after", result); } public void testIncludeProviderWithCharactersBeforeProvider() throws IOException, HttpErrorPage { String page = "<esi:include src=\"somechars$(PROVIDER{mock})/test\" />"; addResource("/test", "code fragment"); String result = render(page); String expected = "code fragment"; assertEquals(expected, result); page = "<esi:include src=\"somechars$PROVIDER({mock})/test\" />"; result = render(page); expected = "code fragment"; assertEquals(expected, result); } public void testIncludeProviderWithUnknownProvider() throws IOException, HttpErrorPage { // Test unknownProvider => error try { String page = "Before <esi:include src=\"$(PROVIDER{unknown})/test\" /> After"; render(page); fail("Should have ConfigurationException"); } catch (IOException e) { ExecutionException cause = (ExecutionException) e.getCause(); ConfigurationException causeOrigine = (ConfigurationException) cause.getCause(); assertEquals("No configuration properties found for factory : unknown", causeOrigine.getMessage()); } // Test unknownProvider => with onerror="continue String page = "Before <esi:include src=\"$(PROVIDER{unknown})/test\" onerror=\"continue\"/> After"; String result = render(page); String expected = "Before After"; assertEquals(expected, result); // Test unknownProvider => with alt page = "Before <esi:include src=\"$(PROVIDER{unknown})/test\" alt=\"http://www.foo.com/test\" /> After"; result = render(page); expected = "Before test After"; assertEquals(expected, result); // Test unknownProvider => with alt with unknowProvider try { page = "Before <esi:include src=\"$(PROVIDER{unknown})/test\" alt=\"$(PROVIDER{unknown2})/test2\" /> After"; render(page); fail("Should have ConfigurationException"); } catch (IOException e) { ExecutionException cause = (ExecutionException) e.getCause(); ConfigurationException causeOrigine = (ConfigurationException) cause.getCause(); assertEquals("No configuration properties found for factory : unknown2", causeOrigine.getMessage()); } // Test unknownProvider => with alt with unknowProvider an // onerror=continue page = "Before <esi:include src=\"$(PROVIDER{unknown})/test\" alt=\"$(PROVIDER{unknown2})/test2\" onerror=\"continue\"/> After"; result = render(page); expected = "Before After"; assertEquals(expected, result); } }