From 90d768bb7f36819893db968bd87daba81abbffb2 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 6 Mar 2018 23:06:04 +0100 Subject: [PATCH] Overloaded convenience setters: setCacheControl(CacheControl), setExpires(ZonedDateTime) Issue: SPR-16562 --- .../org/springframework/http/HttpHeaders.java | 79 +++++++++++-------- .../http/HttpHeadersTests.java | 40 +++++++--- 2 files changed, 76 insertions(+), 43 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java index ff4642a52a6..adfe99afe23 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -48,7 +48,6 @@ import org.springframework.util.LinkedCaseInsensitiveMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; - /** * Represents HTTP request and response headers, mapping string header names to a list of string values. * @@ -472,8 +471,8 @@ public class HttpHeaders implements MultiValueMap, Serializable * {@link #getAcceptLanguageAsLocales()} or if you need to filter based on * a list of supported locales you can pass the returned list to * {@link Locale#filter(List, Collection)}. - * @since 5.0 * @throws IllegalArgumentException if the value cannot be converted to a language range + * @since 5.0 */ public List getAcceptLanguage() { String value = getFirst(ACCEPT_LANGUAGE); @@ -494,8 +493,8 @@ public class HttpHeaders implements MultiValueMap, Serializable * A variant of {@link #getAcceptLanguage()} that converts each * {@link java.util.Locale.LanguageRange} to a {@link Locale}. * @return the locales or an empty list - * @since 5.0 * @throws IllegalArgumentException if the value cannot be converted to a locale + * @since 5.0 */ public List getAcceptLanguageAsLocales() { List ranges = getAcceptLanguage(); @@ -711,6 +710,15 @@ public class HttpHeaders implements MultiValueMap, Serializable } } + /** + * Set a configured {@link CacheControl} instance as the + * new value of the {@code Cache-Control} header. + * @since 5.0.5 + */ + public void setCacheControl(CacheControl cacheControl) { + set(CACHE_CONTROL, cacheControl.getHeaderValue()); + } + /** * Set the (new) value of the {@code Cache-Control} header. */ @@ -907,6 +915,15 @@ public class HttpHeaders implements MultiValueMap, Serializable return getFirst(ETAG); } + /** + * Set the duration after which the message is no longer valid, + * as specified by the {@code Expires} header. + * @since 5.0.5 + */ + public void setExpires(ZonedDateTime expires) { + setZonedDateTime(EXPIRES, expires); + } + /** * Set the date and time at which the message is no longer valid, * as specified by the {@code Expires} header. @@ -932,13 +949,16 @@ public class HttpHeaders implements MultiValueMap, Serializable * Set the (new) value of the {@code Host} header. *

If the given {@linkplain InetSocketAddress#getPort() port} is {@code 0}, * the host header will only contain the - * {@linkplain InetSocketAddress#getHostString() hostname}. + * {@linkplain InetSocketAddress#getHostString() host name}. * @since 5.0 */ public void setHost(@Nullable InetSocketAddress host) { if (host != null) { - String value = (host.getPort() != 0 ? - String.format("%s:%d", host.getHostString(), host.getPort()) : host.getHostString()); + String value = host.getHostString(); + int port = host.getPort(); + if (port != 0) { + value = value + ":" + port; + } set(HOST, value); } else { @@ -958,28 +978,25 @@ public class HttpHeaders implements MultiValueMap, Serializable if (value == null) { return null; } - final int idx; - if (value.startsWith("[")) { - idx = value.indexOf(':', value.indexOf(']')); - } else { - idx = value.lastIndexOf(':'); - } - String hostname = null; + + String host = null; int port = 0; - if (idx != -1 && idx < value.length() - 1) { - hostname = value.substring(0, idx); - String portString = value.substring(idx + 1); + int separator = (value.startsWith("[") ? value.indexOf(':', value.indexOf(']')) : value.lastIndexOf(':')); + if (separator != -1) { + host = value.substring(0, separator); + String portString = value.substring(separator + 1); try { port = Integer.parseInt(portString); } catch (NumberFormatException ex) { - // ignored + // ignore } } - if (hostname == null) { - hostname = value; + + if (host == null) { + host = value; } - return InetSocketAddress.createUnresolved(hostname, port); + return InetSocketAddress.createUnresolved(host, port); } /** @@ -1188,6 +1205,16 @@ public class HttpHeaders implements MultiValueMap, Serializable return getValuesAsList(VARY); } + /** + * Set the given date under the given header name after formatting it as a string + * using the RFC-1123 date-time formatter. The equivalent of + * {@link #set(String, String)} but for date headers. + * @since 5.0 + */ + public void setZonedDateTime(String headerName, ZonedDateTime date) { + set(headerName, DATE_FORMATTERS[0].format(date)); + } + /** * Set the given date under the given header name after formatting it as a string * using the RFC-1123 date-time formatter. The equivalent of @@ -1201,16 +1228,6 @@ public class HttpHeaders implements MultiValueMap, Serializable set(headerName, DATE_FORMATTERS[0].format(zonedDateTime)); } - /** - * Set the given date under the given header name after formatting it as a string - * using the RFC-1123 date-time formatter. The equivalent of - * {@link #set(String, String)} but for date headers. - * @since 5.0 - */ - public void setZonedDateTime(String headerName, ZonedDateTime date) { - set(headerName, DATE_FORMATTERS[0].format(date)); - } - /** * Parse the first header value for the given header name as a date, * return -1 if there is no value, or raise {@link IllegalArgumentException} @@ -1549,7 +1566,7 @@ public class HttpHeaders implements MultiValueMap, Serializable * Return a {@code HttpHeaders} object that can only be read, not written to. */ public static HttpHeaders readOnlyHttpHeaders(HttpHeaders headers) { - return headers.readOnly ? headers : new HttpHeaders(headers, true); + return (headers.readOnly ? headers : new HttpHeaders(headers, true)); } } diff --git a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java index 724de47e216..763967f24fd 100644 --- a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java +++ b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.time.DateTimeException; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.ArrayList; @@ -37,13 +38,8 @@ import org.hamcrest.Matchers; import org.junit.Test; import static java.time.format.DateTimeFormatter.*; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; /** * Unit tests for {@link org.springframework.http.HttpHeaders}. @@ -51,6 +47,7 @@ import static org.junit.Assert.assertTrue; * @author Arjen Poutsma * @author Sebastien Deleuze * @author Brian Clozel + * @author Juergen Hoeller */ public class HttpHeadersTests { @@ -284,7 +281,7 @@ public class HttpHeadersTests { } @Test - public void expires() { + public void expiresLong() { Calendar calendar = new GregorianCalendar(2008, 11, 18, 11, 20); calendar.setTimeZone(TimeZone.getTimeZone("CET")); long date = calendar.getTimeInMillis(); @@ -293,6 +290,19 @@ public class HttpHeadersTests { assertEquals("Invalid Expires header", "Thu, 18 Dec 2008 10:20:00 GMT", headers.getFirst("expires")); } + @Test + public void expiresZonedDateTime() { + ZonedDateTime zonedDateTime = ZonedDateTime.of(2008, 12, 18, 10, 20, 0, 0, ZoneId.of("GMT")); + headers.setExpires(zonedDateTime); + assertEquals("Invalid Expires header", zonedDateTime.toInstant().toEpochMilli(), headers.getExpires()); + assertEquals("Invalid Expires header", "Thu, 18 Dec 2008 10:20:00 GMT", headers.getFirst("expires")); + } + + @Test(expected = DateTimeException.class) // SPR-16560 + public void expiresLargeDate() { + headers.setExpires(Long.MAX_VALUE); + } + @Test // SPR-10648 (example is from INT-3063) public void expiresInvalidDate() { headers.set("Expires", "-1"); @@ -332,9 +342,15 @@ public class HttpHeadersTests { @Test public void cacheControl() { - String cacheControl = "no-cache"; - headers.setCacheControl(cacheControl); - assertEquals("Invalid Cache-Control header", cacheControl, headers.getCacheControl()); + headers.setCacheControl("no-cache"); + assertEquals("Invalid Cache-Control header", "no-cache", headers.getCacheControl()); + assertEquals("Invalid Cache-Control header", "no-cache", headers.getFirst("cache-control")); + } + + @Test + public void cacheControlBuilder() { + headers.setCacheControl(CacheControl.noCache()); + assertEquals("Invalid Cache-Control header", "no-cache", headers.getCacheControl()); assertEquals("Invalid Cache-Control header", "no-cache", headers.getFirst("cache-control")); }