@@ -75,11 +75,7 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
7575
7676 private static final long serialVersionUID = -8578554704772377436L ;
7777
78- /**
79- * The empty {@code HttpHeaders} instance (immutable).
80- */
81- public static final HttpHeaders EMPTY =
82- new ReadOnlyHttpHeaders (new HttpHeaders (new LinkedMultiValueMap <>(0 )));
78+
8379 /**
8480 * The HTTP {@code Accept} header field name.
8581 * @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
381377 */
382378 public static final String WWW_AUTHENTICATE = "WWW-Authenticate" ;
383379
380+
381+ /**
382+ * The empty {@code HttpHeaders} instance (immutable).
383+ */
384+ public static final HttpHeaders EMPTY = new ReadOnlyHttpHeaders (new HttpHeaders (new LinkedMultiValueMap <>(0 )));
385+
384386 /**
385387 * Pattern matching ETag multiple field values in headers such as "If-Match", "If-None-Match".
386388 * @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
409411 * Construct a new, empty instance of the {@code HttpHeaders} object.
410412 */
411413 public HttpHeaders () {
412- this (CollectionUtils .toMultiValueMap (
413- new LinkedCaseInsensitiveMap <>(8 , Locale .ENGLISH )));
414+ this (CollectionUtils .toMultiValueMap (new LinkedCaseInsensitiveMap <>(8 , Locale .ENGLISH )));
414415 }
415416
416417 /**
417418 * Construct a new {@code HttpHeaders} instance backed by an existing map.
419+ * @since 5.1
418420 */
419421 public HttpHeaders (MultiValueMap <String , String > headers ) {
420- Assert .notNull (headers , "headers must not be null" );
422+ Assert .notNull (headers , "MultiValueMap must not be null" );
421423 this .headers = headers ;
422424 }
423425
@@ -445,7 +447,7 @@ public List<MediaType> getAccept() {
445447 * @since 5.0
446448 */
447449 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" );
449451 DecimalFormat decimal = new DecimalFormat ("0.0" , DECIMAL_FORMAT_SYMBOLS );
450452 List <String > values = languages .stream ()
451453 .map (range ->
@@ -555,7 +557,7 @@ public List<HttpMethod> getAccessControlAllowMethods() {
555557 * Set the (new) value of the {@code Access-Control-Allow-Origin} response header.
556558 */
557559 public void setAccessControlAllowOrigin (@ Nullable String allowedOrigin ) {
558- set (ACCESS_CONTROL_ALLOW_ORIGIN , allowedOrigin );
560+ setOrRemove (ACCESS_CONTROL_ALLOW_ORIGIN , allowedOrigin );
559561 }
560562
561563 /**
@@ -614,7 +616,7 @@ public List<String> getAccessControlRequestHeaders() {
614616 * Set the (new) value of the {@code Access-Control-Request-Method} request header.
615617 */
616618 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 ));
618620 }
619621
620622 /**
@@ -766,14 +768,14 @@ public void setBearerAuth(String token) {
766768 * @since 5.0.5
767769 */
768770 public void setCacheControl (CacheControl cacheControl ) {
769- set (CACHE_CONTROL , cacheControl .getHeaderValue ());
771+ setOrRemove (CACHE_CONTROL , cacheControl .getHeaderValue ());
770772 }
771773
772774 /**
773775 * Set the (new) value of the {@code Cache-Control} header.
774776 */
775777 public void setCacheControl (@ Nullable String cacheControl ) {
776- set (CACHE_CONTROL , cacheControl );
778+ setOrRemove (CACHE_CONTROL , cacheControl );
777779 }
778780
779781 /**
@@ -817,7 +819,7 @@ public List<String> getConnection() {
817819 * @see #getContentDisposition()
818820 */
819821 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" );
821823 ContentDisposition .Builder disposition = ContentDisposition .builder ("form-data" ).name (name );
822824 if (filename != null ) {
823825 disposition .filename (filename );
@@ -860,7 +862,7 @@ public ContentDisposition getContentDisposition() {
860862 * @since 5.0
861863 */
862864 public void setContentLanguage (@ Nullable Locale locale ) {
863- set (CONTENT_LANGUAGE , (locale != null ? locale .toLanguageTag () : null ));
865+ setOrRemove (CONTENT_LANGUAGE , (locale != null ? locale .toLanguageTag () : null ));
864866 }
865867
866868 /**
@@ -904,12 +906,12 @@ public long getContentLength() {
904906 */
905907 public void setContentType (@ Nullable MediaType mediaType ) {
906908 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 '*'" );
909911 set (CONTENT_TYPE , mediaType .toString ());
910912 }
911913 else {
912- set (CONTENT_TYPE , null );
914+ remove (CONTENT_TYPE );
913915 }
914916 }
915917
@@ -953,8 +955,11 @@ public void setETag(@Nullable String etag) {
953955 Assert .isTrue (etag .startsWith ("\" " ) || etag .startsWith ("W/" ),
954956 "Invalid ETag: does not start with W/ or \" " );
955957 Assert .isTrue (etag .endsWith ("\" " ), "Invalid ETag: does not end with \" " );
958+ set (ETAG , etag );
959+ }
960+ else {
961+ remove (ETAG );
956962 }
957- set (ETAG , etag );
958963 }
959964
960965 /**
@@ -1012,7 +1017,7 @@ public void setHost(@Nullable InetSocketAddress host) {
10121017 set (HOST , value );
10131018 }
10141019 else {
1015- set (HOST , null );
1020+ remove (HOST , null );
10161021 }
10171022 }
10181023
@@ -1160,7 +1165,7 @@ public void setLastModified(Instant lastModified) {
11601165 * @since 5.1.4
11611166 */
11621167 public void setLastModified (ZonedDateTime lastModified ) {
1163- setZonedDateTime (LAST_MODIFIED , lastModified .withZoneSameInstant (ZoneId . of ( " GMT" ) ));
1168+ setZonedDateTime (LAST_MODIFIED , lastModified .withZoneSameInstant (GMT ));
11641169 }
11651170
11661171 /**
@@ -1179,7 +1184,7 @@ public long getLastModified() {
11791184 * as specified by the {@code Location} header.
11801185 */
11811186 public void setLocation (@ Nullable URI location ) {
1182- set (LOCATION , (location != null ? location .toASCIIString () : null ));
1187+ setOrRemove (LOCATION , (location != null ? location .toASCIIString () : null ));
11831188 }
11841189
11851190 /**
@@ -1197,7 +1202,7 @@ public URI getLocation() {
11971202 * Set the (new) value of the {@code Origin} header.
11981203 */
11991204 public void setOrigin (@ Nullable String origin ) {
1200- set (ORIGIN , origin );
1205+ setOrRemove (ORIGIN , origin );
12011206 }
12021207
12031208 /**
@@ -1212,7 +1217,7 @@ public String getOrigin() {
12121217 * Set the (new) value of the {@code Pragma} header.
12131218 */
12141219 public void setPragma (@ Nullable String pragma ) {
1215- set (PRAGMA , pragma );
1220+ setOrRemove (PRAGMA , pragma );
12161221 }
12171222
12181223 /**
@@ -1244,7 +1249,7 @@ public List<HttpRange> getRange() {
12441249 * Set the (new) value of the {@code Upgrade} header.
12451250 */
12461251 public void setUpgrade (@ Nullable String upgrade ) {
1247- set (UPGRADE , upgrade );
1252+ setOrRemove (UPGRADE , upgrade );
12481253 }
12491254
12501255 /**
@@ -1406,10 +1411,7 @@ public List<String> getValuesAsList(String headerName) {
14061411 List <String > result = new ArrayList <>();
14071412 for (String value : values ) {
14081413 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 , "," ));
14131415 }
14141416 }
14151417 return result ;
@@ -1468,16 +1470,32 @@ protected String getFieldValues(String headerName) {
14681470 */
14691471 protected String toCommaDelimitedString (List <String > headerValues ) {
14701472 StringBuilder builder = new StringBuilder ();
1471- for (Iterator <String > it = headerValues .iterator (); it .hasNext (); ) {
1473+ for (Iterator <String > it = headerValues .iterator (); it .hasNext ();) {
14721474 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+ }
14761480 }
14771481 }
14781482 return builder .toString ();
14791483 }
14801484
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+
14811499
14821500 // MultiValueMap implementation
14831501
0 commit comments