Unit test for empty Access-Control-Request-Headers (Chrome 52)

Includes optimized method/header resolution in CorsConfiguration.

Issue: SPR-14617
This commit is contained in:
Juergen Hoeller
2016-08-24 11:40:18 +02:00
parent e4575330c3
commit d047174c6b
4 changed files with 161 additions and 103 deletions

View File

@@ -546,8 +546,8 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
/** /**
* Set the (new) value of the {@code Access-Control-Request-Method} request header. * Set the (new) value of the {@code Access-Control-Request-Method} request header.
*/ */
public void setAccessControlRequestMethod(HttpMethod requestedMethod) { public void setAccessControlRequestMethod(HttpMethod requestMethod) {
set(ACCESS_CONTROL_REQUEST_METHOD, requestedMethod.name()); set(ACCESS_CONTROL_REQUEST_METHOD, requestMethod.name());
} }
/** /**

View File

@@ -21,6 +21,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@@ -30,6 +31,7 @@ import org.springframework.util.StringUtils;
* *
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Juergen Hoeller
* @author Sam Brannen * @author Sam Brannen
* @since 4.2 * @since 4.2
* @see <a href="http://www.w3.org/TR/cors/">CORS W3C recommendation</a> * @see <a href="http://www.w3.org/TR/cors/">CORS W3C recommendation</a>
@@ -41,10 +43,22 @@ public class CorsConfiguration {
*/ */
public static final String ALL = "*"; public static final String ALL = "*";
private static final List<HttpMethod> DEFAULT_METHODS;
static {
List<HttpMethod> rawMethods = new ArrayList<>(2);
rawMethods.add(HttpMethod.GET);
rawMethods.add(HttpMethod.HEAD);
DEFAULT_METHODS = Collections.unmodifiableList(rawMethods);
}
private List<String> allowedOrigins; private List<String> allowedOrigins;
private List<String> allowedMethods; private List<String> allowedMethods;
private List<HttpMethod> resolvedMethods = DEFAULT_METHODS;
private List<String> allowedHeaders; private List<String> allowedHeaders;
private List<String> exposedHeaders; private List<String> exposedHeaders;
@@ -67,6 +81,7 @@ public class CorsConfiguration {
public CorsConfiguration(CorsConfiguration other) { public CorsConfiguration(CorsConfiguration other) {
this.allowedOrigins = other.allowedOrigins; this.allowedOrigins = other.allowedOrigins;
this.allowedMethods = other.allowedMethods; this.allowedMethods = other.allowedMethods;
this.resolvedMethods = other.resolvedMethods;
this.allowedHeaders = other.allowedHeaders; this.allowedHeaders = other.allowedHeaders;
this.exposedHeaders = other.exposedHeaders; this.exposedHeaders = other.exposedHeaders;
this.allowCredentials = other.allowCredentials; this.allowCredentials = other.allowCredentials;
@@ -86,10 +101,10 @@ public class CorsConfiguration {
return this; return this;
} }
CorsConfiguration config = new CorsConfiguration(this); CorsConfiguration config = new CorsConfiguration(this);
config.setAllowedOrigins(combine(this.getAllowedOrigins(), other.getAllowedOrigins())); config.setAllowedOrigins(combine(getAllowedOrigins(), other.getAllowedOrigins()));
config.setAllowedMethods(combine(this.getAllowedMethods(), other.getAllowedMethods())); config.setAllowedMethods(combine(getAllowedMethods(), other.getAllowedMethods()));
config.setAllowedHeaders(combine(this.getAllowedHeaders(), other.getAllowedHeaders())); config.setAllowedHeaders(combine(getAllowedHeaders(), other.getAllowedHeaders()));
config.setExposedHeaders(combine(this.getExposedHeaders(), other.getExposedHeaders())); config.setExposedHeaders(combine(getExposedHeaders(), other.getExposedHeaders()));
Boolean allowCredentials = other.getAllowCredentials(); Boolean allowCredentials = other.getAllowCredentials();
if (allowCredentials != null) { if (allowCredentials != null) {
config.setAllowCredentials(allowCredentials); config.setAllowCredentials(allowCredentials);
@@ -137,7 +152,7 @@ public class CorsConfiguration {
*/ */
public void addAllowedOrigin(String origin) { public void addAllowedOrigin(String origin) {
if (this.allowedOrigins == null) { if (this.allowedOrigins == null) {
this.allowedOrigins = new ArrayList<>(); this.allowedOrigins = new ArrayList<>(4);
} }
this.allowedOrigins.add(origin); this.allowedOrigins.add(origin);
} }
@@ -146,16 +161,29 @@ public class CorsConfiguration {
* Set the HTTP methods to allow, e.g. {@code "GET"}, {@code "POST"}, * Set the HTTP methods to allow, e.g. {@code "GET"}, {@code "POST"},
* {@code "PUT"}, etc. * {@code "PUT"}, etc.
* <p>The special value {@code "*"} allows all methods. * <p>The special value {@code "*"} allows all methods.
* <p>If not set, only {@code "GET"} is allowed. * <p>If not set, only {@code "GET"} and {@code "HEAD"} are allowed.
* <p>By default this is not set. * <p>By default this is not set.
*/ */
public void setAllowedMethods(List<String> allowedMethods) { public void setAllowedMethods(List<String> allowedMethods) {
this.allowedMethods = (allowedMethods != null ? new ArrayList<>(allowedMethods) : null); this.allowedMethods = (allowedMethods != null ? new ArrayList<>(allowedMethods) : null);
if (!CollectionUtils.isEmpty(allowedMethods)) {
this.resolvedMethods = new ArrayList<>(allowedMethods.size());
for (String method : allowedMethods) {
if (ALL.equals(method)) {
this.resolvedMethods = null;
break;
}
this.resolvedMethods.add(HttpMethod.resolve(method));
}
}
else {
this.resolvedMethods = DEFAULT_METHODS;
}
} }
/** /**
* Return the allowed HTTP methods, possibly {@code null} in which case * Return the allowed HTTP methods, possibly {@code null} in which case
* only {@code "GET"} is allowed. * only {@code "GET"} and {@code "HEAD"} allowed.
* @see #addAllowedMethod(HttpMethod) * @see #addAllowedMethod(HttpMethod)
* @see #addAllowedMethod(String) * @see #addAllowedMethod(String)
* @see #setAllowedMethods(List) * @see #setAllowedMethods(List)
@@ -179,9 +207,16 @@ public class CorsConfiguration {
public void addAllowedMethod(String method) { public void addAllowedMethod(String method) {
if (StringUtils.hasText(method)) { if (StringUtils.hasText(method)) {
if (this.allowedMethods == null) { if (this.allowedMethods == null) {
this.allowedMethods = new ArrayList<>(); this.allowedMethods = new ArrayList<>(4);
this.resolvedMethods = new ArrayList<>(4);
} }
this.allowedMethods.add(method); this.allowedMethods.add(method);
if (ALL.equals(method)) {
this.resolvedMethods = null;
}
else if (this.resolvedMethods != null) {
this.resolvedMethods.add(HttpMethod.resolve(method));
}
} }
} }
@@ -213,7 +248,7 @@ public class CorsConfiguration {
*/ */
public void addAllowedHeader(String allowedHeader) { public void addAllowedHeader(String allowedHeader) {
if (this.allowedHeaders == null) { if (this.allowedHeaders == null) {
this.allowedHeaders = new ArrayList<>(); this.allowedHeaders = new ArrayList<>(4);
} }
this.allowedHeaders.add(allowedHeader); this.allowedHeaders.add(allowedHeader);
} }
@@ -230,7 +265,7 @@ public class CorsConfiguration {
if (exposedHeaders != null && exposedHeaders.contains(ALL)) { if (exposedHeaders != null && exposedHeaders.contains(ALL)) {
throw new IllegalArgumentException("'*' is not a valid exposed header value"); throw new IllegalArgumentException("'*' is not a valid exposed header value");
} }
this.exposedHeaders = (exposedHeaders == null ? null : new ArrayList<>(exposedHeaders)); this.exposedHeaders = (exposedHeaders != null ? new ArrayList<>(exposedHeaders) : null);
} }
/** /**
@@ -251,7 +286,7 @@ public class CorsConfiguration {
throw new IllegalArgumentException("'*' is not a valid exposed header value"); throw new IllegalArgumentException("'*' is not a valid exposed header value");
} }
if (this.exposedHeaders == null) { if (this.exposedHeaders == null) {
this.exposedHeaders = new ArrayList<>(); this.exposedHeaders = new ArrayList<>(4);
} }
this.exposedHeaders.add(exposedHeader); this.exposedHeaders.add(exposedHeader);
} }
@@ -333,27 +368,10 @@ public class CorsConfiguration {
if (requestMethod == null) { if (requestMethod == null) {
return null; return null;
} }
List<String> allowedMethods = if (this.resolvedMethods == null) {
(this.allowedMethods != null ? this.allowedMethods : new ArrayList<>());
if (allowedMethods.contains(ALL)) {
return Collections.singletonList(requestMethod); return Collections.singletonList(requestMethod);
} }
if (allowedMethods.isEmpty()) { return (this.resolvedMethods.contains(requestMethod) ? this.resolvedMethods : null);
allowedMethods.add(HttpMethod.GET.name());
allowedMethods.add(HttpMethod.HEAD.name());
}
List<HttpMethod> result = new ArrayList<>(allowedMethods.size());
boolean allowed = false;
for (String method : allowedMethods) {
if (requestMethod.matches(method)) {
allowed = true;
}
HttpMethod resolved = HttpMethod.resolve(method);
if (resolved != null) {
result.add(resolved);
}
}
return (allowed ? result : null);
} }
/** /**
@@ -376,14 +394,19 @@ public class CorsConfiguration {
} }
boolean allowAnyHeader = this.allowedHeaders.contains(ALL); boolean allowAnyHeader = this.allowedHeaders.contains(ALL);
List<String> result = new ArrayList<>(); List<String> result = new ArrayList<>(requestHeaders.size());
for (String requestHeader : requestHeaders) { for (String requestHeader : requestHeaders) {
if (StringUtils.hasText(requestHeader)) { if (StringUtils.hasText(requestHeader)) {
requestHeader = requestHeader.trim(); requestHeader = requestHeader.trim();
for (String allowedHeader : this.allowedHeaders) { if (allowAnyHeader) {
if (allowAnyHeader || requestHeader.equalsIgnoreCase(allowedHeader)) { result.add(requestHeader);
result.add(requestHeader); }
break; else {
for (String allowedHeader : this.allowedHeaders) {
if (requestHeader.equalsIgnoreCase(allowedHeader)) {
result.add(requestHeader);
break;
}
} }
} }
} }

View File

@@ -16,6 +16,8 @@
package org.springframework.web.cors; package org.springframework.web.cors;
import javax.servlet.http.HttpServletResponse;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -24,8 +26,6 @@ import org.springframework.http.HttpMethod;
import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse; import org.springframework.mock.web.test.MockHttpServletResponse;
import javax.servlet.http.HttpServletResponse;
import static org.junit.Assert.*; import static org.junit.Assert.*;
/** /**
@@ -33,6 +33,7 @@ import static org.junit.Assert.*;
* *
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Juergen Hoeller
*/ */
public class DefaultCorsProcessorTests { public class DefaultCorsProcessorTests {
@@ -56,22 +57,25 @@ public class DefaultCorsProcessorTests {
this.processor = new DefaultCorsProcessor(); this.processor = new DefaultCorsProcessor();
} }
@Test @Test
public void actualRequestWithOriginHeader() throws Exception { public void actualRequestWithOriginHeader() throws Exception {
this.request.setMethod(HttpMethod.GET.name()); this.request.setMethod(HttpMethod.GET.name());
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.processor.processRequest(this.conf, request, response);
this.processor.processRequest(this.conf, this.request, this.response);
assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); assertEquals(HttpServletResponse.SC_FORBIDDEN, this.response.getStatus());
} }
@Test @Test
public void actualRequestWithOriginHeaderAndNullConfig() throws Exception { public void actualRequestWithOriginHeaderAndNullConfig() throws Exception {
this.request.setMethod(HttpMethod.GET.name()); this.request.setMethod(HttpMethod.GET.name());
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.processor.processRequest(null, request, response);
this.processor.processRequest(null, this.request, this.response);
assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertEquals(HttpServletResponse.SC_OK, response.getStatus()); assertEquals(HttpServletResponse.SC_OK, this.response.getStatus());
} }
@Test @Test
@@ -79,12 +83,13 @@ public class DefaultCorsProcessorTests {
this.request.setMethod(HttpMethod.GET.name()); this.request.setMethod(HttpMethod.GET.name());
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.conf.addAllowedOrigin("*"); this.conf.addAllowedOrigin("*");
this.processor.processRequest(this.conf, request, response);
this.processor.processRequest(this.conf, this.request, this.response);
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertEquals("*", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertEquals("*", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE)); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE));
assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS)); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS));
assertEquals(HttpServletResponse.SC_OK, response.getStatus()); assertEquals(HttpServletResponse.SC_OK, this.response.getStatus());
} }
@Test @Test
@@ -95,12 +100,13 @@ public class DefaultCorsProcessorTests {
this.conf.addAllowedOrigin("http://domain2.com"); this.conf.addAllowedOrigin("http://domain2.com");
this.conf.addAllowedOrigin("http://domain3.com"); this.conf.addAllowedOrigin("http://domain3.com");
this.conf.setAllowCredentials(true); this.conf.setAllowCredentials(true);
this.processor.processRequest(this.conf, request, response);
this.processor.processRequest(this.conf, this.request, this.response);
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertEquals("http://domain2.com", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertEquals("http://domain2.com", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
assertEquals("true", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); assertEquals("true", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
assertEquals(HttpServletResponse.SC_OK, response.getStatus()); assertEquals(HttpServletResponse.SC_OK, this.response.getStatus());
} }
@Test @Test
@@ -109,12 +115,13 @@ public class DefaultCorsProcessorTests {
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.conf.addAllowedOrigin("*"); this.conf.addAllowedOrigin("*");
this.conf.setAllowCredentials(true); this.conf.setAllowCredentials(true);
this.processor.processRequest(this.conf, request, response);
this.processor.processRequest(this.conf, this.request, this.response);
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertEquals("http://domain2.com", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertEquals("http://domain2.com", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
assertEquals("true", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); assertEquals("true", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
assertEquals(HttpServletResponse.SC_OK, response.getStatus()); assertEquals(HttpServletResponse.SC_OK, this.response.getStatus());
} }
@Test @Test
@@ -122,9 +129,10 @@ public class DefaultCorsProcessorTests {
this.request.setMethod(HttpMethod.GET.name()); this.request.setMethod(HttpMethod.GET.name());
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.conf.addAllowedOrigin("http://DOMAIN2.com"); this.conf.addAllowedOrigin("http://DOMAIN2.com");
this.processor.processRequest(this.conf, request, response);
this.processor.processRequest(this.conf, this.request, this.response);
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertEquals(HttpServletResponse.SC_OK, response.getStatus()); assertEquals(HttpServletResponse.SC_OK, this.response.getStatus());
} }
@Test @Test
@@ -134,13 +142,14 @@ public class DefaultCorsProcessorTests {
this.conf.addExposedHeader("header1"); this.conf.addExposedHeader("header1");
this.conf.addExposedHeader("header2"); this.conf.addExposedHeader("header2");
this.conf.addAllowedOrigin("http://domain2.com"); this.conf.addAllowedOrigin("http://domain2.com");
this.processor.processRequest(this.conf, request, response);
this.processor.processRequest(this.conf, this.request, this.response);
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertEquals("http://domain2.com", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertEquals("http://domain2.com", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS));
assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS).contains("header1")); assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS).contains("header1"));
assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS).contains("header2")); assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS).contains("header2"));
assertEquals(HttpServletResponse.SC_OK, response.getStatus()); assertEquals(HttpServletResponse.SC_OK, this.response.getStatus());
} }
@Test @Test
@@ -149,8 +158,9 @@ public class DefaultCorsProcessorTests {
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.conf.addAllowedOrigin("*"); this.conf.addAllowedOrigin("*");
this.processor.processRequest(this.conf, request, response);
assertEquals(HttpServletResponse.SC_OK, response.getStatus()); this.processor.processRequest(this.conf, this.request, this.response);
assertEquals(HttpServletResponse.SC_OK, this.response.getStatus());
} }
@Test @Test
@@ -159,8 +169,9 @@ public class DefaultCorsProcessorTests {
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "DELETE"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "DELETE");
this.conf.addAllowedOrigin("*"); this.conf.addAllowedOrigin("*");
this.processor.processRequest(this.conf, request, response);
assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); this.processor.processRequest(this.conf, this.request, this.response);
assertEquals(HttpServletResponse.SC_FORBIDDEN, this.response.getStatus());
} }
@Test @Test
@@ -169,18 +180,20 @@ public class DefaultCorsProcessorTests {
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.conf.addAllowedOrigin("*"); this.conf.addAllowedOrigin("*");
this.processor.processRequest(this.conf, request, response);
assertEquals(HttpServletResponse.SC_OK, response.getStatus()); this.processor.processRequest(this.conf, this.request, this.response);
assertEquals("GET,HEAD", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)); assertEquals(HttpServletResponse.SC_OK, this.response.getStatus());
assertEquals("GET,HEAD", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS));
} }
@Test @Test
public void preflightRequestTestWithOriginButWithoutOtherHeaders() throws Exception { public void preflightRequestTestWithOriginButWithoutOtherHeaders() throws Exception {
this.request.setMethod(HttpMethod.OPTIONS.name()); this.request.setMethod(HttpMethod.OPTIONS.name());
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.processor.processRequest(this.conf, request, response);
this.processor.processRequest(this.conf, this.request, this.response);
assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); assertEquals(HttpServletResponse.SC_FORBIDDEN, this.response.getStatus());
} }
@Test @Test
@@ -188,112 +201,134 @@ public class DefaultCorsProcessorTests {
this.request.setMethod(HttpMethod.OPTIONS.name()); this.request.setMethod(HttpMethod.OPTIONS.name());
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
this.processor.processRequest(this.conf, request, response);
this.processor.processRequest(this.conf, this.request, this.response);
assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); assertEquals(HttpServletResponse.SC_FORBIDDEN, this.response.getStatus());
} }
@Test @Test
public void preflightRequestWithRequestAndMethodHeaderButNoConfig() throws Exception { public void preflightRequestWithRequestAndMethodHeaderButNoConfig() throws Exception {
this.request.setMethod(HttpMethod.OPTIONS.name()); this.request.setMethod(HttpMethod.OPTIONS.name());
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.processor.processRequest(this.conf, request, response); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
this.processor.processRequest(this.conf, this.request, this.response);
assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); assertEquals(HttpServletResponse.SC_FORBIDDEN, this.response.getStatus());
} }
@Test @Test
public void preflightRequestValidRequestAndConfig() throws Exception { public void preflightRequestValidRequestAndConfig() throws Exception {
this.request.setMethod(HttpMethod.OPTIONS.name()); this.request.setMethod(HttpMethod.OPTIONS.name());
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
this.conf.addAllowedOrigin("*"); this.conf.addAllowedOrigin("*");
this.conf.addAllowedMethod("GET"); this.conf.addAllowedMethod("GET");
this.conf.addAllowedMethod("PUT"); this.conf.addAllowedMethod("PUT");
this.conf.addAllowedHeader("header1"); this.conf.addAllowedHeader("header1");
this.conf.addAllowedHeader("header2"); this.conf.addAllowedHeader("header2");
this.processor.processRequest(this.conf, request, response);
this.processor.processRequest(this.conf, this.request, this.response);
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertEquals("*", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertEquals("*", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS));
assertEquals("GET,PUT", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)); assertEquals("GET,PUT", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS));
assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE)); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE));
assertEquals(HttpServletResponse.SC_OK, response.getStatus()); assertEquals(HttpServletResponse.SC_OK, this.response.getStatus());
} }
@Test @Test
public void preflightRequestCredentials() throws Exception { public void preflightRequestCredentials() throws Exception {
this.request.setMethod(HttpMethod.OPTIONS.name()); this.request.setMethod(HttpMethod.OPTIONS.name());
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
this.conf.addAllowedOrigin("http://domain1.com"); this.conf.addAllowedOrigin("http://domain1.com");
this.conf.addAllowedOrigin("http://domain2.com"); this.conf.addAllowedOrigin("http://domain2.com");
this.conf.addAllowedOrigin("http://domain3.com"); this.conf.addAllowedOrigin("http://domain3.com");
this.conf.addAllowedHeader("Header1"); this.conf.addAllowedHeader("Header1");
this.conf.setAllowCredentials(true); this.conf.setAllowCredentials(true);
this.processor.processRequest(this.conf, request, response);
this.processor.processRequest(this.conf, this.request, this.response);
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertEquals("http://domain2.com", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertEquals("http://domain2.com", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
assertEquals("true", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)); assertEquals("true", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
assertEquals(HttpServletResponse.SC_OK, response.getStatus()); assertEquals(HttpServletResponse.SC_OK, this.response.getStatus());
} }
@Test @Test
public void preflightRequestCredentialsWithOriginWildcard() throws Exception { public void preflightRequestCredentialsWithOriginWildcard() throws Exception {
this.request.setMethod(HttpMethod.OPTIONS.name()); this.request.setMethod(HttpMethod.OPTIONS.name());
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1");
this.conf.addAllowedOrigin("http://domain1.com"); this.conf.addAllowedOrigin("http://domain1.com");
this.conf.addAllowedOrigin("*"); this.conf.addAllowedOrigin("*");
this.conf.addAllowedOrigin("http://domain3.com"); this.conf.addAllowedOrigin("http://domain3.com");
this.conf.addAllowedHeader("Header1"); this.conf.addAllowedHeader("Header1");
this.conf.setAllowCredentials(true); this.conf.setAllowCredentials(true);
this.processor.processRequest(this.conf, request, response);
this.processor.processRequest(this.conf, this.request, this.response);
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertEquals("http://domain2.com", response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertEquals("http://domain2.com", this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertEquals(HttpServletResponse.SC_OK, response.getStatus()); assertEquals(HttpServletResponse.SC_OK, this.response.getStatus());
} }
@Test @Test
public void preflightRequestAllowedHeaders() throws Exception { public void preflightRequestAllowedHeaders() throws Exception {
this.request.setMethod(HttpMethod.OPTIONS.name()); this.request.setMethod(HttpMethod.OPTIONS.name());
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1, Header2");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1, Header2");
this.conf.addAllowedHeader("Header1"); this.conf.addAllowedHeader("Header1");
this.conf.addAllowedHeader("Header2"); this.conf.addAllowedHeader("Header2");
this.conf.addAllowedHeader("Header3"); this.conf.addAllowedHeader("Header3");
this.conf.addAllowedOrigin("http://domain2.com"); this.conf.addAllowedOrigin("http://domain2.com");
this.processor.processRequest(this.conf, request, response);
this.processor.processRequest(this.conf, this.request, this.response);
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS));
assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header1")); assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header1"));
assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header2")); assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header2"));
assertFalse(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header3")); assertFalse(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header3"));
assertEquals(HttpServletResponse.SC_OK, response.getStatus()); assertEquals(HttpServletResponse.SC_OK, this.response.getStatus());
} }
@Test @Test
public void preflightRequestAllowsAllHeaders() throws Exception { public void preflightRequestAllowsAllHeaders() throws Exception {
this.request.setMethod(HttpMethod.OPTIONS.name()); this.request.setMethod(HttpMethod.OPTIONS.name());
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1, Header2");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "Header1, Header2");
this.conf.addAllowedHeader("*"); this.conf.addAllowedHeader("*");
this.conf.addAllowedOrigin("http://domain2.com"); this.conf.addAllowedOrigin("http://domain2.com");
this.processor.processRequest(this.conf, request, response);
this.processor.processRequest(this.conf, this.request, this.response);
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS)); assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS));
assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header1")); assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header1"));
assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header2")); assertTrue(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("Header2"));
assertFalse(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("*")); assertFalse(this.response.getHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS).contains("*"));
assertEquals(HttpServletResponse.SC_OK, response.getStatus()); assertEquals(HttpServletResponse.SC_OK, this.response.getStatus());
}
@Test
public void preflightRequestWithEmptyHeaders() throws Exception {
this.request.setMethod(HttpMethod.OPTIONS.name());
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS, "");
this.conf.addAllowedHeader("*");
this.conf.addAllowedOrigin("http://domain2.com");
this.processor.processRequest(this.conf, this.request, this.response);
assertTrue(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS));
assertEquals(HttpServletResponse.SC_OK, this.response.getStatus());
} }
@Test @Test
@@ -302,9 +337,10 @@ public class DefaultCorsProcessorTests {
this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com"); this.request.addHeader(HttpHeaders.ORIGIN, "http://domain2.com");
this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); this.request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
this.conf.addAllowedOrigin("*"); this.conf.addAllowedOrigin("*");
this.processor.processRequest(null, request, response);
this.processor.processRequest(null, this.request, this.response);
assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); assertFalse(this.response.containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); assertEquals(HttpServletResponse.SC_FORBIDDEN, this.response.getStatus());
} }
} }

View File

@@ -64,8 +64,7 @@ import org.springframework.web.util.UrlPathHelper;
* @see #setInterceptors * @see #setInterceptors
* @see org.springframework.web.servlet.HandlerInterceptor * @see org.springframework.web.servlet.HandlerInterceptor
*/ */
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
implements HandlerMapping, Ordered {
private int order = Integer.MAX_VALUE; // default: same as non-Ordered private int order = Integer.MAX_VALUE; // default: same as non-Ordered
@@ -236,6 +235,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
return this.corsConfigSource.getCorsConfigurations(); return this.corsConfigSource.getCorsConfigurations();
} }
/** /**
* Initializes the interceptors. * Initializes the interceptors.
* @see #extendInterceptors(java.util.List) * @see #extendInterceptors(java.util.List)
@@ -339,6 +339,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
return (count > 0 ? mappedInterceptors.toArray(new MappedInterceptor[count]) : null); return (count > 0 ? mappedInterceptors.toArray(new MappedInterceptor[count]) : null);
} }
/** /**
* Look up a handler for the given request, falling back to the default * Look up a handler for the given request, falling back to the default
* handler if no specific one is found. * handler if no specific one is found.
@@ -480,9 +481,7 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
} }
@Override @Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
throws IOException {
corsProcessor.processRequest(this.config, request, response); corsProcessor.processRequest(this.config, request, response);
} }
@@ -502,8 +501,8 @@ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
} }
@Override @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
Object handler) throws Exception { throws Exception {
return corsProcessor.processRequest(this.config, request, response); return corsProcessor.processRequest(this.config, request, response);
} }