/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.faces.test.servlet31.resourcehandler;
import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.CompressableMimeTypes;
import static com.sun.faces.util.Util.getCurrentLoader;
import static java.lang.Integer.parseInt;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.enumeration;
import static java.util.Locale.US;
import static java.util.TimeZone.getTimeZone;
import static javax.faces.FactoryFinder.LIFECYCLE_FACTORY;
import static javax.faces.application.ResourceHandler.RESOURCE_EXCLUDES_DEFAULT_VALUE;
import static javax.faces.lifecycle.LifecycleFactory.DEFAULT_LIFECYCLE;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.zip.GZIPOutputStream;
import javax.faces.FactoryFinder;
import javax.faces.application.Application;
import javax.faces.application.Resource;
import javax.faces.application.ResourceHandler;
import javax.faces.application.ResourceHandlerWrapper;
import javax.faces.application.ResourceWrapper;
import javax.faces.context.ExternalContext;
import javax.faces.context.ExternalContextWrapper;
import javax.faces.context.FacesContext;
import javax.faces.context.FacesContextWrapper;
import javax.faces.lifecycle.Lifecycle;
import javax.faces.lifecycle.LifecycleFactory;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse;
import javax.servlet.WriteListener;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.application.resource.ResourceHandlerImpl;
import com.sun.faces.application.resource.ResourceManager;
import com.sun.faces.config.WebConfiguration;
import com.sun.faces.config.WebConfiguration.WebContextInitParameter;
import com.sun.faces.context.ExternalContextImpl;
import com.sun.faces.context.FacesContextImpl;
/**
* This servlet does a kind of in-container unit tests for {@link ResourceHandlerImpl}.
* <p>
* These tests have been migrated from cactus based tests and are mostly Mojarra specific.
* Some of these should be migrated to regular univeral JSF tests.
* <p>
* By default a single request runs all tests. If a "false" is written to the response a
* test has failed. Individual tests can be run via the <code>test</code> request parameter
* and the name of the method, e.g. <code>/testHandleResourceRequest?test=testAjaxIsAvailable</code>
*
*/
@WebServlet("/testHandleResourceRequest")
public class ResourceHandlerTestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/* HTTP Date format required by the HTTP/1.1 RFC */
private static final String RFC1123_DATE_PATTERN =
"EEE, dd MMM yyyy HH:mm:ss zzz";
private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Run the tests, each one in a newly set up faces environment
run("testAjaxIsAvailable", this::testAjaxIsAvailable, request, response);
run("testAjaxCompression", this::testAjaxCompression, request, response);
run("testCreateResource", this::testCreateResource, request, response);
run("testIsResourceRequestPrefixMapped", this::testIsResourceRequestPrefixMapped, request, response);
run("testIsResourceRequestExtensionMapped", this::testIsResourceRequestExtensionMapped, request, response);
run("testHandleResourceRequestExcludesPrefixMapped1", this::testHandleResourceRequestExcludesPrefixMapped1, request, response);
run("testHandleResourceRequestExcludesPrefixMapped2", this::testHandleResourceRequestExcludesPrefixMapped2, request, response);
run("testHandleResourceRequestExcludesPrefixMapped3", this::testHandleResourceRequestExcludesPrefixMapped3, request, response);
run("testHandleResourceRequestExcludesPrefixMapped4", this::testHandleResourceRequestExcludesPrefixMapped4, request, response);
run("testHandleResourceRequestExcludeExtensionMapped1", this::testHandleResourceRequestExcludeExtensionMapped1, request, response);
run("testHandleResourceRequestExcludeExtensionMapped2", this::testHandleResourceRequestExcludeExtensionMapped2, request, response);
run("testHandleResourceRequestExcludeExtensionMapped3", this::testHandleResourceRequestExcludeExtensionMapped3, request, response);
run("testHandleResourceRequestExcludeExtensionMapped4", this::testHandleResourceRequestExcludeExtensionMapped4, request, response);
run("testUserSpecifiedResourceExclude1", this::testUserSpecifiedResourceExclude1, request, response);
run("testUserSpecifiedResourceExclude2", this::testUserSpecifiedResourceExclude2, request, response);
run("testUserSpecifiedResourceExclude3", this::testUserSpecifiedResourceExclude3, request, response);
run("testLibraryExistsNegative", this::testLibraryExistsNegative, request, response);
run("testHandleResourceRequest1", this::testHandleResourceRequest1, request, response);
run("testHandleResourceRequest2", this::testHandleResourceRequest2, request, response);
run("testHandleResourceRequest3", this::testHandleResourceRequest3, request, response);
run("testHandleResourceRequest4", this::testHandleResourceRequest4, request, response);
run("testHandleResourceRequest5", this::testHandleResourceRequest5, request, response);
run("testHandleResourceRequest6", this::testHandleResourceRequest6, request, response, CompressableMimeTypes, "image/gif");
run("testHandleResourceRequest7", this::testHandleResourceRequest7, request, response, CompressableMimeTypes, "image/gif");
run("testHandleResourceRequest8", this::testHandleResourceRequest8, request, response, CompressableMimeTypes, "image/gif");
run("testHandleResourceRequest9", this::testHandleResourceRequest9, request, response, CompressableMimeTypes, "image/gif");
run("testHandleResourceRequest10", this::testHandleResourceRequest10, request, response, CompressableMimeTypes, "image/gif");
run("testHandleResourceRequest11", this::testHandleResourceRequest11, request, response, CompressableMimeTypes, "image/gif");
run("testHandleResourceRequest12", this::testHandleResourceRequest12, request, response, CompressableMimeTypes, "image/gif");
run("testHandleResourceRequest13", this::testHandleResourceRequest13, request, response, CompressableMimeTypes, "image/gif");
run("testHandleResourceRequest14", this::testHandleResourceRequest14, request, response);
run("testHandleResourceRequest15", this::testHandleResourceRequest15, request, response);
run("testHandleResourceRequest16", this::testHandleResourceRequest16, request, response);
}
// ### Test methods
private void testAjaxIsAvailable(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/jsf.js.faces");
response.getWriter().println(handler != null);
response.getWriter().println(handler instanceof ResourceHandlerImpl);
response.getWriter().println(handler.createResource("jsf.js", "javax.faces") != null);
}
private void testAjaxCompression(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/jsf.js.faces");
Resource resource = handler.createResource("jsf-uncompressed.js", "javax.faces");
InputStream stream = resource.getInputStream();
int origSize = getBytes(stream).length;
resource = handler.createResource("jsf.js", "javax.faces");
stream = resource.getInputStream();
int compSize = getBytes(stream).length;
// If we're not getting 30% compression, something's gone horribly wrong.
response.getWriter().println(origSize * 0.7 > compSize);
}
private void testCreateResource(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/jsf.js.faces");
response.getWriter().println(handler != null);
response.getWriter().println(handler instanceof ResourceHandlerImpl);
Resource resource = handler.createResource("duke-nv.gif");
response.getWriter().println(resource != null);
response.getWriter().println(resource.getLibraryName() == null);
response.getWriter().println("duke-nv.gif".equals(resource.getResourceName()));
response.getWriter().println("image/gif".equals(resource.getContentType()));
resource = handler.createResource("duke-nv.gif", "nvLibrary");
response.getWriter().println(resource != null);
response.getWriter().println("nvLibrary".equals(resource.getLibraryName()));
response.getWriter().println("duke-nv.gif".equals(resource.getResourceName()));
response.getWriter().println("image/gif".equals(resource.getContentType()));
resource = handler.createResource("duke-nv.gif", "nvLibrary", "text/xml");
response.getWriter().println(resource != null);
response.getWriter().println("nvLibrary".equals(resource.getLibraryName()));
response.getWriter().println("duke-nv.gif".equals(resource.getResourceName()));
response.getWriter().println("text/xml".equals(resource.getContentType()));
resource = handler.createResource("duke-nv.gif", "nvLibrary", null);
response.getWriter().println(resource != null);
response.getWriter().println("nvLibrary".equals(resource.getLibraryName()));
response.getWriter().println("duke-nv.gif".equals(resource.getResourceName()));
response.getWriter().println("image/gif".equals(resource.getContentType()));
resource = handler.createResource("foo.jpg");
response.getWriter().println(resource == null);
resource = handler.createResource("duke-nv.gif", "nonExistant");
response.getWriter().println(resource == null);
}
private void testIsResourceRequestPrefixMapped(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/faces/");
testRequest.setPathInfo("/javax.faces.resource/duke-nv.gif");
response.getWriter().println(handler.isResourceRequest(facesContext));
}
private void testIsResourceRequestExtensionMapped(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-nv.gif.faces");
response.getWriter().println(handler.isResourceRequest(facesContext));
}
private void testHandleResourceRequestExcludesPrefixMapped1(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/faces/");
testRequest.setPathInfo("/javax.faces.resource/test.jsp");
handler.handleResourceRequest(facesContext);
response.getWriter().println(wrapper.getStatus() == 404);
}
private void testHandleResourceRequestExcludesPrefixMapped2(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/faces/");
testRequest.setPathInfo("/javax.faces.resource/test.properties");
handler.handleResourceRequest(facesContext);
response.getWriter().println(wrapper.getStatus() == 404);
}
private void testHandleResourceRequestExcludesPrefixMapped3(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/faces/");
testRequest.setPathInfo("/javax.faces.resource/test.xhtml");
handler.handleResourceRequest(facesContext);
response.getWriter().println(wrapper.getStatus() == 404);
}
private void testHandleResourceRequestExcludesPrefixMapped4(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/faces/");
testRequest.setPathInfo("/javax.faces.resource/test.class");
facesContext.getApplication()
.getResourceHandler()
.handleResourceRequest(facesContext);
response.getWriter().println(wrapper.getStatus() == 404);
}
private void testHandleResourceRequestExcludeExtensionMapped1(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/faces/");
testRequest.setPathInfo("/javax.faces.resource/duke-nv.jsp.faces");
facesContext.getApplication()
.getResourceHandler()
.handleResourceRequest(facesContext);
response.getWriter().println(wrapper.getStatus() == 404);
}
private void testHandleResourceRequestExcludeExtensionMapped2(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/faces/");
testRequest.setPathInfo("/javax.faces.resource/duke-nv.properties.faces");
facesContext.getApplication()
.getResourceHandler()
.handleResourceRequest(facesContext);
response.getWriter().println(wrapper.getStatus() == 404);
}
private void testHandleResourceRequestExcludeExtensionMapped3(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/faces/");
testRequest.setPathInfo("/javax.faces.resource/duke-nv.xhtml.faces");
facesContext.getApplication()
.getResourceHandler()
.handleResourceRequest(facesContext);
response.getWriter().println(wrapper.getStatus() == 404);
}
private void testHandleResourceRequestExcludeExtensionMapped4(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/faces/");
testRequest.setPathInfo("/javax.faces.resource/duke-nv.class.faces");
facesContext.getApplication()
.getResourceHandler()
.handleResourceRequest(facesContext);
response.getWriter().println(wrapper.getStatus() == 404);
}
////////////////////////////////////////////////////////////////////////////
// The next 5 tests validate a user specified exclude.
// In this case, .gif is excluded as a valid resource request.
// This should cause the default exclusions of .jsp, .class, .xhtml, and
// .properties to now be considered valid
////////////////////////////////////////////////////////////////////////////
private void testUserSpecifiedResourceExclude1(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-nv.gif.faces");
// documenting this once - this is hack in order to support dynamic init
// parameters. Unfortunately, the config object (which one can obtain
// the ServletContextWrapper from isn't available at the time the
// 'begin' methods are invoked. So instead, leverage the knowledge that
// the init parameters are checked when the ResourceHandlerImpl is
// constructed and set the init parameters in the context before constructing.
WebConfiguration webconfig = WebConfiguration.getInstance(facesContext.getExternalContext());
webconfig.overrideContextInitParameter(WebConfiguration.WebContextInitParameter.ResourceExcludes, ".gif");
ApplicationAssociate associate = ApplicationAssociate.getInstance(facesContext.getExternalContext());
Application app = facesContext.getApplication();
ResourceHandler oldResourceHandler = app.getResourceHandler();
associate.setResourceManager(new ResourceManager(associate.getResourceCache()));
handler = new ResourceHandlerImpl();
app.setResourceHandler(handler);
try {
handler.handleResourceRequest(facesContext);
response.getWriter().println(wrapper.getStatus() == 404);
} finally {
app.setResourceHandler(oldResourceHandler);
webconfig.overrideContextInitParameter(WebConfiguration.WebContextInitParameter.ResourceExcludes, RESOURCE_EXCLUDES_DEFAULT_VALUE);
}
}
private void testUserSpecifiedResourceExclude2(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/com.sun.faces.application.ApplicationImpl.class.faces");
WebConfiguration webconfig = WebConfiguration.getInstance(facesContext.getExternalContext());
webconfig.overrideContextInitParameter(WebConfiguration.WebContextInitParameter.ResourceExcludes, ".gif");
try {
// TODO: isResourceRequest doesn't seem to take exclusions into account.
facesContext.getApplication().getResourceHandler().isResourceRequest(facesContext);
response.getWriter().println(facesContext.getApplication().getResourceHandler().isResourceRequest(facesContext));
} finally {
webconfig.overrideContextInitParameter(WebConfiguration.WebContextInitParameter.ResourceExcludes, RESOURCE_EXCLUDES_DEFAULT_VALUE);
}
}
private void testUserSpecifiedResourceExclude3(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/com.sun.faces.LogStrings.properties.faces");
WebConfiguration webconfig = WebConfiguration.getInstance(facesContext.getExternalContext());
webconfig.overrideContextInitParameter(WebConfiguration.WebContextInitParameter.ResourceExcludes, ".gif");
try {
facesContext.getApplication().getResourceHandler().isResourceRequest(facesContext);
response.getWriter().println(facesContext.getApplication().getResourceHandler().isResourceRequest(facesContext));
} finally {
webconfig.overrideContextInitParameter(WebConfiguration.WebContextInitParameter.ResourceExcludes, RESOURCE_EXCLUDES_DEFAULT_VALUE);
}
}
private void testLibraryExistsNegative(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-nv.gif.faces");
response.getWriter().println(!facesContext.getApplication().getResourceHandler().libraryExists("oeunhtnhtnhhnhh"));
}
//==========================================================================
// Validate a resource streamed from the docroot of a webapp
//
private void testHandleResourceRequest1(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-nv.gif.faces");
byte[] control = getBytes(facesContext.getExternalContext().getResource("/resources/duke-nv.gif"));
handler.handleResourceRequest(facesContext);
byte[] test = wrapper.getBytes();
response.getWriter().println(Arrays.equals(control, test));
response.getWriter().println(wrapper.containsHeader("content-length"));
response.getWriter().println(wrapper.containsHeader("last-modified"));
response.getWriter().println(wrapper.containsHeader("expires"));
response.getWriter().println(wrapper.containsHeader("etag"));
}
//==========================================================================
// Validate a resource streamed from a JAR
//
private void testHandleResourceRequest2(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-nv.gif.faces");
testRequest.getParameterMap().put("ln", new String[] { "nvLibrary-jar" });
byte[] control = getBytes(getCurrentLoader(this).getResource("META-INF/resources/nvLibrary-jar/duke-nv.gif"));
handler.handleResourceRequest(facesContext);
byte[] test = wrapper.getBytes();
response.getWriter().println(Arrays.equals(control, test));
response.getWriter().println(wrapper.containsHeader("content-length"));
response.getWriter().println(wrapper.containsHeader("last-modified"));
response.getWriter().println(wrapper.containsHeader("expires"));
response.getWriter().println(wrapper.containsHeader("etag"));
}
//==========================================================================
// Validate a 304 is returned when a request contains the If-Modified-Since
// request header and the resource hasn't changed on the server side.
//
private void testHandleResourceRequest3(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-nv.gif.faces");
long currentTime = System.currentTimeMillis(), threeHoursAgo = currentTime - 10800000L;
SimpleDateFormat format = new SimpleDateFormat(RFC1123_DATE_PATTERN, US);
format.setTimeZone(GMT);
testRequest.getHeadersMap().put("If-Modified-Since", new String[] { format.format(new Date(currentTime)) });
final ExternalContext externalContext = facesContext.getExternalContext();
final FacesContext oldFacesContext = facesContext;
try {
facesContext = new FacesContextWrapper(facesContext) {
{FacesContext.setCurrentInstance(this);}
public ExternalContext getExternalContext() {
return new ExternalContextWrapper(externalContext) {
public URL getResource(String path) throws java.net.MalformedURLException {
URL url = getWrapped().getResource(path);
return new URL(url, url.toExternalForm(), new URLStreamHandler() {
@Override
protected URLConnection openConnection(URL u) throws IOException {
return new URLConnectionWrapper(url.openConnection()) {
public long getLastModified() {
return threeHoursAgo;
};
};
}
});
};
};
};
};
facesContext.getApplication()
.getResourceHandler()
.handleResourceRequest(facesContext);
response.getWriter().println(wrapper.getStatus() == 304);
} finally {
facesContext = new FacesContextWrapper(oldFacesContext) {
{FacesContext.setCurrentInstance(this);}
};
}
}
private void testHandleResourceRequest4(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-v.gif.faces");
testRequest.getParameterMap().put("ln", new String[] { "nvLibrary" });
facesContext.getApplication()
.getResourceHandler()
.handleResourceRequest(facesContext);
response.getWriter().println(wrapper.getStatus() == 404);
}
//==========================================================================
// Validate a 404 is returned when a request for an excluded resource is made
//
private void testHandleResourceRequest5(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-nv.class.faces");
testRequest.getParameterMap().put("ln", new String[] { "nvLibrary" });
facesContext.getApplication()
.getResourceHandler()
.handleResourceRequest(facesContext);
response.getWriter().println(wrapper.getStatus() == 404);
}
//==========================================================================
// Validate a resource streamed from the docroot of a webapp is compressed
//
private void testHandleResourceRequest6(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-nv.gif.faces");
testRequest.getHeadersMap().put("accept-encoding", new String[] { "deflate", "gzip" });
byte[] control = getBytes(facesContext.getExternalContext().getResource("/resources/duke-nv.gif"), true);
handler.handleResourceRequest(facesContext);
byte[] test = wrapper.getBytes();
response.getWriter().println(Arrays.equals(control, test));
response.getWriter().println(wrapper.containsHeader("content-length"));
response.getWriter().println(wrapper.containsHeader("last-modified"));
response.getWriter().println(wrapper.containsHeader("expires"));
response.getWriter().println(wrapper.containsHeader("etag"));
response.getWriter().println(wrapper.containsHeader("content-encoding"));
}
//==========================================================================
// Validate a resource streamed from a JAR is compressed
//
private void testHandleResourceRequest7(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-nv.gif.faces");
testRequest.getParameterMap().put("ln", new String[] { "nvLibrary-jar" });
testRequest.getHeadersMap().put("accept-encoding", new String[] { "gzip,deflate" });
byte[] control = getBytes(getCurrentLoader(this).getResource("META-INF/resources/nvLibrary-jar/duke-nv.gif"), true);
handler.handleResourceRequest(facesContext);
byte[] test = wrapper.getBytes();
response.getWriter().println(Arrays.equals(control, test));
response.getWriter().println(wrapper.containsHeader("content-length"));
response.getWriter().println(wrapper.containsHeader("last-modified"));
response.getWriter().println(wrapper.containsHeader("expires"));
response.getWriter().println(wrapper.containsHeader("etag"));
response.getWriter().println(wrapper.containsHeader("content-encoding"));
}
//==========================================================================
// Validate a resource streamed from the docroot of a webapp isn't compressed
// when the client doesn't send the accept-encoding request header
//
private void testHandleResourceRequest8(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-nv.gif.faces");
byte[] control = getBytes(facesContext.getExternalContext().getResource("/resources/duke-nv.gif"));
handler.handleResourceRequest(facesContext);
byte[] test = wrapper.getBytes();
response.getWriter().println(Arrays.equals(control, test));
response.getWriter().println(wrapper.containsHeader("content-length"));
response.getWriter().println(wrapper.containsHeader("last-modified"));
response.getWriter().println(wrapper.containsHeader("expires"));
response.getWriter().println(wrapper.containsHeader("etag"));
response.getWriter().println(!wrapper.containsHeader("content-encoding"));
}
private void testHandleResourceRequest9(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-nv.gif.faces");
testRequest.getParameterMap().put("ln", new String[] {"nvLibrary-jar"});
byte[] control = getBytes(facesContext.getExternalContext().getResource("/resources/nvLibrary/duke-nv.gif"));
handler.handleResourceRequest(facesContext);
byte[] test = wrapper.getBytes();
response.getWriter().println(Arrays.equals(control, test));
response.getWriter().println(wrapper.containsHeader("content-length"));
response.getWriter().println(wrapper.containsHeader("last-modified"));
response.getWriter().println(wrapper.containsHeader("expires"));
response.getWriter().println(wrapper.containsHeader("etag"));
response.getWriter().println(!wrapper.containsHeader("content-encoding"));
}
//==========================================================================
// Validate an accept-encoding of gzip;q=0 means non-compressed content
// is sent to the user-agent
//
private void testHandleResourceRequest10(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-nv.gif.faces");
testRequest.getParameterMap().put("accept-encoding", new String[] {"gzip;q=0, deflate"});
byte[] control = getBytes(facesContext.getExternalContext().getResource("/resources/duke-nv.gif"), false);
handler.handleResourceRequest(facesContext);
byte[] test = wrapper.getBytes();
response.getWriter().println(Arrays.equals(control, test));
response.getWriter().println(wrapper.containsHeader("content-length"));
response.getWriter().println(wrapper.containsHeader("last-modified"));
response.getWriter().println(wrapper.containsHeader("expires"));
response.getWriter().println(wrapper.containsHeader("etag"));
response.getWriter().println(!wrapper.containsHeader("content-encoding"));
}
//==========================================================================
// Validate an accept-encoding of that doesn't include gzip, and includes
// *;q=0 will not send compressed content to the user-agent
// is sent to the user-agent
//
private void testHandleResourceRequest11(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-nv.gif.faces");
testRequest.getParameterMap().put("accept-encoding", new String[] { "deflate", "*;q=0"});
byte[] control = getBytes(facesContext.getExternalContext().getResource("/resources/duke-nv.gif"), false);
handler.handleResourceRequest(facesContext);
byte[] test = wrapper.getBytes();
response.getWriter().println(Arrays.equals(control, test));
response.getWriter().println(wrapper.containsHeader("content-length"));
response.getWriter().println(wrapper.containsHeader("last-modified"));
response.getWriter().println(wrapper.containsHeader("expires"));
response.getWriter().println(wrapper.containsHeader("etag"));
response.getWriter().println(!wrapper.containsHeader("content-encoding"));
}
//==========================================================================
// Validate an accept-encoding of that doesn't include gzip, and includes
// * will send compressed content
//
private void testHandleResourceRequest12(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-nv.gif.faces");
testRequest.getHeadersMap().put("accept-encoding", new String[] { "identity;q=1.0", "*;q=0.5", "deflate;q=1.0"});
byte[] control = getBytes(facesContext.getExternalContext().getResource("/resources/duke-nv.gif"), true);
handler.handleResourceRequest(facesContext);
byte[] test = wrapper.getBytes();
response.getWriter().println(Arrays.equals(control, test));
response.getWriter().println(wrapper.containsHeader("content-length"));
response.getWriter().println(wrapper.containsHeader("last-modified"));
response.getWriter().println(wrapper.containsHeader("expires"));
response.getWriter().println(wrapper.containsHeader("etag"));
response.getWriter().println(wrapper.containsHeader("content-encoding"));
}
private void testHandleResourceRequest13(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-nv.gif.faces");
testRequest.getHeadersMap().put("accept-encoding", new String[] { "identity;q=0.5, deflate;q=1.0" });
byte[] control = getBytes(facesContext.getExternalContext().getResource("/resources/duke-nv.gif"), false);
handler.handleResourceRequest(facesContext);
byte[] test = wrapper.getBytes();
response.getWriter().println(Arrays.equals(control, test));
response.getWriter().println(wrapper.containsHeader("content-length"));
response.getWriter().println(wrapper.containsHeader("last-modified"));
response.getWriter().println(wrapper.containsHeader("expires"));
response.getWriter().println(wrapper.containsHeader("etag"));
response.getWriter().println(!wrapper.containsHeader("content-encoding"));
}
//==========================================================================
// Validate the fix for issue 1162.
//
private void testHandleResourceRequest14(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/web.xml.faces");
testRequest.getParameterMap().put("ln", new String[] { "../WEB-INF" });
facesContext.getApplication()
.getResourceHandler()
.handleResourceRequest(facesContext);
response.getWriter().println(wrapper.getStatus() == 404);
}
//==========================================================================
// Validate the fix for issue 1162.
//
private void testHandleResourceRequest15(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/web.xml.faces");
testRequest.getParameterMap().put("ln", new String[] { "nvLibrary/../../WEB-INF" });
facesContext.getApplication()
.getResourceHandler()
.handleResourceRequest(facesContext);
response.getWriter().println(wrapper.getStatus() == 404);
}
//==========================================================================
// Validate the fix for issue ????.
//
private void testHandleResourceRequest16(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException {
testRequest.setServletPath("/javax.faces.resource/duke-nv.gif.faces");
testRequest.getHeadersMap().put("accept-encoding", new String[] { "gzip;q=0, deflate" });
class TestInputStreamContainingZeroes extends InputStream {
private boolean open = true;
@Override
public int read() throws IOException {
return 0;
}
@Override
public void close() throws IOException {
open = false;
}
public boolean isOpen() {
return open;
}
};
final TestInputStreamContainingZeroes resourceInputStream = new TestInputStreamContainingZeroes();
ResourceHandler resourceHandler = new ResourceHandlerWrapper(handler) {
@Override
public Resource createResource(String resourceName, String libraryName) {
return new ResourceWrapper(super.createResource(resourceName, libraryName)) {
@Override
public InputStream getInputStream() throws IOException {
return resourceInputStream;
}
};
}
};
HttpServletResponse wrappedResponse = new HttpServletResponseWrapper(wrapper) {
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new ServletOutputStream() {
@Override
public void write(int b) throws IOException {
throw new IOException("Simulation of broken pipe or connection reset by peer");
}
@Override
public void close() throws IOException {
throw new IOException("Simulation of broken pipe or connection reset by peer");
}
@Override
public boolean isReady() {
throw new UnsupportedOperationException("Not supported");
}
@Override
public void setWriteListener(WriteListener wl) {
throw new UnsupportedOperationException("Not supported");
}
};
}
};
facesContext.getExternalContext().setResponse(wrappedResponse);
facesContext.getApplication().setResourceHandler(resourceHandler);
boolean exceptionOccurred = false;
try {
resourceHandler.handleResourceRequest(facesContext);
} catch (IOException e) {
exceptionOccurred = true;
}
response.getWriter().println(!resourceInputStream.isOpen());
response.getWriter().println(!exceptionOccurred);
response.getWriter().println(wrapper.getStatus() == 404);
}
// ### Helper methods
private FacesContext createFacesContext(HttpServletRequest request, TestRequest testRequest, HttpServletResponse response) {
LifecycleFactory factory = (LifecycleFactory) FactoryFinder.getFactory(LIFECYCLE_FACTORY);
Lifecycle lifecycle = factory.getLifecycle(DEFAULT_LIFECYCLE);
ExternalContext extContext = new ExternalContextImpl(request.getServletContext(), testRequest, response);
FacesContext facesContext = new FacesContextImpl(extContext, lifecycle) {
{
FacesContext.setCurrentInstance(this);
}
};
return facesContext;
}
interface TestMethod {
void test(FacesContext facesContext, TestRequest testRequest, TestResponse wrapper, HttpServletResponse response, ResourceHandler handler) throws ServletException, IOException;
}
private void run(String methodName, TestMethod testMethod, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
run(methodName, testMethod, request, response, null, null);
}
private void run(String methodName, TestMethod testMethod, HttpServletRequest request, HttpServletResponse response, WebContextInitParameter param, String paramValue) throws ServletException, IOException {
// Check if we need to skip this test
String testRequestParam = request.getParameter("test");
if (testRequestParam != null && !testRequestParam.equals(methodName)) {
return;
}
TestRequest testRequest = new TestRequest(request);
FacesContext facesContext = createFacesContext(request, testRequest, response);
WebConfiguration config = WebConfiguration.getInstance();
String oldParamValue = "";
try {
if (param != null) {
oldParamValue = config.getOptionValue(param);
config.overrideContextInitParameter(param, paramValue);
}
ApplicationAssociate associate = ApplicationAssociate.getInstance(facesContext.getExternalContext());
associate.setResourceManager(new ResourceManager(null)); // null for no caching
ResourceHandler handler = new ResourceHandlerImpl();
Application app = facesContext.getApplication();
ResourceHandler oldResourceHandler = app.getResourceHandler();
app.setResourceHandler(handler);
TestResponse testResponse = new TestResponse((HttpServletResponse) facesContext.getExternalContext().getResponse());
facesContext.getExternalContext().setResponse(testResponse);
try {
response.getWriter().println("\n" + methodName);
// Execute the actual test method
testMethod.test(facesContext, testRequest, testResponse, response, oldResourceHandler);
} finally {
app.setResourceHandler(oldResourceHandler);
}
} finally {
if (param != null) {
config.setOptionValue(param, oldParamValue == null? "" : oldParamValue);
}
}
}
private byte[] getBytes(URL url) throws IOException {
return getBytes(url, false);
}
private byte[] getBytes(URL url, boolean compress) throws IOException {
URLConnection c = url.openConnection();
c.setUseCaches(false);
InputStream in = c.getInputStream();
return compress ? getCompressedBytes(in) : getBytes(in);
}
private byte[] getBytes(InputStream in) throws IOException {
ByteArrayOutputStream o = new ByteArrayOutputStream();
for (int i = in.read(); i != -1; i = in.read()) {
o.write(i);
}
in.close();
return o.toByteArray();
}
private byte[] getCompressedBytes(InputStream in) throws IOException {
ByteArrayOutputStream o = new ByteArrayOutputStream();
GZIPOutputStream compress = new GZIPOutputStream(o);
for (int i = in.read(); i != -1; i = in.read()) {
compress.write(i);
}
compress.flush();
compress.close();
return o.toByteArray();
}
// Helper wrappers
private static class TestRequest extends HttpServletRequestWrapper {
private static final TimeZone GMT = getTimeZone("GMT");
private static final String[] datePatterns = { "EE, dd MMM yyyy HH:mm:ss zz", "EEEE, dd-MMM-yy HH:mm:ss zz",
"EE MMM d HH:mm:ss yyyy" };
private List<DateFormat> dateFormats;
private String servletPath;
private String pathInfo;
private Map<String, String[]> parameterMap = new HashMap<>();
private Map<String, String[]> headersMap = new HashMap<>();
public TestRequest(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
if (parameterMap.containsKey(name)) {
return parameterMap.get(name)[0];
}
return null;
}
@Override
public Map<String, String[]> getParameterMap() {
return parameterMap;
}
@Override
public String[] getParameterValues(String name) {
return parameterMap.get(name);
}
@Override
public Enumeration<String> getParameterNames() {
return enumeration(getParameterMap().keySet());
}
@Override
public String getHeader(String name) {
if (headersMap.containsKey(name)) {
return headersMap.get(name)[0];
}
return null;
}
public Map<String, String[]> getHeadersMap() {
return headersMap;
}
@Override
public Enumeration<String> getHeaderNames() {
return enumeration(headersMap.keySet());
}
@Override
public Enumeration<String> getHeaders(String name) {
if (headersMap.containsKey(name)) {
return enumeration(asList(headersMap.get(name)));
}
return enumeration(emptyList());
}
@Override
public long getDateHeader(String name) {
String header = getHeader(name);
if (header == null) {
// Spec defines we should return -1 if header doesn't exist
return -1;
}
if (dateFormats == null) {
dateFormats = new ArrayList<>(datePatterns.length);
for (String datePattern : datePatterns) {
dateFormats.add(createDateFormat(datePattern));
}
}
for (DateFormat dateFormat : dateFormats) {
try {
return dateFormat.parse(header).getTime();
} catch (ParseException e) {
// noop
}
}
// If no conversion is possible, spec says an IllegalArgumentException should be thrown
throw new IllegalArgumentException("Can't convert " + header + " to a date");
}
private DateFormat createDateFormat(String pattern) {
DateFormat dateFormat = new SimpleDateFormat(pattern, US);
dateFormat.setTimeZone(GMT);
return dateFormat;
}
@Override
public int getIntHeader(String name) {
String header = getHeader(name);
if (header == null) {
// Spec defines we should return -1 is header doesn't exist
return -1;
}
// If header ain't an integer, spec says a NumberFormatException should be thrown,
// which is what Integer.parseInt will do.
return parseInt(header);
}
public String getServletPath() {
return servletPath;
}
public void setServletPath(String servletPath) {
this.servletPath = servletPath;
}
public String getPathInfo() {
return pathInfo;
}
public void setPathInfo(String pathInfo) {
this.pathInfo = pathInfo;
}
}
private static class TestResponse extends HttpServletResponseWrapper {
private TestServletOutputStream out;
private Map<String, String> headers = new HashMap<>();
private int status;
@Override
public ServletResponse getResponse() {
return super.getResponse();
}
public byte[] getBytes() {
return out.getBytes();
}
public TestResponse(HttpServletResponse httpServletResponse) {
super(httpServletResponse);
}
@Override
public void addHeader(String name, String value) {
headers.put(name.toLowerCase(), value.toLowerCase());
}
@Override
public void setHeader(String name, String value) {
headers.put(name.toLowerCase(), value.toLowerCase());
}
@Override
public String getHeader(String name) {
return headers.get(name);
}
@Override
public boolean containsHeader(String name) {
return headers.containsKey(name);
}
@Override
public void setContentType(String type) {
headers.put("content-type", type);
}
@Override
public void setContentLength(int len) {
headers.put("content-length", len + "");
}
@Override
public void setBufferSize(int size) {
}
@Override
public boolean isCommitted() {
return false;
}
public ServletOutputStream getOutputStream() throws IOException {
out = new TestServletOutputStream();
return out;
}
@Override
public void setStatus(int sc) {
status = sc;
}
@Override
public void sendError(int sc) throws IOException {
status = sc;
}
@Override
public void sendError(int sc, String msg) throws IOException {
status = sc;
}
@Override
public int getStatus() {
return status;
}
private class TestServletOutputStream extends ServletOutputStream {
private ByteArrayOutputStream out = new ByteArrayOutputStream();
public TestServletOutputStream() {
}
public void write(int b) throws IOException {
out.write(b);
}
public void write(byte b[]) throws IOException {
out.write(b);
}
public void write(byte b[], int off, int len) throws IOException {
out.write(b, off, len);
}
public void flush() throws IOException {
out.flush();
}
public void close() throws IOException {
out.close();
}
public byte[] getBytes() {
return out.toByteArray();
}
@Override
public boolean isReady() {
throw new UnsupportedOperationException("Not supported");
}
@Override
public void setWriteListener(WriteListener wl) {
throw new UnsupportedOperationException("Not supported");
}
}
}
private static abstract class URLConnectionWrapper extends URLConnection {
private URLConnection wrapped;
public URLConnectionWrapper(final URLConnection urlConnection) {
super(urlConnection.getURL());
this.wrapped = urlConnection;
}
@Override
public void connect() throws IOException {
wrapped.connect();
connected = true;
}
@Override
public boolean getAllowUserInteraction() {
return wrapped.getAllowUserInteraction();
}
@Override
public Object getContent() throws IOException {
return wrapped.getContent();
}
@Override
@SuppressWarnings("rawtypes")
public Object getContent(final Class[] types) throws IOException {
return wrapped.getContent(types);
}
@Override
public String getContentEncoding() {
return wrapped.getContentEncoding();
}
@Override
public int getContentLength() {
return wrapped.getContentLength();
}
@Override
public String getContentType() {
return wrapped.getContentType();
}
@Override
public long getDate() {
return wrapped.getDate();
}
@Override
public boolean getDefaultUseCaches() {
return wrapped.getDefaultUseCaches();
}
@Override
public boolean getDoInput() {
return wrapped.getDoInput();
}
@Override
public boolean getDoOutput() {
return wrapped.getDoInput();
}
@Override
public long getExpiration() {
return wrapped.getExpiration();
}
@Override
public String getHeaderField(final int pos) {
return wrapped.getHeaderField(pos);
}
@Override
public Map<String, List<String>> getHeaderFields() {
return wrapped.getHeaderFields();
}
@Override
public Map<String, List<String>> getRequestProperties() {
return wrapped.getRequestProperties();
}
@Override
public void addRequestProperty(final String field, final String newValue) {
wrapped.addRequestProperty(field, newValue);
}
@Override
public String getHeaderField(final String key) {
return wrapped.getHeaderField(key);
}
@Override
public long getHeaderFieldDate(final String field, final long defaultValue) {
return wrapped.getHeaderFieldDate(field, defaultValue);
}
@Override
public int getHeaderFieldInt(final String field, final int defaultValue) {
return wrapped.getHeaderFieldInt(field, defaultValue);
}
@Override
public String getHeaderFieldKey(final int posn) {
return wrapped.getHeaderFieldKey(posn);
}
@Override
public long getIfModifiedSince() {
return wrapped.getIfModifiedSince();
}
@Override
public InputStream getInputStream() throws IOException {
return wrapped.getInputStream();
}
@Override
public long getLastModified() {
return wrapped.getLastModified();
}
@Override
public OutputStream getOutputStream() throws IOException {
return wrapped.getOutputStream();
}
@Override
public java.security.Permission getPermission() throws IOException {
return wrapped.getPermission();
}
@Override
public String getRequestProperty(final String field) {
return wrapped.getRequestProperty(field);
}
@Override
public URL getURL() {
return url;
}
@Override
public boolean getUseCaches() {
return wrapped.getUseCaches();
}
@Override
public void setAllowUserInteraction(final boolean newValue) {
wrapped.setAllowUserInteraction(newValue);
}
@Override
public void setDefaultUseCaches(final boolean newValue) {
wrapped.setDefaultUseCaches(newValue);
}
@Override
public void setDoInput(final boolean newValue) {
wrapped.setDoInput(newValue);
}
@Override
public void setDoOutput(final boolean newValue) {
wrapped.setDoOutput(newValue);
}
@Override
public void setIfModifiedSince(final long newValue) {
wrapped.setIfModifiedSince(newValue);
}
@Override
public void setRequestProperty(final String field, final String newValue) {
wrapped.setRequestProperty(field, newValue);
}
@Override
public void setUseCaches(final boolean newValue) {
wrapped.setUseCaches(newValue);
}
@Override
public void setConnectTimeout(final int timeout) {
wrapped.setConnectTimeout(timeout);
}
@Override
public int getConnectTimeout() {
return wrapped.getConnectTimeout();
}
@Override
public void setReadTimeout(final int timeout) {
wrapped.setReadTimeout(timeout);
}
@Override
public int getReadTimeout() {
return wrapped.getReadTimeout();
}
}
}