Skip to content

Commit 1e8065d

Browse files
committed
Unit test for empty Access-Control-Request-Headers (Chrome 52)
Includes optimized method/header resolution in CorsConfiguration. Issue: SPR-14617 (cherry picked from commit d047174)
1 parent f735d12 commit 1e8065d

File tree

4 files changed

+161
-103
lines changed

4 files changed

+161
-103
lines changed

spring-web/src/main/java/org/springframework/http/HttpHeaders.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -546,8 +546,8 @@ public List<String> getAccessControlRequestHeaders() {
546546
/**
547547
* Set the (new) value of the {@code Access-Control-Request-Method} request header.
548548
*/
549-
public void setAccessControlRequestMethod(HttpMethod requestedMethod) {
550-
set(ACCESS_CONTROL_REQUEST_METHOD, requestedMethod.name());
549+
public void setAccessControlRequestMethod(HttpMethod requestMethod) {
550+
set(ACCESS_CONTROL_REQUEST_METHOD, requestMethod.name());
551551
}
552552

553553
/**

spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java

Lines changed: 58 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
8+
* http://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -21,6 +21,7 @@
2121
import java.util.List;
2222

2323
import org.springframework.http.HttpMethod;
24+
import org.springframework.util.CollectionUtils;
2425
import org.springframework.util.ObjectUtils;
2526
import org.springframework.util.StringUtils;
2627

@@ -30,6 +31,7 @@
3031
*
3132
* @author Sebastien Deleuze
3233
* @author Rossen Stoyanchev
34+
* @author Juergen Hoeller
3335
* @author Sam Brannen
3436
* @since 4.2
3537
* @see <a href="http://www.w3.org/TR/cors/">CORS W3C recommendation</a>
@@ -41,10 +43,22 @@ public class CorsConfiguration {
4143
*/
4244
public static final String ALL = "*";
4345

46+
private static final List<HttpMethod> DEFAULT_METHODS;
47+
48+
static {
49+
List<HttpMethod> rawMethods = new ArrayList<HttpMethod>(2);
50+
rawMethods.add(HttpMethod.GET);
51+
rawMethods.add(HttpMethod.HEAD);
52+
DEFAULT_METHODS = Collections.unmodifiableList(rawMethods);
53+
}
54+
55+
4456
private List<String> allowedOrigins;
4557

4658
private List<String> allowedMethods;
4759

60+
private List<HttpMethod> resolvedMethods = DEFAULT_METHODS;
61+
4862
private List<String> allowedHeaders;
4963

5064
private List<String> exposedHeaders;
@@ -67,6 +81,7 @@ public CorsConfiguration() {
6781
public CorsConfiguration(CorsConfiguration other) {
6882
this.allowedOrigins = other.allowedOrigins;
6983
this.allowedMethods = other.allowedMethods;
84+
this.resolvedMethods = other.resolvedMethods;
7085
this.allowedHeaders = other.allowedHeaders;
7186
this.exposedHeaders = other.exposedHeaders;
7287
this.allowCredentials = other.allowCredentials;
@@ -86,10 +101,10 @@ public CorsConfiguration combine(CorsConfiguration other) {
86101
return this;
87102
}
88103
CorsConfiguration config = new CorsConfiguration(this);
89-
config.setAllowedOrigins(combine(this.getAllowedOrigins(), other.getAllowedOrigins()));
90-
config.setAllowedMethods(combine(this.getAllowedMethods(), other.getAllowedMethods()));
91-
config.setAllowedHeaders(combine(this.getAllowedHeaders(), other.getAllowedHeaders()));
92-
config.setExposedHeaders(combine(this.getExposedHeaders(), other.getExposedHeaders()));
104+
config.setAllowedOrigins(combine(getAllowedOrigins(), other.getAllowedOrigins()));
105+
config.setAllowedMethods(combine(getAllowedMethods(), other.getAllowedMethods()));
106+
config.setAllowedHeaders(combine(getAllowedHeaders(), other.getAllowedHeaders()));
107+
config.setExposedHeaders(combine(getExposedHeaders(), other.getExposedHeaders()));
93108
Boolean allowCredentials = other.getAllowCredentials();
94109
if (allowCredentials != null) {
95110
config.setAllowCredentials(allowCredentials);
@@ -137,7 +152,7 @@ public List<String> getAllowedOrigins() {
137152
*/
138153
public void addAllowedOrigin(String origin) {
139154
if (this.allowedOrigins == null) {
140-
this.allowedOrigins = new ArrayList<String>();
155+
this.allowedOrigins = new ArrayList<String>(4);
141156
}
142157
this.allowedOrigins.add(origin);
143158
}
@@ -146,16 +161,29 @@ public void addAllowedOrigin(String origin) {
146161
* Set the HTTP methods to allow, e.g. {@code "GET"}, {@code "POST"},
147162
* {@code "PUT"}, etc.
148163
* <p>The special value {@code "*"} allows all methods.
149-
* <p>If not set, only {@code "GET"} is allowed.
164+
* <p>If not set, only {@code "GET"} and {@code "HEAD"} are allowed.
150165
* <p>By default this is not set.
151166
*/
152167
public void setAllowedMethods(List<String> allowedMethods) {
153168
this.allowedMethods = (allowedMethods != null ? new ArrayList<String>(allowedMethods) : null);
169+
if (!CollectionUtils.isEmpty(allowedMethods)) {
170+
this.resolvedMethods = new ArrayList<HttpMethod>(allowedMethods.size());
171+
for (String method : allowedMethods) {
172+
if (ALL.equals(method)) {
173+
this.resolvedMethods = null;
174+
break;
175+
}
176+
this.resolvedMethods.add(HttpMethod.resolve(method));
177+
}
178+
}
179+
else {
180+
this.resolvedMethods = DEFAULT_METHODS;
181+
}
154182
}
155183

156184
/**
157185
* Return the allowed HTTP methods, possibly {@code null} in which case
158-
* only {@code "GET"} is allowed.
186+
* only {@code "GET"} and {@code "HEAD"} allowed.
159187
* @see #addAllowedMethod(HttpMethod)
160188
* @see #addAllowedMethod(String)
161189
* @see #setAllowedMethods(List)
@@ -179,9 +207,16 @@ public void addAllowedMethod(HttpMethod method) {
179207
public void addAllowedMethod(String method) {
180208
if (StringUtils.hasText(method)) {
181209
if (this.allowedMethods == null) {
182-
this.allowedMethods = new ArrayList<String>();
210+
this.allowedMethods = new ArrayList<String>(4);
211+
this.resolvedMethods = new ArrayList<HttpMethod>(4);
183212
}
184213
this.allowedMethods.add(method);
214+
if (ALL.equals(method)) {
215+
this.resolvedMethods = null;
216+
}
217+
else if (this.resolvedMethods != null) {
218+
this.resolvedMethods.add(HttpMethod.resolve(method));
219+
}
185220
}
186221
}
187222

@@ -213,7 +248,7 @@ public List<String> getAllowedHeaders() {
213248
*/
214249
public void addAllowedHeader(String allowedHeader) {
215250
if (this.allowedHeaders == null) {
216-
this.allowedHeaders = new ArrayList<String>();
251+
this.allowedHeaders = new ArrayList<String>(4);
217252
}
218253
this.allowedHeaders.add(allowedHeader);
219254
}
@@ -230,7 +265,7 @@ public void setExposedHeaders(List<String> exposedHeaders) {
230265
if (exposedHeaders != null && exposedHeaders.contains(ALL)) {
231266
throw new IllegalArgumentException("'*' is not a valid exposed header value");
232267
}
233-
this.exposedHeaders = (exposedHeaders == null ? null : new ArrayList<String>(exposedHeaders));
268+
this.exposedHeaders = (exposedHeaders != null ? new ArrayList<String>(exposedHeaders) : null);
234269
}
235270

236271
/**
@@ -333,27 +368,10 @@ public List<HttpMethod> checkHttpMethod(HttpMethod requestMethod) {
333368
if (requestMethod == null) {
334369
return null;
335370
}
336-
List<String> allowedMethods =
337-
(this.allowedMethods != null ? this.allowedMethods : new ArrayList<String>());
338-
if (allowedMethods.contains(ALL)) {
371+
if (this.resolvedMethods == null) {
339372
return Collections.singletonList(requestMethod);
340373
}
341-
if (allowedMethods.isEmpty()) {
342-
allowedMethods.add(HttpMethod.GET.name());
343-
allowedMethods.add(HttpMethod.HEAD.name());
344-
}
345-
List<HttpMethod> result = new ArrayList<HttpMethod>(allowedMethods.size());
346-
boolean allowed = false;
347-
for (String method : allowedMethods) {
348-
if (requestMethod.matches(method)) {
349-
allowed = true;
350-
}
351-
HttpMethod resolved = HttpMethod.resolve(method);
352-
if (resolved != null) {
353-
result.add(resolved);
354-
}
355-
}
356-
return (allowed ? result : null);
374+
return (this.resolvedMethods.contains(requestMethod) ? this.resolvedMethods : null);
357375
}
358376

359377
/**
@@ -376,14 +394,19 @@ public List<String> checkHeaders(List<String> requestHeaders) {
376394
}
377395

378396
boolean allowAnyHeader = this.allowedHeaders.contains(ALL);
379-
List<String> result = new ArrayList<String>();
397+
List<String> result = new ArrayList<String>(requestHeaders.size());
380398
for (String requestHeader : requestHeaders) {
381399
if (StringUtils.hasText(requestHeader)) {
382400
requestHeader = requestHeader.trim();
383-
for (String allowedHeader : this.allowedHeaders) {
384-
if (allowAnyHeader || requestHeader.equalsIgnoreCase(allowedHeader)) {
385-
result.add(requestHeader);
386-
break;
401+
if (allowAnyHeader) {
402+
result.add(requestHeader);
403+
}
404+
else {
405+
for (String allowedHeader : this.allowedHeaders) {
406+
if (requestHeader.equalsIgnoreCase(allowedHeader)) {
407+
result.add(requestHeader);
408+
break;
409+
}
387410
}
388411
}
389412
}

0 commit comments

Comments
 (0)