/* * Copyright 2002-2014 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. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.http; import java.io.Serializable; 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.LinkedHashMap; import java.util.LinkedList; 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; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** * Represents HTTP request and response headers, mapping string header names to list of string values. * *
In addition to the normal methods defined by {@link Map}, this class offers the following convenience methods: *
Inspired by {@link com.sun.net.httpserver.Headers}.
*
* @author Arjen Poutsma
* @since 3.0
*/
public class HttpHeaders implements MultiValueMap Returns an empty list when the acceptable media types are unspecified.
* @return the acceptable media types
*/
public List Returns an empty set when the allowed methods are unspecified.
* @return the allowed methods
*/
public Set Returns -1 when the content-length is unknown.
* @return the content length
*/
public long getContentLength() {
String value = getFirst(CONTENT_LENGTH);
return (value != null ? Long.parseLong(value) : -1);
}
/**
* 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) {
Assert.isTrue(!mediaType.isWildcardType(), "'Content-Type' cannot contain wildcard type '*'");
Assert.isTrue(!mediaType.isWildcardSubtype(), "'Content-Type' cannot contain wildcard subtype '*'");
set(CONTENT_TYPE, mediaType.toString());
}
/**
* Return the {@linkplain MediaType media type} of the body, as specified by the {@code Content-Type} header.
* Returns {@code null} when the content-type is unknown.
* @return the content type
*/
public MediaType getContentType() {
String value = getFirst(CONTENT_TYPE);
return (value != null ? MediaType.parseMediaType(value) : null);
}
/**
* Sets the date and time at which the message was created, as specified by the {@code Date} header.
* 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.
* 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) {
if (eTag != null) {
Assert.isTrue(eTag.startsWith("\"") || eTag.startsWith("W/"), "Invalid eTag, does not start with W/ or \"");
Assert.isTrue(eTag.endsWith("\""), "Invalid eTag, does not end with \"");
}
set(ETAG, eTag);
}
/**
* Returns the entity tag of the body, as specified by the {@code ETag} header.
* @return the entity tag
*/
public String getETag() {
return getFirst(ETAG);
}
/**
* Sets the date and time at which the message is no longer valid, as specified by the {@code Expires} header.
* 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.
* 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() {
try {
return getFirstDate(EXPIRES);
}
catch (IllegalArgumentException ex) {
return -1;
}
}
/**
* Sets the (new) value of the {@code If-Modified-Since} header.
* The date should be specified as the number of milliseconds since January 1, 1970 GMT.
* @param ifModifiedSince the new value of the header
*/
public void setIfModifiedSince(long ifModifiedSince) {
setDate(IF_MODIFIED_SINCE, ifModifiedSince);
}
/**
* Returns the value of the {@code IfModifiedSince} header.
* The date is returned as the number of milliseconds since January 1, 1970 GMT. Returns -1 when the date is unknown.
* @return the header value
* @deprecated use {@link #getIfModifiedSince()}
*/
@Deprecated
public long getIfNotModifiedSince() {
return getIfModifiedSince();
}
/**
* Returns the value of the {@code If-Modified-Since} header.
* The date is returned as the number of milliseconds since January 1, 1970 GMT. Returns -1 when the date is unknown.
* @return the header value
*/
public long getIfModifiedSince() {
return getFirstDate(IF_MODIFIED_SINCE);
}
/**
* 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, ifNoneMatch);
}
/**
* Sets the (new) values of the {@code If-None-Match} header.
* @param ifNoneMatchList the new value of the header
*/
public void setIfNoneMatch(List 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.
* 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) {
set(LOCATION, location.toASCIIString());
}
/**
* Return the (new) location of a resource, as specified by the {@code Location} header.
* Returns {@code null} when the location is unknown.
* @return the location
*/
public URI getLocation() {
String value = getFirst(LOCATION);
return (value != null ? URI.create(value) : null);
}
/**
* Sets the (new) value of the {@code Origin} header.
* @param origin the value of the header
*/
public void setOrigin(String origin) {
set(ORIGIN, origin);
}
/**
* Returns the value of the {@code Origin} header.
* @return the value of the header
*/
public String getOrigin() {
return getFirst(ORIGIN);
}
/**
* Sets the (new) value of the {@code Pragma} header.
* @param pragma the value of the header
*/
public void setPragma(String pragma) {
set(PRAGMA, pragma);
}
/**
* Returns the value of the {@code Pragma} header.
* @return the value of the header
*/
public String getPragma() {
return getFirst(PRAGMA);
}
/**
* Sets the (new) value of the {@code Upgrade} header.
* @param upgrade the value of the header
*/
public void setUpgrade(String upgrade) {
set(UPGRADE, upgrade);
}
/**
* Returns the value of the {@code Upgrade} header.
* @return the value of the header
*/
public String getUpgrade() {
return getFirst(UPGRADE);
}
// Date methods
/**
* Parse the first header value for the given header name as a date, return -1 if
* there is no value, or raise {@link IllegalArgumentException} if the value cannot be
* parsed as a date.
*/
public 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");
}
/**
* Set the given date under the given header name after formatting it as a string
* using the pattern {@code "EEE, dd MMM yyyy HH:mm:ss zzz"}. The equivalent of
* {@link #set(String, String)} but for date headers.
*/
public void setDate(String headerName, long date) {
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMATS[0], Locale.US);
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}
*/
@Override
public String getFirst(String headerName) {
List>(headers.size(), Locale.ENGLISH);
for (Entry
>(8, Locale.ENGLISH), false);
}
/**
* Returns {@code HttpHeaders} object that can only be read, not written to.
*/
public static HttpHeaders readOnlyHttpHeaders(HttpHeaders headers) {
return new HttpHeaders(headers, true);
}
/**
* 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
> values() {
return this.headers.values();
}
@Override
public Set