org.eclipse.jetty.server.handler.TryPathsHandler Maven / Gradle / Ivy
Show all versions of jetty-server Show documentation
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//
package org.eclipse.jetty.server.handler;
import java.util.List;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.URIUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Inspired by nginx's {@code try_files} functionality.
*
* This handler can be configured with a list of rewrite URI paths.
* The special token {@code $path} represents the current request
* {@code pathInContext} (the portion after the context path).
*
* Typical example of how this handler can be configured is the following:
* {@code
* TryPathsHandler tryPathsHandler = new TryPathsHandler();
* tryPathsHandler.setPaths("/maintenance.html", "$path", "/index.php?p=$path");
*
* PathMappingsHandler pathMappingsHandler = new PathMappingsHandler();
* tryPathsHandler.setHandler(pathMappingsHandler);
*
* pathMappingsHandler.addMapping(new ServletPathSpec("*.php"), new PHPHandler());
* pathMappingsHandler.addMapping(new ServletPathSpec("/"), new ResourceHandler());
* }
*
* For a request such as {@code /context/path/to/resource.ext}:
*
* - This handler rewrites the request {@code pathInContext} to
* {@code /maintenance.html} and forwards the request to the next handler,
* where it matches the {@code /} mapping, hitting the {@code ResourceHandler}
* that serves the file if it exists.
* - Otherwise, this handler rewrites the request {@code pathInContext} to
* {@code /path/to/resource.ext} and forwards the request to the next handler,
* where it matches the {@code /} mapping, hitting the {@code ResourceHandler}
* that serves the file if it exists.
* - Otherwise, this handler rewrites the request {@code pathInContext} to
* {@code /index.php?p=/path/to/resource.ext} and forwards the request to
* the next handler, where it matches the {@code *.php} mapping, hitting
* the {@code PHPHandler}.
*
*
* The original path and query may be stored as request attributes,
* under the names specified by {@link #setOriginalPathAttribute(String)}
* and {@link #setOriginalQueryAttribute(String)}.
*/
public class TryPathsHandler extends Handler.Wrapper
{
private static final Logger LOG = LoggerFactory.getLogger(TryPathsHandler.class);
private String originalPathAttribute;
private String originalQueryAttribute;
private List paths;
public TryPathsHandler()
{
this(null);
}
public TryPathsHandler(Handler handler)
{
super(handler);
}
/**
* Get the attribute name of the original request path.
* @return the attribute name of the original request path
*/
public String getOriginalPathAttribute()
{
return originalPathAttribute;
}
/**
* Sets the request attribute name to use to
* retrieve the original request path.
*
* @param originalPathAttribute the attribute name of the original
* request path
*/
public void setOriginalPathAttribute(String originalPathAttribute)
{
this.originalPathAttribute = originalPathAttribute;
}
/**
* Get the attribute name of the original request query.
* @return the attribute name of the original request query
*/
public String getOriginalQueryAttribute()
{
return originalQueryAttribute;
}
/**
* Sets the request attribute name to use to
* retrieve the original request query.
*
* @param originalQueryAttribute the attribute name of the original
* request query
*/
public void setOriginalQueryAttribute(String originalQueryAttribute)
{
this.originalQueryAttribute = originalQueryAttribute;
}
/**
* @return the rewrite URI paths
*/
public List getPaths()
{
return paths;
}
/**
* Sets a list of rewrite URI paths.
* The special token {@code $path} represents the current request
* {@code pathInContext} (the portion after the context path).
*
* @param paths the rewrite URI paths
*/
public void setPaths(List paths)
{
this.paths = paths;
}
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
{
Handler next = getHandler();
if (next == null)
return false;
for (String path : paths)
{
String interpolated = interpolate(request, path);
Request tryRequest = tryPath(request, interpolated);
if (LOG.isDebugEnabled())
LOG.debug("rewritten request URI {} -> {}", request.getHttpURI(), tryRequest.getHttpURI());
boolean handled = super.handle(tryRequest, response, callback);
if (LOG.isDebugEnabled())
LOG.debug("handled {} {}", handled, tryRequest.getHttpURI());
if (handled)
return true;
}
return false;
}
private String interpolate(Request request, String value)
{
String path = Request.getPathInContext(request);
return value.replace("$path", path);
}
private Request tryPath(Request wrapped, String newPathQuery)
{
HttpURI originalURI = wrapped.getHttpURI();
String originalPathAttribute = getOriginalPathAttribute();
if (originalPathAttribute != null)
{
if (wrapped.getAttribute(originalPathAttribute) == null)
wrapped.setAttribute(originalPathAttribute, Request.getPathInContext(wrapped));
}
String originalQueryAttribute = getOriginalQueryAttribute();
if (originalQueryAttribute != null)
{
if (wrapped.getAttribute(originalQueryAttribute) == null)
wrapped.setAttribute(originalQueryAttribute, originalURI.getQuery());
}
String originalContextPath = Request.getContextPath(wrapped);
HttpURI.Mutable rewrittenURI = HttpURI.build(originalURI);
int queryIdx = newPathQuery.indexOf('?');
if (queryIdx >= 0)
{
String path = newPathQuery.substring(0, queryIdx);
rewrittenURI.path(URIUtil.addPaths(originalContextPath, path));
rewrittenURI.query(newPathQuery.substring(queryIdx + 1));
}
else
{
rewrittenURI.path(URIUtil.addPaths(originalContextPath, newPathQuery));
}
HttpURI uri = rewrittenURI.asImmutable();
return Request.serveAs(wrapped, uri);
}
}