Merge pull request #1613 from xz64/header_filters
Add general purpose HeaderFilter
This commit is contained in:
commit
10da4fce1d
|
@ -30,6 +30,7 @@ include::balancer-servlet.adoc[]
|
|||
include::cgi-servlet.adoc[]
|
||||
include::qos-filter.adoc[]
|
||||
include::dos-filter.adoc[]
|
||||
include::header-filter.adoc[]
|
||||
include::gzip-filter.adoc[]
|
||||
include::cross-origin-filter.adoc[]
|
||||
include::resource-handler.adoc[]
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.
|
||||
// ========================================================================
|
||||
|
||||
[[header-filter]]
|
||||
=== Header Filter
|
||||
|
||||
[[header-filter-metadata]]
|
||||
==== Info
|
||||
|
||||
* Classname: `org.eclipse.jetty.servlets.HeaderFilter`
|
||||
* Maven Artifact: org.eclipse.jetty:jetty-servlets
|
||||
* Javadoc: {JDURL}/org/eclipse/jetty/servlets/HeaderFilter.html
|
||||
* Xref: {JXURL}/org/eclipse/jetty/servlets/HeaderFilter.html
|
||||
|
||||
[[header-filter-usage]]
|
||||
==== Usage
|
||||
|
||||
The header filter sets or adds headers to each response based on an optionally included/excluded list of path specs, mime types, and/or HTTP methods.
|
||||
|
||||
===== Required JARs
|
||||
|
||||
To use the Header Filter, these JAR files must be available in WEB-INF/lib:
|
||||
|
||||
* $JETTY_HOME/lib/jetty-http.jar
|
||||
* $JETTY_HOME/lib/jetty-servlets.jar
|
||||
* $JETTY_HOME/lib/jetty-util.jar
|
||||
|
||||
===== Sample Configuration
|
||||
|
||||
Place the configuration in a webapp's `web.xml` or `jetty-web.xml`.
|
||||
This filter will perform the following actions on each response:
|
||||
|
||||
* Set the X-Frame-Options header to DENY.
|
||||
* Add a Cache-Control header containing no-cache, no-store, must-revalidate
|
||||
* Set the Expires header to approximately one year in the future.
|
||||
* Add a Date header with the current system time.
|
||||
|
||||
____
|
||||
[NOTE]
|
||||
Each action must be separated by a comma.
|
||||
____
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<filter>
|
||||
<filter-name>HeaderFilter</filter-name>
|
||||
<filter-class>org.eclipse.jetty.servlets.HeaderFilter</filter-class>
|
||||
<init-param>
|
||||
<param-name>headerConfig</param-name>
|
||||
<param-value>
|
||||
set X-Frame-Options: DENY,
|
||||
"add Cache-Control: no-cache, no-store, must-revalidate",
|
||||
setDate Expires: 31540000000,
|
||||
addDate Date: 0
|
||||
</param-value>
|
||||
</init-param>
|
||||
</filter>
|
||||
----
|
||||
|
||||
[[header-filter-init]]
|
||||
===== Configuring Header Filter Parameters
|
||||
|
||||
The following `init` parameters control the behavior of the filter:
|
||||
|
||||
includedPaths::
|
||||
Optional. CSV of included path specs.
|
||||
|
||||
excludedPaths::
|
||||
Optional. CSV of excluded path specs.
|
||||
|
||||
includedMimeTypes::
|
||||
Optional. CSV of included mime types.
|
||||
|
||||
excludedMimeTypes::
|
||||
Optional. CSV of excluded mime types.
|
||||
|
||||
includedHttpMethods::
|
||||
Optional. CSV of included http methods.
|
||||
|
||||
excludedHttpMethods::
|
||||
Optional. CSV of excluded http methods.
|
||||
|
||||
headerConfig::
|
||||
CSV of actions to perform on headers. The syntax for each action is `action headerName: headerValue`.
|
||||
|
||||
Supported header actions:
|
||||
|
||||
* `set` - causes set `setHeader` to be called on the response
|
||||
* `add` - causes set `addHeader` to be called on the response
|
||||
* `setDate` - causes `setDateHeader` to be called on the response.
|
||||
* `addDate` - causes `addDateHeader` to be called on the response.
|
||||
|
||||
If `setDate` or `addDate` is used, `headerValue` should be the number of milliseconds to add to the current system time before writing the header value.
|
||||
|
||||
If a property is both included and excluded by the filter configuration, then it will be considered excluded.
|
||||
|
||||
Path spec rules:
|
||||
|
||||
* If the spec starts with `^`, the spec is assumed to be a regex based path spec and will match with normal Java regex rules.
|
||||
* If the spec starts with `/`, the spec is assumed to be a Servlet url-pattern rules path spec for either an exact match or prefix based match.
|
||||
* If the spec starts with `*.`, the spec is assumed to be a Servlet url-pattern rules path spec for a suffix based match.
|
||||
* All other syntaxes are unsupported.
|
|
@ -0,0 +1,197 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.servlets;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/**
|
||||
* Header Filter
|
||||
* <p>
|
||||
* This filter sets or adds a header to the response.
|
||||
* <p>
|
||||
* The {@code headerConfig} init param is a CSV of actions to perform on headers, with the following syntax: <br>
|
||||
* [action] [header name]: [header value] <br>
|
||||
* [action] can be one of <code>set</code>, <code>add</code>, <code>setDate</code>, or <code>addDate</code> <br>
|
||||
* The date actions will add the header value in milliseconds to the current system time before setting a date header.
|
||||
* <p>
|
||||
* Below is an example value for <code>headerConfig</code>:<br>
|
||||
*
|
||||
* <pre>
|
||||
* set X-Frame-Options: DENY,
|
||||
* "add Cache-Control: no-cache, no-store, must-revalidate",
|
||||
* setDate Expires: 31540000000,
|
||||
* addDate Date: 0
|
||||
* </pre>
|
||||
*
|
||||
* @see IncludeExcludeBasedFilter
|
||||
*/
|
||||
public class HeaderFilter extends IncludeExcludeBasedFilter
|
||||
{
|
||||
private List<ConfiguredHeader> _configuredHeaders = new ArrayList<>();
|
||||
private static final Logger LOG = Log.getLogger(HeaderFilter.class);
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException
|
||||
{
|
||||
super.init(filterConfig);
|
||||
String header_config = filterConfig.getInitParameter("headerConfig");
|
||||
|
||||
if (header_config != null)
|
||||
{
|
||||
String[] configs = StringUtil.csvSplit(header_config);
|
||||
for (String config : configs)
|
||||
_configuredHeaders.add(parseHeaderConfiguration(config));
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(this.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
|
||||
{
|
||||
chain.doFilter(request,response);
|
||||
|
||||
HttpServletRequest http_request = (HttpServletRequest)request;
|
||||
HttpServletResponse http_response = (HttpServletResponse)response;
|
||||
|
||||
if (!super.shouldFilter(http_request,http_response))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (ConfiguredHeader header : _configuredHeaders)
|
||||
{
|
||||
if (header.isDate())
|
||||
{
|
||||
long header_value = System.currentTimeMillis() + header.getMsOffset();
|
||||
if (header.isAdd())
|
||||
{
|
||||
http_response.addDateHeader(header.getName(),header_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
http_response.setDateHeader(header.getName(),header_value);
|
||||
}
|
||||
}
|
||||
else // constant header value
|
||||
{
|
||||
if (header.isAdd())
|
||||
{
|
||||
http_response.addHeader(header.getName(),header.getValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
http_response.setHeader(header.getName(),header.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(super.toString()).append("\n");
|
||||
sb.append("configured headers:\n");
|
||||
for (ConfiguredHeader c : _configuredHeaders)
|
||||
sb.append(c).append("\n");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private ConfiguredHeader parseHeaderConfiguration(String config)
|
||||
{
|
||||
String[] config_tokens = config.trim().split(" ",2);
|
||||
String method = config_tokens[0].trim();
|
||||
String header = config_tokens[1];
|
||||
String[] header_tokens = header.trim().split(":",2);
|
||||
String header_name = header_tokens[0].trim();
|
||||
String header_value = header_tokens[1].trim();
|
||||
ConfiguredHeader configured_header = new ConfiguredHeader(header_name,header_value,method.startsWith("add"),method.endsWith("Date"));
|
||||
return configured_header;
|
||||
}
|
||||
|
||||
private static class ConfiguredHeader
|
||||
{
|
||||
private String _name;
|
||||
private String _value;
|
||||
private long _msOffset;
|
||||
private boolean _add;
|
||||
private boolean _date;
|
||||
|
||||
public ConfiguredHeader(String name, String value, boolean add, boolean date)
|
||||
{
|
||||
_name = name;
|
||||
_value = value;
|
||||
_add = add;
|
||||
_date = date;
|
||||
|
||||
if (_date)
|
||||
{
|
||||
_msOffset = Long.parseLong(_value);
|
||||
}
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
public String getValue()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
public boolean isAdd()
|
||||
{
|
||||
return _add;
|
||||
}
|
||||
|
||||
public boolean isDate()
|
||||
{
|
||||
return _date;
|
||||
}
|
||||
|
||||
public long getMsOffset()
|
||||
{
|
||||
return _msOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return (_add?"add":"set") + (_date?"Date":"") + " " + _name + ": " + _value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.servlets;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.http.pathmap.PathSpecSet;
|
||||
import org.eclipse.jetty.util.IncludeExclude;
|
||||
import org.eclipse.jetty.util.IncludeExcludeSet;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/**
|
||||
* Include Exclude Based Filter
|
||||
* <p>
|
||||
* This is an abstract filter which helps with filtering based on include/exclude of paths, mime types, and/or http methods.
|
||||
* <p>
|
||||
* Use the {@link #shouldFilter(HttpServletRequest, HttpServletResponse)} method to determine if a request/response should be filtered. If mime types are used,
|
||||
* it should be called after {@link javax.servlet.FilterChain#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse)} since the mime type may not
|
||||
* be written until then.
|
||||
*
|
||||
* Supported init params:
|
||||
* <ul>
|
||||
* <li><code>includedPaths</code> - CSV of path specs to include</li>
|
||||
* <li><code>excludedPaths</code> - CSV of path specs to exclude</li>
|
||||
* <li><code>includedMimeTypes</code> - CSV of mime types to include</li>
|
||||
* <li><code>excludedMimeTypes</code> - CSV of mime types to exclude</li>
|
||||
* <li><code>includedHttpMethods</code> - CSV of http methods to include</li>
|
||||
* <li><code>excludedHttpMethods</code> - CSV of http methods to exclude</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Path spec rules:
|
||||
* <ul>
|
||||
* <li>If the spec starts with <code>'^'</code> the spec is assumed to be a regex based path spec and will match with normal Java regex rules.</li>
|
||||
* <li>If the spec starts with <code>'/'</code> the spec is assumed to be a Servlet url-pattern rules path spec for either an exact match or prefix based
|
||||
* match.</li>
|
||||
* <li>If the spec starts with <code>'*.'</code> the spec is assumed to be a Servlet url-pattern rules path spec for a suffix based match.</li>
|
||||
* <li>All other syntaxes are unsupported.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* CSVs are parsed with {@link StringUtil#csvSplit(String)}
|
||||
*
|
||||
* @see PathSpecSet
|
||||
* @see IncludeExcludeSet
|
||||
*/
|
||||
public abstract class IncludeExcludeBasedFilter implements Filter
|
||||
{
|
||||
private final IncludeExclude<String> _mimeTypes = new IncludeExclude<>();
|
||||
private final IncludeExclude<String> _httpMethods = new IncludeExclude<>();
|
||||
private final IncludeExclude<String> _paths = new IncludeExclude<>(PathSpecSet.class);
|
||||
private static final Logger LOG = Log.getLogger(IncludeExcludeBasedFilter.class);
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException
|
||||
{
|
||||
String included_paths = filterConfig.getInitParameter("includedPaths");
|
||||
String excluded_paths = filterConfig.getInitParameter("excludedPaths");
|
||||
String included_mime_types = filterConfig.getInitParameter("includedMimeTypes");
|
||||
String excluded_mime_types = filterConfig.getInitParameter("excludedMimeTypes");
|
||||
String included_http_methods = filterConfig.getInitParameter("includedHttpMethods");
|
||||
String excluded_http_methods = filterConfig.getInitParameter("excludedHttpMethods");
|
||||
|
||||
if (included_paths != null)
|
||||
{
|
||||
_paths.include(StringUtil.csvSplit(included_paths));
|
||||
}
|
||||
if (excluded_paths != null)
|
||||
{
|
||||
_paths.exclude(StringUtil.csvSplit(excluded_paths));
|
||||
}
|
||||
if (included_mime_types != null)
|
||||
{
|
||||
_mimeTypes.include(StringUtil.csvSplit(included_mime_types));
|
||||
}
|
||||
if (excluded_mime_types != null)
|
||||
{
|
||||
_mimeTypes.exclude(StringUtil.csvSplit(excluded_mime_types));
|
||||
}
|
||||
if (included_http_methods != null)
|
||||
{
|
||||
_httpMethods.include(StringUtil.csvSplit(included_http_methods));
|
||||
}
|
||||
if (excluded_http_methods != null)
|
||||
{
|
||||
_httpMethods.exclude(StringUtil.csvSplit(excluded_http_methods));
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean shouldFilter(HttpServletRequest http_request, HttpServletResponse http_response)
|
||||
{
|
||||
String http_method = http_request.getMethod();
|
||||
LOG.debug("HTTP method is: {}",http_method);
|
||||
if (!_httpMethods.test(http_method))
|
||||
{
|
||||
LOG.debug("should not apply filter because HTTP method does not match");
|
||||
return false;
|
||||
}
|
||||
|
||||
String content_type = http_response.getContentType();
|
||||
LOG.debug("Content Type is: {}",content_type);
|
||||
content_type = (content_type == null)?"":content_type;
|
||||
String mime_type = MimeTypes.getContentTypeWithoutCharset(content_type);
|
||||
|
||||
LOG.debug("Mime Type is: {}",content_type);
|
||||
if (!_mimeTypes.test(mime_type))
|
||||
{
|
||||
LOG.debug("should not apply filter because mime type does not match");
|
||||
return false;
|
||||
}
|
||||
|
||||
ServletContext context = http_request.getServletContext();
|
||||
String path = context == null?http_request.getRequestURI():URIUtil.addPaths(http_request.getServletPath(),http_request.getPathInfo());
|
||||
LOG.debug("Path is: {}",path);
|
||||
if (!_paths.test(path))
|
||||
{
|
||||
LOG.debug("should not apply filter because path does not match");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("filter configuration:\n");
|
||||
sb.append("paths:\n").append(_paths).append("\n");
|
||||
sb.append("mime types:\n").append(_mimeTypes).append("\n");
|
||||
sb.append("http methods:\n").append(_httpMethods);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.servlets;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlet.ServletTester;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HeaderFilterTest
|
||||
{
|
||||
private ServletTester _tester;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
_tester = new ServletTester();
|
||||
_tester.setContextPath("/context");
|
||||
_tester.addServlet(NullServlet.class,"/test/*");
|
||||
|
||||
_tester.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception
|
||||
{
|
||||
_tester.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeaderFilterSet() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(HeaderFilter.class);
|
||||
holder.setInitParameter("headerConfig","set X-Frame-Options: DENY");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/0");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertTrue(response.contains("X-Frame-Options","DENY"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeaderFilterAdd() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(HeaderFilter.class);
|
||||
holder.setInitParameter("headerConfig","add X-Frame-Options: DENY");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/0");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertTrue(response.contains("X-Frame-Options","DENY"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeaderFilterSetDate() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(HeaderFilter.class);
|
||||
holder.setInitParameter("headerConfig","setDate Expires: 100");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/0");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertTrue(response.contains(HttpHeader.EXPIRES));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHeaderFilterAddDate() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(HeaderFilter.class);
|
||||
holder.setInitParameter("headerConfig","addDate Expires: 100");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/0");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertTrue(response.contains(HttpHeader.EXPIRES));
|
||||
}
|
||||
|
||||
public static class NullServlet extends HttpServlet
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||
{
|
||||
resp.setStatus(HttpStatus.NO_CONTENT_204);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,353 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.servlets;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlet.ServletTester;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class IncludeExcludeBasedFilterTest
|
||||
{
|
||||
private ServletTester _tester;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
_tester = new ServletTester();
|
||||
_tester.setContextPath("/context");
|
||||
_tester.addServlet(NullServlet.class,"/test/*");
|
||||
|
||||
_tester.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception
|
||||
{
|
||||
_tester.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeExcludeFilterIncludedPathMatch() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(MockIncludeExcludeFilter.class);
|
||||
holder.setInitParameter("includedPaths","^/test/0$");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/0");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertTrue(response.contains("X-Custom-Value","1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeExcludeFilterIncludedPathNoMatch() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(MockIncludeExcludeFilter.class);
|
||||
holder.setInitParameter("includedPaths","^/nomatchtest$");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/0");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertFalse(response.contains("X-Custom-Value","1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeExcludeFilterExcludedPathMatch() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(MockIncludeExcludeFilter.class);
|
||||
holder.setInitParameter("excludedPaths","^/test/0$");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/0");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertFalse(response.contains("X-Custom-Value","1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeExcludeFilterExcludedPathNoMatch() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(MockIncludeExcludeFilter.class);
|
||||
holder.setInitParameter("excludedPaths","^/nomatchtest$");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/0");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertTrue(response.contains("X-Custom-Value","1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeExcludeFilterExcludeOverridesInclude() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(MockIncludeExcludeFilter.class);
|
||||
holder.setInitParameter("includedPaths","^/test/0$");
|
||||
holder.setInitParameter("excludedPaths","^/test/0$");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/0");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertFalse(response.contains("X-Custom-Value","1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeExcludeFilterIncludeMethodMatch() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(MockIncludeExcludeFilter.class);
|
||||
holder.setInitParameter("includedHttpMethods","GET");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/0");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertTrue(response.contains("X-Custom-Value","1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeExcludeFilterIncludeMethodNoMatch() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(MockIncludeExcludeFilter.class);
|
||||
holder.setInitParameter("includedHttpMethods","POST,PUT");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/0");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertFalse(response.contains("X-Custom-Value","1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeExcludeFilterExcludeMethodMatch() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(MockIncludeExcludeFilter.class);
|
||||
holder.setInitParameter("excludedHttpMethods","GET");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/0");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertFalse(response.contains("X-Custom-Value","1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeExcludeFilterExcludeMethodNoMatch() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(MockIncludeExcludeFilter.class);
|
||||
holder.setInitParameter("excludedHttpMethods","POST,PUT");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/0");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertTrue(response.contains("X-Custom-Value","1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeExcludeFilterIncludeMimeTypeMatch() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(MockIncludeExcludeFilter.class);
|
||||
holder.setInitParameter("includedMimeTypes","application/json");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/json");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertTrue(response.contains("X-Custom-Value","1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeExcludeFilterIncludeMimeTypeNoMatch() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(MockIncludeExcludeFilter.class);
|
||||
holder.setInitParameter("includedMimeTypes","application/xml");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/json");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertFalse(response.contains("X-Custom-Value","1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeExcludeFilterExcludeMimeTypeMatch() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(MockIncludeExcludeFilter.class);
|
||||
holder.setInitParameter("excludedMimeTypes","application/json");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/json");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertFalse(response.contains("X-Custom-Value","1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeExcludeFilterExcludeMimeTypeNoMatch() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(MockIncludeExcludeFilter.class);
|
||||
holder.setInitParameter("excludedMimeTypes","application/xml");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/json");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertTrue(response.contains("X-Custom-Value","1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeExcludeFilterIncludeMimeTypeSemicolonMatch() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(MockIncludeExcludeFilter.class);
|
||||
holder.setInitParameter("includedMimeTypes","application/json");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/json-utf8");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertTrue(response.contains("X-Custom-Value","1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncludeExcludeFilterIncludeMimeTypeSemicolonNoMatch() throws Exception
|
||||
{
|
||||
FilterHolder holder = new FilterHolder(MockIncludeExcludeFilter.class);
|
||||
holder.setInitParameter("includedMimeTypes","application/xml");
|
||||
_tester.getContext().getServletHandler().addFilterWithMapping(holder,"/*",EnumSet.of(DispatcherType.REQUEST));
|
||||
|
||||
HttpTester.Request request = HttpTester.newRequest();
|
||||
request.setMethod("GET");
|
||||
request.setVersion("HTTP/1.1");
|
||||
request.setHeader("Host","localhost");
|
||||
request.setURI("/context/test/json-utf8");
|
||||
|
||||
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
|
||||
Assert.assertFalse(response.contains("X-Custom-Value","1"));
|
||||
}
|
||||
|
||||
public static class MockIncludeExcludeFilter extends IncludeExcludeBasedFilter
|
||||
{
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
|
||||
{
|
||||
chain.doFilter(request,response);
|
||||
HttpServletRequest http_request = (HttpServletRequest)request;
|
||||
HttpServletResponse http_response = (HttpServletResponse)response;
|
||||
|
||||
if (!super.shouldFilter(http_request,http_response))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
http_response.setHeader("X-Custom-Value","1");
|
||||
}
|
||||
}
|
||||
|
||||
public static class NullServlet extends HttpServlet
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||
{
|
||||
if (req.getPathInfo().equals("/json"))
|
||||
{
|
||||
resp.setContentType("application/json");
|
||||
}
|
||||
else if (req.getPathInfo().equals("/json-utf8"))
|
||||
{
|
||||
resp.setContentType("application/json; charset=utf-8");
|
||||
}
|
||||
resp.setStatus(HttpStatus.NO_CONTENT_204);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue