From 28c3c3e917d5979d83bca4174473427c55ef571c Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Tue, 5 Jun 2012 16:03:08 +0200
Subject: [PATCH] 381639 - CrossOriginFilter does not support
Access-Control-Expose-Headers.
---
.../jetty/servlets/CrossOriginFilter.java | 23 +++++++++++++++----
.../jetty/servlets/CrossOriginFilterTest.java | 21 +++++++++++++++++
2 files changed, 39 insertions(+), 5 deletions(-)
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java
index bb57608c4b4..eb1b9859959 100644
--- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java
@@ -54,15 +54,18 @@ import org.eclipse.jetty.util.log.Logger;
* and any 3 letter top-level domain (.com, .net, .org, etc.).
* allowedMethods, a comma separated list of HTTP methods that
* are allowed to be used when accessing the resources. Default value is
- * GET,POST
+ * GET,POST,HEAD
* allowedHeaders, a comma separated list of HTTP headers that
* are allowed to be specified when accessing the resources. Default value
- * is X-Requested-With
+ * is X-Requested-With,Content-Type,Accept,Origin
* preflightMaxAge, the number of seconds that preflight requests
* can be cached by the client. Default value is 1800 seconds, or 30
* minutes
* allowCredentials, a boolean indicating if the resource allows
* requests with credentials. Default value is false
+ * exposeHeaders, a comma separated list of HTTP headers that
+ * are allowed to be exposed on the client. Default value is the
+ * empty list
*
* A typical configuration could be:
*
@@ -79,8 +82,6 @@ import org.eclipse.jetty.util.log.Logger;
* ...
* </web-app>
*
- *
- * @version $Revision$ $Date$
*/
public class CrossOriginFilter implements Filter
{
@@ -96,12 +97,14 @@ public class CrossOriginFilter implements Filter
public static final String ACCESS_CONTROL_ALLOW_HEADERS_HEADER = "Access-Control-Allow-Headers";
public static final String ACCESS_CONTROL_MAX_AGE_HEADER = "Access-Control-Max-Age";
public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER = "Access-Control-Allow-Credentials";
+ public static final String ACCESS_CONTROL_EXPOSE_HEADERS_HEADER = "Access-Control-Expose-Headers";
// Implementation constants
public static final String ALLOWED_ORIGINS_PARAM = "allowedOrigins";
public static final String ALLOWED_METHODS_PARAM = "allowedMethods";
public static final String ALLOWED_HEADERS_PARAM = "allowedHeaders";
public static final String PREFLIGHT_MAX_AGE_PARAM = "preflightMaxAge";
public static final String ALLOW_CREDENTIALS_PARAM = "allowCredentials";
+ public static final String EXPOSED_HEADERS_PARAM = "exposedHeaders";
private static final String ANY_ORIGIN = "*";
private static final List SIMPLE_HTTP_METHODS = Arrays.asList("GET", "POST", "HEAD");
@@ -109,6 +112,7 @@ public class CrossOriginFilter implements Filter
private List allowedOrigins = new ArrayList();
private List allowedMethods = new ArrayList();
private List allowedHeaders = new ArrayList();
+ private List exposedHeaders = new ArrayList();
private int preflightMaxAge = 0;
private boolean allowCredentials;
@@ -163,6 +167,11 @@ public class CrossOriginFilter implements Filter
allowedCredentialsConfig = "true";
allowCredentials = Boolean.parseBoolean(allowedCredentialsConfig);
+ String exposedHeadersConfig = config.getInitParameter(EXPOSED_HEADERS_PARAM);
+ if (exposedHeadersConfig == null)
+ exposedHeadersConfig = "";
+ exposedHeaders.addAll(Arrays.asList(exposedHeadersConfig.split(",")));
+
if (LOG.isDebugEnabled())
{
LOG.debug("Cross-origin filter configuration: " +
@@ -170,7 +179,9 @@ public class CrossOriginFilter implements Filter
ALLOWED_METHODS_PARAM + " = " + allowedMethodsConfig + ", " +
ALLOWED_HEADERS_PARAM + " = " + allowedHeadersConfig + ", " +
PREFLIGHT_MAX_AGE_PARAM + " = " + preflightMaxAgeConfig + ", " +
- ALLOW_CREDENTIALS_PARAM + " = " + allowedCredentialsConfig);
+ ALLOW_CREDENTIALS_PARAM + " = " + allowedCredentialsConfig + "," +
+ EXPOSED_HEADERS_PARAM + " = " + exposedHeadersConfig
+ );
}
}
@@ -305,6 +316,8 @@ public class CrossOriginFilter implements Filter
response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, origin);
if (allowCredentials)
response.setHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER, "true");
+ if (!exposedHeaders.isEmpty())
+ response.setHeader(ACCESS_CONTROL_EXPOSE_HEADERS_HEADER, commify(exposedHeaders));
}
private void handlePreflightResponse(HttpServletRequest request, HttpServletResponse response, String origin)
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java
index fb8d6bba490..bd631ebf37c 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java
@@ -371,6 +371,27 @@ public class CrossOriginFilterTest
Assert.assertTrue(latch.await(1, TimeUnit.SECONDS));
}
+ @Test
+ public void testSimpleRequestWithExposedHeaders() throws Exception
+ {
+ FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
+ filterHolder.setInitParameter("exposedHeaders", "Content-Length");
+ tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
+
+ CountDownLatch latch = new CountDownLatch(1);
+ tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
+
+ String request = "" +
+ "GET / HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "Origin: http://localhost\r\n" +
+ "\r\n";
+ String response = tester.getResponses(request);
+ Assert.assertTrue(response.contains("HTTP/1.1 200"));
+ Assert.assertTrue(response.contains(CrossOriginFilter.ACCESS_CONTROL_EXPOSE_HEADERS_HEADER));
+ Assert.assertTrue(latch.await(1, TimeUnit.SECONDS));
+ }
+
public static class ResourceServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;