Skip to content

Commit 15c96b8

Browse files
committed
ServletResponseHttpHeaders consistently overrides HttpHeaders again
Issue: SPR-14406
1 parent ea5baeb commit 15c96b8

File tree

2 files changed

+111
-89
lines changed

2 files changed

+111
-89
lines changed

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

Lines changed: 104 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
* @author Arjen Poutsma
5959
* @author Sebastien Deleuze
6060
* @author Brian Clozel
61+
* @author Juergen Hoeller
6162
* @since 3.0
6263
*/
6364
public class HttpHeaders implements MultiValueMap<String, String>, Serializable {
@@ -454,7 +455,7 @@ public void setAccessControlAllowCredentials(boolean allowCredentials) {
454455
* Returns the value of the {@code Access-Control-Allow-Credentials} response header.
455456
*/
456457
public boolean getAccessControlAllowCredentials() {
457-
return new Boolean(getFirst(ACCESS_CONTROL_ALLOW_CREDENTIALS));
458+
return Boolean.parseBoolean(getFirst(ACCESS_CONTROL_ALLOW_CREDENTIALS));
458459
}
459460

460461
/**
@@ -510,22 +511,6 @@ public String getAccessControlAllowOrigin() {
510511
return getFieldValues(ACCESS_CONTROL_ALLOW_ORIGIN);
511512
}
512513

513-
protected String getFieldValues(String headerName) {
514-
List<String> headerValues = this.headers.get(headerName);
515-
if (headerValues != null) {
516-
StringBuilder builder = new StringBuilder();
517-
for (Iterator<String> iterator = headerValues.iterator(); iterator.hasNext(); ) {
518-
String ifNoneMatch = iterator.next();
519-
builder.append(ifNoneMatch);
520-
if (iterator.hasNext()) {
521-
builder.append(", ");
522-
}
523-
}
524-
return builder.toString();
525-
}
526-
return null;
527-
}
528-
529514
/**
530515
* Set the (new) value of the {@code Access-Control-Expose-Headers} response header.
531516
*/
@@ -809,62 +794,28 @@ public long getExpires() {
809794

810795
/**
811796
* Set the (new) value of the {@code If-Match} header.
797+
* @since 4.3
812798
*/
813799
public void setIfMatch(String ifMatch) {
814800
set(IF_MATCH, ifMatch);
815801
}
816802

817803
/**
818804
* Set the (new) value of the {@code If-Match} header.
805+
* @since 4.3
819806
*/
820807
public void setIfMatch(List<String> ifMatchList) {
821808
set(IF_MATCH, toCommaDelimitedString(ifMatchList));
822809
}
823810

824-
protected String toCommaDelimitedString(List<String> list) {
825-
StringBuilder builder = new StringBuilder();
826-
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
827-
String ifNoneMatch = iterator.next();
828-
builder.append(ifNoneMatch);
829-
if (iterator.hasNext()) {
830-
builder.append(", ");
831-
}
832-
}
833-
return builder.toString();
834-
}
835-
836811
/**
837812
* Return the value of the {@code If-Match} header.
813+
* @since 4.3
838814
*/
839815
public List<String> getIfMatch() {
840816
return getETagValuesAsList(IF_MATCH);
841817
}
842818

843-
protected List<String> getETagValuesAsList(String headerName) {
844-
List<String> values = get(headerName);
845-
if (values != null) {
846-
List<String> result = new ArrayList<String>();
847-
for (String value : values) {
848-
if (value != null) {
849-
Matcher matcher = ETAG_HEADER_VALUE_PATTERN.matcher(value);
850-
while (matcher.find()) {
851-
if ("*".equals(matcher.group())) {
852-
result.add(matcher.group());
853-
}
854-
else {
855-
result.add(matcher.group(1));
856-
}
857-
}
858-
if(result.size() == 0) {
859-
throw new IllegalArgumentException("Could not parse '" + headerName + "' value=" + value);
860-
}
861-
}
862-
}
863-
return result;
864-
}
865-
return Collections.emptyList();
866-
}
867-
868819
/**
869820
* Set the (new) value of the {@code If-Modified-Since} header.
870821
* <p>The date should be specified as the number of milliseconds since
@@ -904,32 +855,11 @@ public List<String> getIfNoneMatch() {
904855
return getETagValuesAsList(IF_NONE_MATCH);
905856
}
906857

907-
/**
908-
* Return all values of a given header name,
909-
* even if this header is set multiple times.
910-
* @since 4.3
911-
*/
912-
public List<String> getValuesAsList(String headerName) {
913-
List<String> values = get(headerName);
914-
if (values != null) {
915-
List<String> result = new ArrayList<String>();
916-
for (String value : values) {
917-
if (value != null) {
918-
String[] tokens = StringUtils.tokenizeToStringArray(value, ",");
919-
for (String token : tokens) {
920-
result.add(token);
921-
}
922-
}
923-
}
924-
return result;
925-
}
926-
return Collections.emptyList();
927-
}
928-
929858
/**
930859
* Set the (new) value of the {@code If-Unmodified-Since} header.
931860
* <p>The date should be specified as the number of milliseconds since
932861
* January 1, 1970 GMT.
862+
* @since 4.3
933863
*/
934864
public void setIfUnmodifiedSince(long ifUnmodifiedSince) {
935865
setDate(IF_UNMODIFIED_SINCE, ifUnmodifiedSince);
@@ -939,6 +869,7 @@ public void setIfUnmodifiedSince(long ifUnmodifiedSince) {
939869
* Return the value of the {@code If-Unmodified-Since} header.
940870
* <p>The date is returned as the number of milliseconds since
941871
* January 1, 1970 GMT. Returns -1 when the date is unknown.
872+
* @since 4.3
942873
*/
943874
public long getIfUnmodifiedSince() {
944875
return getFirstDate(IF_UNMODIFIED_SINCE, false);
@@ -1054,17 +985,31 @@ public void setVary(List<String> requestHeaders) {
1054985

1055986
/**
1056987
* Return the request header names subject to content negotiation.
988+
* @since 4.3
1057989
*/
1058990
public List<String> getVary() {
1059991
return getValuesAsList(VARY);
1060992
}
1061993

994+
/**
995+
* Set the given date under the given header name after formatting it as a string
996+
* using the pattern {@code "EEE, dd MMM yyyy HH:mm:ss zzz"}. The equivalent of
997+
* {@link #set(String, String)} but for date headers.
998+
* @since 3.2.4
999+
*/
1000+
public void setDate(String headerName, long date) {
1001+
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMATS[0], Locale.US);
1002+
dateFormat.setTimeZone(GMT);
1003+
set(headerName, dateFormat.format(new Date(date)));
1004+
}
1005+
10621006
/**
10631007
* Parse the first header value for the given header name as a date,
10641008
* return -1 if there is no value, or raise {@link IllegalArgumentException}
10651009
* if the value cannot be parsed as a date.
10661010
* @param headerName the header name
10671011
* @return the parsed date header, or -1 if none
1012+
* @since 3.2.4
10681013
*/
10691014
public long getFirstDate(String headerName) {
10701015
return getFirstDate(headerName, true);
@@ -1109,16 +1054,92 @@ private long getFirstDate(String headerName, boolean rejectInvalid) {
11091054
}
11101055

11111056
/**
1112-
* Set the given date under the given header name after formatting it as a string
1113-
* using the pattern {@code "EEE, dd MMM yyyy HH:mm:ss zzz"}. The equivalent of
1114-
* {@link #set(String, String)} but for date headers.
1057+
* Return all values of a given header name,
1058+
* even if this header is set multiple times.
1059+
* @param headerName the header name
1060+
* @return all associated values
1061+
* @since 4.3
11151062
*/
1116-
public void setDate(String headerName, long date) {
1117-
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMATS[0], Locale.US);
1118-
dateFormat.setTimeZone(GMT);
1119-
set(headerName, dateFormat.format(new Date(date)));
1063+
public List<String> getValuesAsList(String headerName) {
1064+
List<String> values = get(headerName);
1065+
if (values != null) {
1066+
List<String> result = new ArrayList<String>();
1067+
for (String value : values) {
1068+
if (value != null) {
1069+
String[] tokens = StringUtils.tokenizeToStringArray(value, ",");
1070+
for (String token : tokens) {
1071+
result.add(token);
1072+
}
1073+
}
1074+
}
1075+
return result;
1076+
}
1077+
return Collections.emptyList();
1078+
}
1079+
1080+
/**
1081+
* Retrieve a combined result from the field values of the ETag header.
1082+
* @param headerName the header name
1083+
* @return the combined result
1084+
* @since 4.3
1085+
*/
1086+
protected List<String> getETagValuesAsList(String headerName) {
1087+
List<String> values = get(headerName);
1088+
if (values != null) {
1089+
List<String> result = new ArrayList<String>();
1090+
for (String value : values) {
1091+
if (value != null) {
1092+
Matcher matcher = ETAG_HEADER_VALUE_PATTERN.matcher(value);
1093+
while (matcher.find()) {
1094+
if ("*".equals(matcher.group())) {
1095+
result.add(matcher.group());
1096+
}
1097+
else {
1098+
result.add(matcher.group(1));
1099+
}
1100+
}
1101+
if (result.isEmpty()) {
1102+
throw new IllegalArgumentException(
1103+
"Could not parse header '" + headerName + "' with value '" + value + "'");
1104+
}
1105+
}
1106+
}
1107+
return result;
1108+
}
1109+
return Collections.emptyList();
11201110
}
11211111

1112+
/**
1113+
* Retrieve a combined result from the field values of multi-valued headers.
1114+
* @param headerName the header name
1115+
* @return the combined result
1116+
* @since 4.3
1117+
*/
1118+
protected String getFieldValues(String headerName) {
1119+
List<String> headerValues = get(headerName);
1120+
return (headerValues != null ? toCommaDelimitedString(headerValues) : null);
1121+
}
1122+
1123+
/**
1124+
* Turn the given list of header values into a comma-delimited result.
1125+
* @param headerValues the list of header values
1126+
* @return a combined result with comma delimitation
1127+
*/
1128+
protected String toCommaDelimitedString(List<String> headerValues) {
1129+
StringBuilder builder = new StringBuilder();
1130+
for (Iterator<String> it = headerValues.iterator(); it.hasNext(); ) {
1131+
String val = it.next();
1132+
builder.append(val);
1133+
if (it.hasNext()) {
1134+
builder.append(", ");
1135+
}
1136+
}
1137+
return builder.toString();
1138+
}
1139+
1140+
1141+
// MultiValueMap implementation
1142+
11221143
/**
11231144
* Return the first header value for the given header name, if any.
11241145
* @param headerName the header name

spring-web/src/test/java/org/springframework/http/server/ServletServerHttpResponseTests.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,13 +29,12 @@
2929
import org.springframework.mock.web.test.MockHttpServletResponse;
3030
import org.springframework.util.FileCopyUtils;
3131

32-
import static org.junit.Assert.assertArrayEquals;
33-
import static org.junit.Assert.assertEquals;
34-
import static org.junit.Assert.assertTrue;
32+
import static org.junit.Assert.*;
3533

3634
/**
3735
* @author Arjen Poutsma
3836
* @author Rossen Stoyanchev
37+
* @author Juergen Hoeller
3938
*/
4039
public class ServletServerHttpResponseTests {
4140

@@ -79,7 +78,6 @@ public void getHeaders() throws Exception {
7978

8079
@Test
8180
public void preExistingHeadersFromHttpServletResponse() {
82-
8381
String headerName = "Access-Control-Allow-Origin";
8482
String headerValue = "localhost:8080";
8583

@@ -89,6 +87,8 @@ public void preExistingHeadersFromHttpServletResponse() {
8987
assertEquals(headerValue, this.response.getHeaders().getFirst(headerName));
9088
assertEquals(Collections.singletonList(headerValue), this.response.getHeaders().get(headerName));
9189
assertTrue(this.response.getHeaders().containsKey(headerName));
90+
assertEquals(headerValue, this.response.getHeaders().getFirst(headerName));
91+
assertEquals(headerValue, this.response.getHeaders().getAccessControlAllowOrigin());
9292
}
9393

9494
@Test
@@ -98,4 +98,5 @@ public void getBody() throws Exception {
9898

9999
assertArrayEquals("Invalid content written", content, mockResponse.getContentAsByteArray());
100100
}
101-
}
101+
102+
}

0 commit comments

Comments
 (0)