@@ -75,11 +75,7 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
75
75
76
76
private static final long serialVersionUID = -8578554704772377436L ;
77
77
78
- /**
79
- * The empty {@code HttpHeaders} instance (immutable).
80
- */
81
- public static final HttpHeaders EMPTY =
82
- new ReadOnlyHttpHeaders (new HttpHeaders (new LinkedMultiValueMap <>(0 )));
78
+
83
79
/**
84
80
* The HTTP {@code Accept} header field name.
85
81
* @see <a href="http://tools.ietf.org/html/rfc7231#section-5.3.2">Section 5.3.2 of RFC 7231</a>
@@ -381,6 +377,12 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
381
377
*/
382
378
public static final String WWW_AUTHENTICATE = "WWW-Authenticate" ;
383
379
380
+
381
+ /**
382
+ * The empty {@code HttpHeaders} instance (immutable).
383
+ */
384
+ public static final HttpHeaders EMPTY = new ReadOnlyHttpHeaders (new HttpHeaders (new LinkedMultiValueMap <>(0 )));
385
+
384
386
/**
385
387
* Pattern matching ETag multiple field values in headers such as "If-Match", "If-None-Match".
386
388
* @see <a href="https://tools.ietf.org/html/rfc7232#section-2.3">Section 2.3 of RFC 7232</a>
@@ -409,15 +411,15 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
409
411
* Construct a new, empty instance of the {@code HttpHeaders} object.
410
412
*/
411
413
public HttpHeaders () {
412
- this (CollectionUtils .toMultiValueMap (
413
- new LinkedCaseInsensitiveMap <>(8 , Locale .ENGLISH )));
414
+ this (CollectionUtils .toMultiValueMap (new LinkedCaseInsensitiveMap <>(8 , Locale .ENGLISH )));
414
415
}
415
416
416
417
/**
417
418
* Construct a new {@code HttpHeaders} instance backed by an existing map.
419
+ * @since 5.1
418
420
*/
419
421
public HttpHeaders (MultiValueMap <String , String > headers ) {
420
- Assert .notNull (headers , "headers must not be null" );
422
+ Assert .notNull (headers , "MultiValueMap must not be null" );
421
423
this .headers = headers ;
422
424
}
423
425
@@ -445,7 +447,7 @@ public List<MediaType> getAccept() {
445
447
* @since 5.0
446
448
*/
447
449
public void setAcceptLanguage (List <Locale .LanguageRange > languages ) {
448
- Assert .notNull (languages , "'languages' must not be null" );
450
+ Assert .notNull (languages , "LanguageRange List must not be null" );
449
451
DecimalFormat decimal = new DecimalFormat ("0.0" , DECIMAL_FORMAT_SYMBOLS );
450
452
List <String > values = languages .stream ()
451
453
.map (range ->
@@ -555,7 +557,7 @@ public List<HttpMethod> getAccessControlAllowMethods() {
555
557
* Set the (new) value of the {@code Access-Control-Allow-Origin} response header.
556
558
*/
557
559
public void setAccessControlAllowOrigin (@ Nullable String allowedOrigin ) {
558
- set (ACCESS_CONTROL_ALLOW_ORIGIN , allowedOrigin );
560
+ setOrRemove (ACCESS_CONTROL_ALLOW_ORIGIN , allowedOrigin );
559
561
}
560
562
561
563
/**
@@ -614,7 +616,7 @@ public List<String> getAccessControlRequestHeaders() {
614
616
* Set the (new) value of the {@code Access-Control-Request-Method} request header.
615
617
*/
616
618
public void setAccessControlRequestMethod (@ Nullable HttpMethod requestMethod ) {
617
- set (ACCESS_CONTROL_REQUEST_METHOD , (requestMethod != null ? requestMethod .name () : null ));
619
+ setOrRemove (ACCESS_CONTROL_REQUEST_METHOD , (requestMethod != null ? requestMethod .name () : null ));
618
620
}
619
621
620
622
/**
@@ -766,14 +768,14 @@ public void setBearerAuth(String token) {
766
768
* @since 5.0.5
767
769
*/
768
770
public void setCacheControl (CacheControl cacheControl ) {
769
- set (CACHE_CONTROL , cacheControl .getHeaderValue ());
771
+ setOrRemove (CACHE_CONTROL , cacheControl .getHeaderValue ());
770
772
}
771
773
772
774
/**
773
775
* Set the (new) value of the {@code Cache-Control} header.
774
776
*/
775
777
public void setCacheControl (@ Nullable String cacheControl ) {
776
- set (CACHE_CONTROL , cacheControl );
778
+ setOrRemove (CACHE_CONTROL , cacheControl );
777
779
}
778
780
779
781
/**
@@ -817,7 +819,7 @@ public List<String> getConnection() {
817
819
* @see #getContentDisposition()
818
820
*/
819
821
public void setContentDispositionFormData (String name , @ Nullable String filename ) {
820
- Assert .notNull (name , "'name' must not be null" );
822
+ Assert .notNull (name , "Name must not be null" );
821
823
ContentDisposition .Builder disposition = ContentDisposition .builder ("form-data" ).name (name );
822
824
if (filename != null ) {
823
825
disposition .filename (filename );
@@ -860,7 +862,7 @@ public ContentDisposition getContentDisposition() {
860
862
* @since 5.0
861
863
*/
862
864
public void setContentLanguage (@ Nullable Locale locale ) {
863
- set (CONTENT_LANGUAGE , (locale != null ? locale .toLanguageTag () : null ));
865
+ setOrRemove (CONTENT_LANGUAGE , (locale != null ? locale .toLanguageTag () : null ));
864
866
}
865
867
866
868
/**
@@ -904,12 +906,12 @@ public long getContentLength() {
904
906
*/
905
907
public void setContentType (@ Nullable MediaType mediaType ) {
906
908
if (mediaType != null ) {
907
- Assert .isTrue (!mediaType .isWildcardType (), "' Content-Type' cannot contain wildcard type '*'" );
908
- Assert .isTrue (!mediaType .isWildcardSubtype (), "' Content-Type' cannot contain wildcard subtype '*'" );
909
+ Assert .isTrue (!mediaType .isWildcardType (), "Content-Type cannot contain wildcard type '*'" );
910
+ Assert .isTrue (!mediaType .isWildcardSubtype (), "Content-Type cannot contain wildcard subtype '*'" );
909
911
set (CONTENT_TYPE , mediaType .toString ());
910
912
}
911
913
else {
912
- set (CONTENT_TYPE , null );
914
+ remove (CONTENT_TYPE );
913
915
}
914
916
}
915
917
@@ -953,8 +955,11 @@ public void setETag(@Nullable String etag) {
953
955
Assert .isTrue (etag .startsWith ("\" " ) || etag .startsWith ("W/" ),
954
956
"Invalid ETag: does not start with W/ or \" " );
955
957
Assert .isTrue (etag .endsWith ("\" " ), "Invalid ETag: does not end with \" " );
958
+ set (ETAG , etag );
959
+ }
960
+ else {
961
+ remove (ETAG );
956
962
}
957
- set (ETAG , etag );
958
963
}
959
964
960
965
/**
@@ -1012,7 +1017,7 @@ public void setHost(@Nullable InetSocketAddress host) {
1012
1017
set (HOST , value );
1013
1018
}
1014
1019
else {
1015
- set (HOST , null );
1020
+ remove (HOST , null );
1016
1021
}
1017
1022
}
1018
1023
@@ -1160,7 +1165,7 @@ public void setLastModified(Instant lastModified) {
1160
1165
* @since 5.1.4
1161
1166
*/
1162
1167
public void setLastModified (ZonedDateTime lastModified ) {
1163
- setZonedDateTime (LAST_MODIFIED , lastModified .withZoneSameInstant (ZoneId . of ( " GMT" ) ));
1168
+ setZonedDateTime (LAST_MODIFIED , lastModified .withZoneSameInstant (GMT ));
1164
1169
}
1165
1170
1166
1171
/**
@@ -1179,7 +1184,7 @@ public long getLastModified() {
1179
1184
* as specified by the {@code Location} header.
1180
1185
*/
1181
1186
public void setLocation (@ Nullable URI location ) {
1182
- set (LOCATION , (location != null ? location .toASCIIString () : null ));
1187
+ setOrRemove (LOCATION , (location != null ? location .toASCIIString () : null ));
1183
1188
}
1184
1189
1185
1190
/**
@@ -1197,7 +1202,7 @@ public URI getLocation() {
1197
1202
* Set the (new) value of the {@code Origin} header.
1198
1203
*/
1199
1204
public void setOrigin (@ Nullable String origin ) {
1200
- set (ORIGIN , origin );
1205
+ setOrRemove (ORIGIN , origin );
1201
1206
}
1202
1207
1203
1208
/**
@@ -1212,7 +1217,7 @@ public String getOrigin() {
1212
1217
* Set the (new) value of the {@code Pragma} header.
1213
1218
*/
1214
1219
public void setPragma (@ Nullable String pragma ) {
1215
- set (PRAGMA , pragma );
1220
+ setOrRemove (PRAGMA , pragma );
1216
1221
}
1217
1222
1218
1223
/**
@@ -1244,7 +1249,7 @@ public List<HttpRange> getRange() {
1244
1249
* Set the (new) value of the {@code Upgrade} header.
1245
1250
*/
1246
1251
public void setUpgrade (@ Nullable String upgrade ) {
1247
- set (UPGRADE , upgrade );
1252
+ setOrRemove (UPGRADE , upgrade );
1248
1253
}
1249
1254
1250
1255
/**
@@ -1406,10 +1411,7 @@ public List<String> getValuesAsList(String headerName) {
1406
1411
List <String > result = new ArrayList <>();
1407
1412
for (String value : values ) {
1408
1413
if (value != null ) {
1409
- String [] tokens = StringUtils .tokenizeToStringArray (value , "," );
1410
- for (String token : tokens ) {
1411
- result .add (token );
1412
- }
1414
+ Collections .addAll (result , StringUtils .tokenizeToStringArray (value , "," ));
1413
1415
}
1414
1416
}
1415
1417
return result ;
@@ -1468,16 +1470,32 @@ protected String getFieldValues(String headerName) {
1468
1470
*/
1469
1471
protected String toCommaDelimitedString (List <String > headerValues ) {
1470
1472
StringBuilder builder = new StringBuilder ();
1471
- for (Iterator <String > it = headerValues .iterator (); it .hasNext (); ) {
1473
+ for (Iterator <String > it = headerValues .iterator (); it .hasNext ();) {
1472
1474
String val = it .next ();
1473
- builder .append (val );
1474
- if (it .hasNext ()) {
1475
- builder .append (", " );
1475
+ if (val != null ) {
1476
+ builder .append (val );
1477
+ if (it .hasNext ()) {
1478
+ builder .append (", " );
1479
+ }
1476
1480
}
1477
1481
}
1478
1482
return builder .toString ();
1479
1483
}
1480
1484
1485
+ /**
1486
+ * Set the given header value, or remove the header if {@code null}.
1487
+ * @param headerName the header name
1488
+ * @param headerValue the header value, or {@code null} for none
1489
+ */
1490
+ private void setOrRemove (String headerName , @ Nullable String headerValue ) {
1491
+ if (headerValue != null ) {
1492
+ set (headerName , headerValue );
1493
+ }
1494
+ else {
1495
+ remove (headerName );
1496
+ }
1497
+ }
1498
+
1481
1499
1482
1500
// MultiValueMap implementation
1483
1501
0 commit comments