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;