Merge remote-tracking branch 'origin/jetty-9.4.x'

This commit is contained in:
Greg Wilkins 2017-06-22 11:24:21 +02:00
commit 7fd45b6766
4 changed files with 107 additions and 93 deletions

View File

@ -28,7 +28,8 @@
[[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.
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.
This filter processes its configured headers before calling `doFilter` in the filter chain. Some of the headers configured in this filter may get overwritten by other filters and/or the servlet processing the request.
===== Required JARs
@ -76,25 +77,25 @@ ____
The following `init` parameters control the behavior of the filter:
includedPaths::
Optional. CSV of included path specs.
Optional. Comma separated values of included path specs.
excludedPaths::
Optional. CSV of excluded path specs.
Optional. Comma separated values of excluded path specs.
includedMimeTypes::
Optional. CSV of included mime types.
Optional. Comma separated values of included mime types. The mime type will be guessed from the extension at the end of the request URL if the content type has not been set on the response.
excludedMimeTypes::
Optional. CSV of excluded mime types.
Optional. Comma separated values of excluded mime types. The mime type will be guessed from the extension at the end of the request URL if the content type has not been set on the response.
includedHttpMethods::
Optional. CSV of included http methods.
Optional. Comma separated values of included http methods.
excludedHttpMethods::
Optional. CSV of excluded http methods.
Optional. Comma separated values of excluded http methods.
headerConfig::
CSV of actions to perform on headers. The syntax for each action is `action headerName: headerValue`.
Comma separated values of actions to perform on headers. The syntax for each action is `action headerName: headerValue`.
Supported header actions:

View File

@ -80,42 +80,40 @@ public class HeaderFilter 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))
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());
}
}
}
}
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());
}
}
}
chain.doFilter(request,response);
}
@Override

View File

@ -110,6 +110,33 @@ public abstract class IncludeExcludeBasedFilter implements Filter
}
}
protected String guessMimeType(HttpServletRequest http_request, HttpServletResponse http_response)
{
String content_type = http_response.getContentType();
LOG.debug("Content Type is: {}",content_type);
String mime_type = "";
if (content_type != null)
{
mime_type = MimeTypes.getContentTypeWithoutCharset(content_type);
LOG.debug("Mime Type is: {}",mime_type);
}
else
{
String request_url = http_request.getPathInfo();
mime_type = MimeTypes.getDefaultMimeByExtension(request_url);
if (mime_type == null)
{
mime_type = "";
}
LOG.debug("Guessed mime type is {}",mime_type);
}
return mime_type;
}
protected boolean shouldFilter(HttpServletRequest http_request, HttpServletResponse http_response)
{
String http_method = http_request.getMethod();
@ -120,12 +147,8 @@ public abstract class IncludeExcludeBasedFilter implements Filter
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);
String mime_type = guessMimeType(http_request,http_response);
LOG.debug("Mime Type is: {}",content_type);
if (!_mimeTypes.test(mime_type))
{
LOG.debug("should not apply filter because mime type does not match");

View File

@ -224,7 +224,24 @@ public class IncludeExcludeBasedFilterTest
request.setMethod("GET");
request.setVersion("HTTP/1.1");
request.setHeader("Host","localhost");
request.setURI("/context/test/json");
request.setURI("/context/test/json.json");
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
Assert.assertTrue(response.contains("X-Custom-Value","1"));
}
@Test
public void testIncludeExcludeFilterIncludeMimeTypeMatchWithQueryString() 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.json?some=value");
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
Assert.assertTrue(response.contains("X-Custom-Value","1"));
@ -241,7 +258,24 @@ public class IncludeExcludeBasedFilterTest
request.setMethod("GET");
request.setVersion("HTTP/1.1");
request.setHeader("Host","localhost");
request.setURI("/context/test/json");
request.setURI("/context/test/json.json");
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
Assert.assertFalse(response.contains("X-Custom-Value","1"));
}
@Test
public void testIncludeExcludeFilterIncludeMimeTypeNoMatchNoExtension() 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/abcdef");
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
Assert.assertFalse(response.contains("X-Custom-Value","1"));
@ -258,7 +292,7 @@ public class IncludeExcludeBasedFilterTest
request.setMethod("GET");
request.setVersion("HTTP/1.1");
request.setHeader("Host","localhost");
request.setURI("/context/test/json");
request.setURI("/context/test/json.json");
HttpTester.Response response = HttpTester.parseResponse(_tester.getResponses(request.generate()));
Assert.assertFalse(response.contains("X-Custom-Value","1"));
@ -275,61 +309,26 @@ public class IncludeExcludeBasedFilterTest
request.setMethod("GET");
request.setVersion("HTTP/1.1");
request.setHeader("Host","localhost");
request.setURI("/context/test/json");
request.setURI("/context/test/json.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))
if (super.shouldFilter(http_request,http_response))
{
return;
http_response.setHeader("X-Custom-Value","1");
}
http_response.setHeader("X-Custom-Value","1");
chain.doFilter(request,response);
}
}
@ -338,15 +337,8 @@ public class IncludeExcludeBasedFilterTest
@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);
resp.flushBuffer();
}
}