Added more headers.
This commit is contained in:
parent
ff1dac8381
commit
c9f4de3ebf
|
|
@ -18,9 +18,12 @@ package org.springframework.http;
|
|||
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
|
|
@ -28,6 +31,7 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
|
|
@ -51,24 +55,43 @@ import org.springframework.util.StringUtils;
|
|||
*/
|
||||
public class HttpHeaders implements MultiValueMap<String, String> {
|
||||
|
||||
private static String ACCEPT = "Accept";
|
||||
private static final String ACCEPT = "Accept";
|
||||
|
||||
private static String ACCEPT_CHARSET = "Accept-Charset";
|
||||
private static final String ACCEPT_CHARSET = "Accept-Charset";
|
||||
|
||||
private static String ALLOW = "Allow";
|
||||
private static final String ALLOW = "Allow";
|
||||
|
||||
private static String CONTENT_LENGTH = "Content-Length";
|
||||
private static final String CONTENT_LENGTH = "Content-Length";
|
||||
|
||||
private static String CONTENT_TYPE = "Content-Type";
|
||||
private static final String CONTENT_TYPE = "Content-Type";
|
||||
|
||||
private static String LOCATION = "Location";
|
||||
private static final String DATE = "Date";
|
||||
|
||||
private static final String ETAG = "ETag";
|
||||
|
||||
private static final String EXPIRES = "Expires";
|
||||
|
||||
private static final String IF_NONE_MATCH = "If-None-Match";
|
||||
|
||||
private static final String LAST_MODIFIED = "Last-Modified";
|
||||
|
||||
private static final String LOCATION = "Location";
|
||||
|
||||
|
||||
private static final String[] DATE_FORMATS = new String[] {
|
||||
"EEE, dd MMM yyyy HH:mm:ss zzz",
|
||||
"EEE, dd-MMM-yy HH:mm:ss zzz",
|
||||
"EEE MMM dd HH:mm:ss yyyy"
|
||||
};
|
||||
|
||||
private static TimeZone GMT = TimeZone.getTimeZone("GMT");
|
||||
|
||||
|
||||
private final Map<String, List<String>> headers = new LinkedCaseInsensitiveMap<List<String>>(8);
|
||||
|
||||
|
||||
/**
|
||||
* Set the list of acceptable {@linkplain MediaType media types}, as specified by the <code>Accept</code> header.
|
||||
* Set the list of acceptable {@linkplain MediaType media types}, as specified by the {@code Accept} header.
|
||||
* @param acceptableMediaTypes the acceptable media types
|
||||
*/
|
||||
public void setAccept(List<MediaType> acceptableMediaTypes) {
|
||||
|
|
@ -76,7 +99,7 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the list of acceptable {@linkplain MediaType media types}, as specified by the <code>Accept</code> header.
|
||||
* Return the list of acceptable {@linkplain MediaType media types}, as specified by the {@code Accept} header.
|
||||
* <p>Returns an empty list when the acceptable media types are unspecified.
|
||||
* @return the acceptable media types
|
||||
*/
|
||||
|
|
@ -86,7 +109,7 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the list of acceptable {@linkplain Charset charsets}, as specified by the <code>Accept-Charset</code> header.
|
||||
* Set the list of acceptable {@linkplain Charset charsets}, as specified by the {@code Accept-Charset} header.
|
||||
* @param acceptableCharsets the acceptable charsets
|
||||
*/
|
||||
public void setAcceptCharset(List<Charset> acceptableCharsets) {
|
||||
|
|
@ -102,7 +125,7 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the list of acceptable {@linkplain Charset charsets}, as specified by the <code>Accept-Charset</code>
|
||||
* Return the list of acceptable {@linkplain Charset charsets}, as specified by the {@code Accept-Charset}
|
||||
* header.
|
||||
* @return the acceptable charsets
|
||||
*/
|
||||
|
|
@ -125,7 +148,7 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the set of allowed {@link HttpMethod HTTP methods}, as specified by the <code>Allow</code> header.
|
||||
* Set the set of allowed {@link HttpMethod HTTP methods}, as specified by the {@code Allow} header.
|
||||
* @param allowedMethods the allowed methods
|
||||
*/
|
||||
public void setAllow(Set<HttpMethod> allowedMethods) {
|
||||
|
|
@ -133,7 +156,7 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the set of allowed {@link HttpMethod HTTP methods}, as specified by the <code>Allow</code> header.
|
||||
* Return the set of allowed {@link HttpMethod HTTP methods}, as specified by the {@code Allow} header.
|
||||
* <p>Returns an empty set when the allowed methods are unspecified.
|
||||
* @return the allowed methods
|
||||
*/
|
||||
|
|
@ -153,7 +176,7 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the length of the body in bytes, as specified by the <code>Content-Length</code> header.
|
||||
* Set the length of the body in bytes, as specified by the {@code Content-Length} header.
|
||||
* @param contentLength the content length
|
||||
*/
|
||||
public void setContentLength(long contentLength) {
|
||||
|
|
@ -161,7 +184,7 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the length of the body in bytes, as specified by the <code>Content-Length</code> header.
|
||||
* Return the length of the body in bytes, as specified by the {@code Content-Length} header.
|
||||
* <p>Returns -1 when the content-length is unknown.
|
||||
* @return the content length
|
||||
*/
|
||||
|
|
@ -171,7 +194,7 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the {@linkplain MediaType media type} of the body, as specified by the <code>Content-Type</code> header.
|
||||
* Set the {@linkplain MediaType media type} of the body, as specified by the {@code Content-Type} header.
|
||||
* @param mediaType the media type
|
||||
*/
|
||||
public void setContentType(MediaType mediaType) {
|
||||
|
|
@ -181,8 +204,8 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the {@linkplain MediaType media type} of the body, as specified by the <code>Content-Type</code> header.
|
||||
* <p>Returns <code>null</code> when the content-type is unknown.
|
||||
* Return the {@linkplain MediaType media type} of the body, as specified by the {@code Content-Type} header.
|
||||
* <p>Returns {@code null} when the content-type is unknown.
|
||||
* @return the content type
|
||||
*/
|
||||
public MediaType getContentType() {
|
||||
|
|
@ -191,7 +214,119 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the (new) location of a resource, as specified by the <code>Location</code> header.
|
||||
* Sets the date and time at which the message was created, as specified by the {@code Date} header.
|
||||
* <p>The date should be specified as the number of milliseconds since January 1, 1970 GMT.
|
||||
* @param date the date
|
||||
*/
|
||||
public void setDate(long date) {
|
||||
setDate(DATE, date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date and time at which the message was created, as specified by the {@code Date} header.
|
||||
* <p>The date is returned as the number of milliseconds since January 1, 1970 GMT. Returns -1 when the date is unknown.
|
||||
* @return the creation date/time
|
||||
* @throws IllegalArgumentException if the value can't be converted to a date
|
||||
*/
|
||||
public long getDate() {
|
||||
return getFirstDate(DATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the (new) entity tag of the body, as specified by the {@code ETag} header.
|
||||
* @param eTag the new entity tag
|
||||
*/
|
||||
public void setETag(String eTag) {
|
||||
set(ETAG, quote(eTag));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity tag of the body, as specified by the {@code ETag} header.
|
||||
* @return the entity tag
|
||||
*/
|
||||
public String getETag() {
|
||||
return unquote(getFirst(ETAG));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date and time at which the message is no longer valid, as specified by the {@code Expires} header.
|
||||
* <p>The date should be specified as the number of milliseconds since January 1, 1970 GMT.
|
||||
* @param expires the new expires header value
|
||||
*/
|
||||
public void setExpires(long expires) {
|
||||
setDate(EXPIRES, expires);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date and time at which the message is no longer valid, as specified by the {@code Expires} header.
|
||||
* <p>The date is returned as the number of milliseconds since January 1, 1970 GMT. Returns -1 when the date is unknown.
|
||||
* @return the expires value
|
||||
*/
|
||||
public long getExpires() {
|
||||
return getFirstDate(EXPIRES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the (new) value of the {@code If-None-Match} header.
|
||||
* @param ifNoneMatch the new value of the header
|
||||
*/
|
||||
public void setIfNoneMatch(String ifNoneMatch) {
|
||||
set(IF_NONE_MATCH, quote(ifNoneMatch));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the (new) values of the {@code If-None-Match} header.
|
||||
* @param ifNoneMatchList the new value of the header
|
||||
*/
|
||||
public void setIfNoneMatch(List<String> ifNoneMatchList) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (Iterator<String> iterator = ifNoneMatchList.iterator(); iterator.hasNext();) {
|
||||
String ifNoneMatch = iterator.next();
|
||||
builder.append(quote(ifNoneMatch));
|
||||
if (iterator.hasNext()) {
|
||||
builder.append(", ");
|
||||
}
|
||||
}
|
||||
set(IF_NONE_MATCH, builder.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the {@code If-None-Match} header.
|
||||
* @return the header value
|
||||
*/
|
||||
public List<String> getIfNoneMatch() {
|
||||
List<String> result = new ArrayList<String>();
|
||||
|
||||
String value = getFirst(IF_NONE_MATCH);
|
||||
if (value != null) {
|
||||
String[] tokens = value.split(",\\s*");
|
||||
for (String token : tokens) {
|
||||
result.add(unquote(token));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time the resource was last changed, as specified by the {@code Last-Modified} header.
|
||||
* <p>The date should be specified as the number of milliseconds since January 1, 1970 GMT.
|
||||
* @param lastModified the last modified date
|
||||
*/
|
||||
public void setLastModified(long lastModified) {
|
||||
setDate(LAST_MODIFIED, lastModified);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time the resource was last changed, as specified by the {@code Last-Modified} header.
|
||||
* <p>The date is returned as the number of milliseconds since January 1, 1970 GMT. Returns -1 when the date is unknown.
|
||||
* @return the last modified date
|
||||
*/
|
||||
public long getLastModified() {
|
||||
return getFirstDate(LAST_MODIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the (new) location of a resource, as specified by the {@code Location} header.
|
||||
* @param location the location
|
||||
*/
|
||||
public void setLocation(URI location) {
|
||||
|
|
@ -199,8 +334,8 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the (new) location of a resource, as specified by the <code>Location</code> header.
|
||||
* <p>Returns <code>null</code> when the location is unknown.
|
||||
* Return the (new) location of a resource, as specified by the {@code Location} header.
|
||||
* <p>Returns {@code null} when the location is unknown.
|
||||
* @return the location
|
||||
*/
|
||||
public URI getLocation() {
|
||||
|
|
@ -208,13 +343,64 @@ public class HttpHeaders implements MultiValueMap<String, String> {
|
|||
return (value != null ? URI.create(value) : null);
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
|
||||
private String quote(String s) {
|
||||
Assert.notNull(s);
|
||||
if (!s.startsWith("\"")) {
|
||||
s = "\"" + s;
|
||||
}
|
||||
if (!s.endsWith("\"")) {
|
||||
s = s + "\"";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
private String unquote(String s) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
if (s.startsWith("\"")) {
|
||||
s = s.substring(1);
|
||||
}
|
||||
if (s.endsWith("\"")) {
|
||||
s = s.substring(0, s.length() - 1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
private long getFirstDate(String headerName) {
|
||||
String headerValue = getFirst(headerName);
|
||||
if (headerValue == null) {
|
||||
return -1;
|
||||
}
|
||||
for (String dateFormat : DATE_FORMATS) {
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US);
|
||||
simpleDateFormat.setTimeZone(GMT);
|
||||
try {
|
||||
return simpleDateFormat.parse(headerValue).getTime();
|
||||
}
|
||||
catch (ParseException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Cannot parse date value \"" + headerValue +
|
||||
"\" for \"" + headerName + "\" header");
|
||||
}
|
||||
|
||||
private void setDate(String headerName, long date) {
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMATS[0]);
|
||||
dateFormat.setTimeZone(GMT);
|
||||
set(headerName, dateFormat.format(new Date(date)));
|
||||
}
|
||||
|
||||
// Single string methods
|
||||
|
||||
/**
|
||||
* Return the first header value for the given header name, if any.
|
||||
* @param headerName the header name
|
||||
* @return the first header value; or <code>null</code>
|
||||
* @return the first header value; or {@code null}
|
||||
*/
|
||||
public String getFirst(String headerName) {
|
||||
List<String> headerValues = headers.get(headerName);
|
||||
|
|
|
|||
|
|
@ -20,15 +20,16 @@ import java.net.URI;
|
|||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.EnumSet;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
|
|
@ -96,4 +97,74 @@ public class HttpHeadersTests {
|
|||
assertEquals("Invalid Location header", location, headers.getLocation());
|
||||
assertEquals("Invalid Location header", "http://www.example.com/hotels", headers.getFirst("Location"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eTag() {
|
||||
String eTag = "v2.6";
|
||||
headers.setETag(eTag);
|
||||
assertEquals("Invalid ETag header", eTag, headers.getETag());
|
||||
assertEquals("Invalid ETag header", "\"v2.6\"", headers.getFirst("ETag"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ifNoneMatch() {
|
||||
String ifNoneMatch = "v2.6";
|
||||
headers.setIfNoneMatch(ifNoneMatch);
|
||||
assertEquals("Invalid If-None-Match header", ifNoneMatch, headers.getIfNoneMatch().get(0));
|
||||
assertEquals("Invalid If-None-Match header", "\"v2.6\"", headers.getFirst("If-None-Match"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ifNoneMatchList() {
|
||||
String ifNoneMatch1 = "v2.6";
|
||||
String ifNoneMatch2 = "v2.7";
|
||||
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"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void date() {
|
||||
Calendar calendar = new GregorianCalendar(2008, 11, 18, 11, 20);
|
||||
calendar.setTimeZone(TimeZone.getTimeZone("CET"));
|
||||
long date = calendar.getTimeInMillis();
|
||||
headers.setDate(date);
|
||||
assertEquals("Invalid Date header", date, headers.getDate());
|
||||
assertEquals("Invalid Date header", "Thu, 18 Dec 2008 10:20:00 GMT", headers.getFirst("date"));
|
||||
|
||||
// RFC 850
|
||||
headers.set("Date", "Thursday, 18-Dec-08 11:20:00 CET");
|
||||
assertEquals("Invalid Date header", date, headers.getDate());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void dateInvalid() {
|
||||
headers.set("Date", "Foo Bar Baz");
|
||||
headers.getDate();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lastModified() {
|
||||
Calendar calendar = new GregorianCalendar(2008, 11, 18, 11, 20);
|
||||
calendar.setTimeZone(TimeZone.getTimeZone("CET"));
|
||||
long date = calendar.getTimeInMillis();
|
||||
headers.setLastModified(date);
|
||||
assertEquals("Invalid Last-Modified header", date, headers.getLastModified());
|
||||
assertEquals("Invalid Last-Modified header", "Thu, 18 Dec 2008 10:20:00 GMT", headers.getFirst("last-modified"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void expires() {
|
||||
Calendar calendar = new GregorianCalendar(2008, 11, 18, 11, 20);
|
||||
calendar.setTimeZone(TimeZone.getTimeZone("CET"));
|
||||
long date = calendar.getTimeInMillis();
|
||||
headers.setExpires(date);
|
||||
assertEquals("Invalid Expires header", date, headers.getExpires());
|
||||
assertEquals("Invalid Expires header", "Thu, 18 Dec 2008 10:20:00 GMT", headers.getFirst("expires"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue