Improve multi-valued HTTP headers support

Prior to this change, getting header values with `HttpHeaders` when
headers are multi-valued would cause issues.
For example, for a given HTTP message with headers:
    Cache-Control: public, s-maxage=50
    Cache-Control: max-age=42

Getting a `List` of all values would return <"public", "s-maxage=50">
and getting the header value would return "public, s-maxage=50".

This commit takes now into account multi-valued HTTP headers and adds
new getters/setters for "If-Match" and "If-Unmodified-Since" headers.

Note that for ETag-related headers such as "If-Match" and
"If-None-Match", a special parser has been implemented since ETag values
can contain separator characters.

Issue: SPR-14223, SPR-14228
This commit is contained in:
Brian Clozel
2016-04-28 17:59:34 +02:00
parent 15138ed96f
commit 55dae618a6
3 changed files with 189 additions and 33 deletions

View File

@@ -32,6 +32,7 @@ import java.util.TimeZone;
import org.hamcrest.Matchers;
import org.junit.Test;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.*;
/**
@@ -39,12 +40,20 @@ import static org.junit.Assert.*;
*
* @author Arjen Poutsma
* @author Sebastien Deleuze
* @author Brian Clozel
*/
public class HttpHeadersTests {
private final HttpHeaders headers = new HttpHeaders();
@Test
public void getFirst() {
headers.add(HttpHeaders.CACHE_CONTROL, "max-age=1000, public");
headers.add(HttpHeaders.CACHE_CONTROL, "s-maxage=1000");
assertThat(headers.getFirst(HttpHeaders.CACHE_CONTROL), is("max-age=1000, public"));
}
@Test
public void accept() {
MediaType mediaType1 = new MediaType("text", "html");
@@ -132,6 +141,29 @@ public class HttpHeadersTests {
assertEquals("Invalid ETag header", "\"v2.6\"", headers.getFirst("ETag"));
}
@Test
public void ifMatch() {
String ifMatch = "\"v2.6\"";
headers.setIfMatch(ifMatch);
assertEquals("Invalid If-Match header", ifMatch, headers.getIfMatch().get(0));
assertEquals("Invalid If-Match header", "\"v2.6\"", headers.getFirst("If-Match"));
}
@Test(expected = IllegalArgumentException.class)
public void ifMatchIllegalHeader() {
headers.setIfMatch("Illegal");
headers.getIfMatch();
}
@Test
public void ifMatchMultipleHeaders() {
headers.add(HttpHeaders.IF_MATCH, "\"v2,0\"");
headers.add(HttpHeaders.IF_MATCH, "W/\"v2,1\", \"v2,2\"");
assertEquals("Invalid If-Match header", "\"v2,0\"", headers.get(HttpHeaders.IF_MATCH).get(0));
assertEquals("Invalid If-Match header", "W/\"v2,1\", \"v2,2\"", headers.get(HttpHeaders.IF_MATCH).get(1));
assertThat(headers.getIfMatch(), Matchers.contains("\"v2,0\"", "W/\"v2,1\"", "\"v2,2\""));
}
@Test
public void ifNoneMatch() {
String ifNoneMatch = "\"v2.6\"";
@@ -140,16 +172,24 @@ public class HttpHeadersTests {
assertEquals("Invalid If-None-Match header", "\"v2.6\"", headers.getFirst("If-None-Match"));
}
@Test
public void ifNoneMatchWildCard() {
String ifNoneMatch = "*";
headers.setIfNoneMatch(ifNoneMatch);
assertEquals("Invalid If-None-Match header", ifNoneMatch, headers.getIfNoneMatch().get(0));
assertEquals("Invalid If-None-Match header", "*", headers.getFirst("If-None-Match"));
}
@Test
public void ifNoneMatchList() {
String ifNoneMatch1 = "\"v2.6\"";
String ifNoneMatch2 = "\"v2.7\"";
String ifNoneMatch2 = "\"v2.7\", \"v2.8\"";
List<String> ifNoneMatchList = new ArrayList<String>(2);
ifNoneMatchList.add(ifNoneMatch1);
ifNoneMatchList.add(ifNoneMatch2);
headers.setIfNoneMatch(ifNoneMatchList);
assertEquals("Invalid If-None-Match header", ifNoneMatchList, headers.getIfNoneMatch());
assertEquals("Invalid If-None-Match header", "\"v2.6\", \"v2.7\"", headers.getFirst("If-None-Match"));
assertThat(headers.getIfNoneMatch(), Matchers.contains("\"v2.6\"", "\"v2.7\"", "\"v2.8\""));
assertEquals("Invalid If-None-Match header", "\"v2.6\", \"v2.7\", \"v2.8\"", headers.getFirst("If-None-Match"));
}
@Test
@@ -255,6 +295,13 @@ public class HttpHeadersTests {
assertEquals("Invalid Cache-Control header", "no-cache", headers.getFirst("cache-control"));
}
@Test
public void cacheControlAllValues() {
headers.add(HttpHeaders.CACHE_CONTROL, "max-age=1000, public");
headers.add(HttpHeaders.CACHE_CONTROL, "s-maxage=1000");
assertThat(headers.getCacheControl(), is("max-age=1000, public, s-maxage=1000"));
}
@Test
public void contentDisposition() {
headers.setContentDispositionFormData("name", null);
@@ -290,6 +337,16 @@ public class HttpHeadersTests {
assertEquals(allowedHeaders, Arrays.asList("header1", "header2"));
}
@Test
public void accessControlAllowHeadersMultipleValues() {
List<String> allowedHeaders = headers.getAccessControlAllowHeaders();
assertThat(allowedHeaders, Matchers.emptyCollectionOf(String.class));
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "header1, header2");
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "header3");
allowedHeaders = headers.getAccessControlAllowHeaders();
assertEquals(Arrays.asList("header1", "header2", "header3"), allowedHeaders);
}
@Test
public void accessControlAllowMethods() {
List<HttpMethod> allowedMethods = headers.getAccessControlAllowMethods();