Implemented a TryFilesFilter inspired by nginx's try_files directive.

This is needed by - for example - WordPress because it allows for
different permalinks schemes.
For example, /foo could be a permalink that should be directed to
PHP, while /favicon.ico is a file that must be served statically.
URI /foo would be matched by the default servlet resulting in a 404.
Therefore we need a filter mapped to /* that checks whether the
request URI is a file that exists and likely to be served by the
default servlet, otherwise it needs to rewrite the URL to something
like /index.php?p=/foo.
The rewrite filter does not have the functionality of "try the file,
if missing then rewrite" that is now implemented by the TryFilesFilter.
This commit is contained in:
Simone Bordet 2013-10-31 19:54:59 +01:00
parent f841b42fde
commit 226c522451
2 changed files with 195 additions and 0 deletions

View File

@ -0,0 +1,109 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.fcgi.proxy;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Inspired by nginx's try_files functionality
*/
public class TryFilesFilter implements Filter
{
public static final String ROOT_INIT_PARAM = "root";
public static final String FILES_INIT_PARAM = "files";
private String root;
private String[] files;
@Override
public void init(FilterConfig config) throws ServletException
{
root = config.getInitParameter(ROOT_INIT_PARAM);
if (root == null)
throw new ServletException(String.format("Missing mandatory parameter '%s'", ROOT_INIT_PARAM));
String param = config.getInitParameter(FILES_INIT_PARAM);
if (param == null)
throw new ServletException(String.format("Missing mandatory parameter '%s'", FILES_INIT_PARAM));
files = param.split(" ");
}
public String getRoot()
{
return root;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
HttpServletRequest httpRequest = (HttpServletRequest)request;
HttpServletResponse httpResponse = (HttpServletResponse)response;
for (int i = 0; i < files.length - 1; ++i)
{
String file = files[i];
String resolved = resolve(httpRequest, file);
Path path = Paths.get(getRoot(), resolved);
if (Files.exists(path) && Files.isReadable(path))
{
chain.doFilter(httpRequest, httpResponse);
return;
}
}
// The last one is the fallback
fallback(httpRequest, httpResponse, chain, files[files.length - 1]);
}
protected void fallback(HttpServletRequest request, HttpServletResponse response, FilterChain chain, String fallback) throws IOException, ServletException
{
String resolved = resolve(request, fallback);
request.getRequestDispatcher(resolved).forward(request, response);
}
private String resolve(HttpServletRequest request, String value)
{
String path = request.getRequestURI();
String query = request.getQueryString();
String result = value.replaceAll("\\$path", path);
result = result.replaceAll("\\$query", query == null ? "" : query);
// Remove the "?" or "&" at the end if there is no query
if (query == null && (result.endsWith("?") || result.endsWith("&")))
result = result.substring(0, result.length() - 1);
return result;
}
@Override
public void destroy()
{
}
}

View File

@ -0,0 +1,86 @@
//
// ========================================================================
// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.fcgi.proxy;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.DispatcherType;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnector;
import org.eclipse.jetty.spdy.server.http.PushStrategy;
import org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy;
import org.eclipse.jetty.util.ssl.SslContextFactory;
public class WordPressSPDYFastCGIProxyServer
{
public static void main(String[] args) throws Exception
{
// int port = 8080;
int port = 8443;
// SslContextFactory sslContextFactory = null;
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setEndpointIdentificationAlgorithm("");
sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
sslContextFactory.setKeyStorePassword("storepwd");
sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks");
sslContextFactory.setTrustStorePassword("storepwd");
Server server = new Server();
Map<Short, PushStrategy> pushStrategies = new HashMap<>();
pushStrategies.put(SPDY.V3, new ReferrerPushStrategy());
HTTPSPDYServerConnector connector = new HTTPSPDYServerConnector(server, sslContextFactory, pushStrategies);
connector.setPort(port);
server.addConnector(connector);
String root = "/home/simon/programs/wordpress-3.7.1";
ServletContextHandler context = new ServletContextHandler(server, "/");
context.setResourceBase(root);
context.setWelcomeFiles(new String[]{"index.php"});
// Serve static resources
ServletHolder defaultServlet = new ServletHolder(DefaultServlet.class);
defaultServlet.setName("default");
context.addServlet(defaultServlet, "/");
FilterHolder tryFileFilter = context.addFilter(TryFilesFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
tryFileFilter.setInitParameter(TryFilesFilter.ROOT_INIT_PARAM, root);
// tryFileFilter.setInitParameter(TryFilesFilter.FILES_INIT_PARAM, "$path $path/index.php?$query"); // Permalink /?p=123
tryFileFilter.setInitParameter(TryFilesFilter.FILES_INIT_PARAM, "$path /index.php?p=$path&$query"); // Permalink /%year%/%monthnum%/%postname%
// FastCGI
ServletHolder fcgiServlet = new ServletHolder(FastCGIProxyServlet.class);
fcgiServlet.setInitParameter(FastCGIProxyServlet.SCRIPT_ROOT_INIT_PARAM, root);
fcgiServlet.setInitParameter("proxyTo", "http://localhost:9000");
fcgiServlet.setInitParameter("prefix", "/");
fcgiServlet.setInitParameter(FastCGIProxyServlet.SCRIPT_PATTERN_INIT_PARAM, "(.+?\\.php)");
context.addServlet(fcgiServlet, "*.php");
server.start();
}
}