View Javadoc
1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.http;
18  
19  import java.io.Serializable;
20  import java.net.URI;
21  import java.nio.charset.Charset;
22  import java.text.ParseException;
23  import java.text.SimpleDateFormat;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.Date;
28  import java.util.EnumSet;
29  import java.util.Iterator;
30  import java.util.LinkedHashMap;
31  import java.util.LinkedList;
32  import java.util.List;
33  import java.util.Locale;
34  import java.util.Map;
35  import java.util.Set;
36  import java.util.TimeZone;
37  
38  import org.springframework.util.Assert;
39  import org.springframework.util.LinkedCaseInsensitiveMap;
40  import org.springframework.util.MultiValueMap;
41  import org.springframework.util.StringUtils;
42  
43  /**
44   * Represents HTTP request and response headers, mapping string header names to list of string values.
45   *
46   * <p>In addition to the normal methods defined by {@link Map}, this class offers the following
47   * convenience methods:
48   * <ul>
49   * <li>{@link #getFirst(String)} returns the first value associated with a given header name</li>
50   * <li>{@link #add(String, String)} adds a header value to the list of values for a header name</li>
51   * <li>{@link #set(String, String)} sets the header value to a single string value</li>
52   * </ul>
53   *
54   * <p>Inspired by {@link com.sun.net.httpserver.Headers}.
55   *
56   * @author Arjen Poutsma
57   * @author Sebastien Deleuze
58   * @since 3.0
59   */
60  public class HttpHeaders implements MultiValueMap<String, String>, Serializable {
61  
62  	private static final long serialVersionUID = -8578554704772377436L;
63  
64  	/**
65  	 * The HTTP {@code Accept} header field name.
66  	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.3.2">Section 5.3.2 of RFC 7231</a>
67  	 */
68  	public static final String ACCEPT = "Accept";
69  	/**
70  	 * The HTTP {@code Accept-Charset} header field name.
71  	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.3.3">Section 5.3.3 of RFC 7231</a>
72  	 */
73  	public static final String ACCEPT_CHARSET = "Accept-Charset";
74  	/**
75  	 * The HTTP {@code Accept-Encoding} header field name.
76  	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.3.4">Section 5.3.4 of RFC 7231</a>
77  	 */
78  	public static final String ACCEPT_ENCODING = "Accept-Encoding";
79  	/**
80  	 * The HTTP {@code Accept-Language} header field name.
81  	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.3.5">Section 5.3.5 of RFC 7231</a>
82  	 */
83  	public static final String ACCEPT_LANGUAGE = "Accept-Language";
84  	/**
85  	 * The HTTP {@code Accept-Ranges} header field name.
86  	 * @see <a href="http://tools.ietf.org/html/rfc7233#section-2.3">Section 5.3.5 of RFC 7233</a>
87  	 */
88  	public static final String ACCEPT_RANGES = "Accept-Ranges";
89  	/**
90  	 * The HTTP {@code Age} header field name.
91  	 * @see <a href="http://tools.ietf.org/html/rfc7234#section-5.1">Section 5.1 of RFC 7234</a>
92  	 */
93  	public static final String AGE = "Age";
94  	/**
95  	 * The HTTP {@code Allow} header field name.
96  	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-7.4.1">Section 7.4.1 of RFC 7231</a>
97  	 */
98  	public static final String ALLOW = "Allow";
99  	/**
100 	 * The HTTP {@code Authorization} header field name.
101 	 * @see <a href="http://tools.ietf.org/html/rfc7235#section-4.2">Section 4.2 of RFC 7235</a>
102 	 */
103 	public static final String AUTHORIZATION = "Authorization";
104 	/**
105 	 * The HTTP {@code Cache-Control} header field name.
106 	 * @see <a href="http://tools.ietf.org/html/rfc7234#section-5.2">Section 5.2 of RFC 7234</a>
107 	 */
108 	public static final String CACHE_CONTROL = "Cache-Control";
109 	/**
110 	 * The HTTP {@code Connection} header field name.
111 	 * @see <a href="http://tools.ietf.org/html/rfc7230#section-6.1">Section 6.1 of RFC 7230</a>
112 	 */
113 	public static final String CONNECTION = "Connection";
114 	/**
115 	 * The HTTP {@code Content-Encoding} header field name.
116 	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-3.1.2.2">Section 3.1.2.2 of RFC 7231</a>
117 	 */
118 	public static final String CONTENT_ENCODING = "Content-Encoding";
119 	/**
120 	 * The HTTP {@code Content-Disposition} header field name
121 	 * @see <a href="http://tools.ietf.org/html/rfc6266">RFC 6266</a>
122 	 */
123 	public static final String CONTENT_DISPOSITION = "Content-Disposition";
124 	/**
125 	 * The HTTP {@code Content-Language} header field name.
126 	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-3.1.3.2">Section 3.1.3.2 of RFC 7231</a>
127 	 */
128 	public static final String CONTENT_LANGUAGE = "Content-Language";
129 	/**
130 	 * The HTTP {@code Content-Length} header field name.
131 	 * @see <a href="http://tools.ietf.org/html/rfc7230#section-3.3.2">Section 3.3.2 of RFC 7230</a>
132 	 */
133 	public static final String CONTENT_LENGTH = "Content-Length";
134 	/**
135 	 * The HTTP {@code Content-Location} header field name.
136 	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-3.1.4.2">Section 3.1.4.2 of RFC 7231</a>
137 	 */
138 	public static final String CONTENT_LOCATION = "Content-Location";
139 	/**
140 	 * The HTTP {@code Content-Range} header field name.
141 	 * @see <a href="http://tools.ietf.org/html/rfc7233#section-4.2">Section 4.2 of RFC 7233</a>
142 	 */
143 	public static final String CONTENT_RANGE = "Content-Range";
144 	/**
145 	 * The HTTP {@code Content-Type} header field name.
146 	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-3.1.1.5">Section 3.1.1.5 of RFC 7231</a>
147 	 */
148 	public static final String CONTENT_TYPE = "Content-Type";
149 	/**
150 	 * The HTTP {@code Cookie} header field name.
151 	 * @see <a href="http://tools.ietf.org/html/rfc2109#section-4.3.4">Section 4.3.4 of RFC 2109</a>
152 	 */
153 	public static final String COOKIE = "Cookie";
154 	/**
155 	 * The HTTP {@code Date} header field name.
156 	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-7.1.1.2">Section 7.1.1.2 of RFC 7231</a>
157 	 */
158 	public static final String DATE = "Date";
159 	/**
160 	 * The HTTP {@code ETag} header field name.
161 	 * @see <a href="http://tools.ietf.org/html/rfc7232#section-2.3">Section 2.3 of RFC 7232</a>
162 	 */
163 	public static final String ETAG = "ETag";
164 	/**
165 	 * The HTTP {@code Expect} header field name.
166 	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.1.1">Section 5.1.1 of RFC 7231</a>
167 	 */
168 	public static final String EXPECT = "Expect";
169 	/**
170 	 * The HTTP {@code Expires} header field name.
171 	 * @see <a href="http://tools.ietf.org/html/rfc7234#section-5.3">Section 5.3 of RFC 7234</a>
172 	 */
173 	public static final String EXPIRES = "Expires";
174 	/**
175 	 * The HTTP {@code From} header field name.
176 	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.5.1">Section 5.5.1 of RFC 7231</a>
177 	 */
178 	public static final String FROM = "From";
179 	/**
180 	 * The HTTP {@code Host} header field name.
181 	 * @see <a href="http://tools.ietf.org/html/rfc7230#section-5.4">Section 5.4 of RFC 7230</a>
182 	 */
183 	public static final String HOST = "Host";
184 	/**
185 	 * The HTTP {@code If-Match} header field name.
186 	 * @see <a href="http://tools.ietf.org/html/rfc7232#section-3.1">Section 3.1 of RFC 7232</a>
187 	 */
188 	public static final String IF_MATCH = "If-Match";
189 	/**
190 	 * The HTTP {@code If-Modified-Since} header field name.
191 	 * @see <a href="http://tools.ietf.org/html/rfc7232#section-3.3">Section 3.3 of RFC 7232</a>
192 	 */
193 	public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
194 	/**
195 	 * The HTTP {@code If-None-Match} header field name.
196 	 * @see <a href="http://tools.ietf.org/html/rfc7232#section-3.2">Section 3.2 of RFC 7232</a>
197 	 */
198 	public static final String IF_NONE_MATCH = "If-None-Match";
199 	/**
200 	 * The HTTP {@code If-Range} header field name.
201 	 * @see <a href="http://tools.ietf.org/html/rfc7233#section-3.2">Section 3.2 of RFC 7233</a>
202 	 */
203 	public static final String IF_RANGE = "If-Range";
204 	/**
205 	 * The HTTP {@code If-Unmodified-Since} header field name.
206 	 * @see <a href="http://tools.ietf.org/html/rfc7232#section-3.4">Section 3.4 of RFC 7232</a>
207 	 */
208 	public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
209 	/**
210 	 * The HTTP {@code Last-Modified} header field name.
211 	 * @see <a href="http://tools.ietf.org/html/rfc7232#section-2.2">Section 2.2 of RFC 7232</a>
212 	 */
213 	public static final String LAST_MODIFIED = "Last-Modified";
214 	/**
215 	 * The HTTP {@code Link} header field name.
216 	 * @see <a href="http://tools.ietf.org/html/rfc5988">RFC 5988</a>
217 	 */
218 	public static final String LINK = "Link";
219 	/**
220 	 * The HTTP {@code Location} header field name.
221 	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-7.1.2">Section 7.1.2 of RFC 7231</a>
222 	 */
223 	public static final String LOCATION = "Location";
224 	/**
225 	 * The HTTP {@code Max-Forwards} header field name.
226 	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.1.2">Section 5.1.2 of RFC 7231</a>
227 	 */
228 	public static final String MAX_FORWARDS = "Max-Forwards";
229 	/**
230 	 * The HTTP {@code Origin} header field name.
231 	 * @see <a href="http://tools.ietf.org/html/rfc6454">RFC 6454</a>
232 	 */
233 	public static final String ORIGIN = "Origin";
234 	/**
235 	 * The HTTP {@code Pragma} header field name.
236 	 * @see <a href="http://tools.ietf.org/html/rfc7234#section-5.4">Section 5.4 of RFC 7234</a>
237 	 */
238 	public static final String PRAGMA = "Pragma";
239 	/**
240 	 * The HTTP {@code Proxy-Authenticate} header field name.
241 	 * @see <a href="http://tools.ietf.org/html/rfc7235#section-4.3">Section 4.3 of RFC 7235</a>
242 	 */
243 	public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
244 	/**
245 	 * The HTTP {@code Proxy-Authorization} header field name.
246 	 * @see <a href="http://tools.ietf.org/html/rfc7235#section-4.4">Section 4.4 of RFC 7235</a>
247 	 */
248 	public static final String PROXY_AUTHORIZATION = "Proxy-Authorization";
249 	/**
250 	 * The HTTP {@code Range} header field name.
251 	 * @see <a href="http://tools.ietf.org/html/rfc7233#section-3.1">Section 3.1 of RFC 7233</a>
252 	 */
253 	public static final String RANGE = "Range";
254 	/**
255 	 * The HTTP {@code Referer} header field name.
256 	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.5.2">Section 5.5.2 of RFC 7231</a>
257 	 */
258 	public static final String REFERER = "Referer";
259 	/**
260 	 * The HTTP {@code Retry-After} header field name.
261 	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-7.1.3">Section 7.1.3 of RFC 7231</a>
262 	 */
263 	public static final String RETRY_AFTER = "Retry-After";
264 	/**
265 	 * The HTTP {@code Server} header field name.
266 	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-7.4.2">Section 7.4.2 of RFC 7231</a>
267 	 */
268 	public static final String SERVER = "Server";
269 	/**
270 	 * The HTTP {@code Set-Cookie} header field name.
271 	 * @see <a href="http://tools.ietf.org/html/rfc2109#section-4.2.2">Section 4.2.2 of RFC 2109</a>
272 	 */
273 	public static final String SET_COOKIE = "Set-Cookie";
274 	/**
275 	 * The HTTP {@code Set-Cookie2} header field name.
276 	 * @see <a href="http://tools.ietf.org/html/rfc2965">RFC 2965</a>
277 	 */
278 	public static final String SET_COOKIE2 = "Set-Cookie2";
279 	/**
280 	 * The HTTP {@code TE} header field name.
281 	 * @see <a href="http://tools.ietf.org/html/rfc7230#section-4.3">Section 4.3 of RFC 7230</a>
282 	 */
283 	public static final String TE = "TE";
284 	/**
285 	 * The HTTP {@code Trailer} header field name.
286 	 * @see <a href="http://tools.ietf.org/html/rfc7230#section-4.4">Section 4.4 of RFC 7230</a>
287 	 */
288 	public static final String TRAILER = "Trailer";
289 	/**
290 	 * The HTTP {@code Transfer-Encoding} header field name.
291 	 * @see <a href="http://tools.ietf.org/html/rfc7230#section-3.3.1">Section 3.3.1 of RFC 7230</a>
292 	 */
293 	public static final String TRANSFER_ENCODING = "Transfer-Encoding";
294 	/**
295 	 * The HTTP {@code Upgrade} header field name.
296 	 * @see <a href="http://tools.ietf.org/html/rfc7230#section-6.7">Section 6.7 of RFC 7230</a>
297 	 */
298 	public static final String UPGRADE = "Upgrade";
299 	/**
300 	 * The HTTP {@code User-Agent} header field name.
301 	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.5.3">Section 5.5.3 of RFC 7231</a>
302 	 */
303 	public static final String USER_AGENT = "User-Agent";
304 	/**
305 	 * The HTTP {@code Vary} header field name.
306 	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-7.1.4">Section 7.1.4 of RFC 7231</a>
307 	 */
308 	public static final String VARY = "Vary";
309 	/**
310 	 * The HTTP {@code Via} header field name.
311 	 * @see <a href="http://tools.ietf.org/html/rfc7230#section-5.7.1">Section 5.7.1 of RFC 7230</a>
312 	 */
313 	public static final String VIA = "Via";
314 	/**
315 	 * The HTTP {@code Warning} header field name.
316 	 * @see <a href="http://tools.ietf.org/html/rfc7234#section-5.5">Section 5.5 of RFC 7234</a>
317 	 */
318 	public static final String WARNING = "Warning";
319 	/**
320 	 * The HTTP {@code WWW-Authenticate} header field name.
321 	 * @see <a href="http://tools.ietf.org/html/rfc7235#section-4.1">Section 4.1 of RFC 7235</a>
322 	 */
323 	public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
324 
325 	private static final String[] DATE_FORMATS = new String[] {
326 		"EEE, dd MMM yyyy HH:mm:ss zzz",
327 		"EEE, dd-MMM-yy HH:mm:ss zzz",
328 		"EEE MMM dd HH:mm:ss yyyy"
329 	};
330 
331 	private static TimeZone GMT = TimeZone.getTimeZone("GMT");
332 
333 
334 	private final Map<String, List<String>> headers;
335 
336 
337 	/**
338 	 * Constructs a new, empty instance of the {@code HttpHeaders} object.
339 	 */
340 	public HttpHeaders() {
341 		this(new LinkedCaseInsensitiveMap<List<String>>(8, Locale.ENGLISH), false);
342 	}
343 
344 	/**
345 	 * Private constructor that can create read-only {@code HttpHeader} instances.
346 	 */
347 	private HttpHeaders(Map<String, List<String>> headers, boolean readOnly) {
348 		Assert.notNull(headers, "'headers' must not be null");
349 		if (readOnly) {
350 			Map<String, List<String>> map =
351 					new LinkedCaseInsensitiveMap<List<String>>(headers.size(), Locale.ENGLISH);
352 			for (Entry<String, List<String>> entry : headers.entrySet()) {
353 				List<String> values = Collections.unmodifiableList(entry.getValue());
354 				map.put(entry.getKey(), values);
355 			}
356 			this.headers = Collections.unmodifiableMap(map);
357 		}
358 		else {
359 			this.headers = headers;
360 		}
361 	}
362 
363 
364 	/**
365 	 * Set the list of acceptable {@linkplain MediaType media types},
366 	 * as specified by the {@code Accept} header.
367 	 */
368 	public void setAccept(List<MediaType> acceptableMediaTypes) {
369 		set(ACCEPT, MediaType.toString(acceptableMediaTypes));
370 	}
371 
372 	/**
373 	 * Return the list of acceptable {@linkplain MediaType media types},
374 	 * as specified by the {@code Accept} header.
375 	 * <p>Returns an empty list when the acceptable media types are unspecified.
376 	 */
377 	public List<MediaType> getAccept() {
378 		String value = getFirst(ACCEPT);
379 		List<MediaType> result = (value != null ? MediaType.parseMediaTypes(value) : Collections.<MediaType>emptyList());
380 
381 		// Some containers parse 'Accept' into multiple values
382 		if (result.size() == 1) {
383 			List<String> acceptHeader = get(ACCEPT);
384 			if (acceptHeader.size() > 1) {
385 				value = StringUtils.collectionToCommaDelimitedString(acceptHeader);
386 				result = MediaType.parseMediaTypes(value);
387 			}
388 		}
389 
390 		return result;
391 	}
392 
393 	/**
394 	 * Set the list of acceptable {@linkplain Charset charsets},
395 	 * as specified by the {@code Accept-Charset} header.
396 	 */
397 	public void setAcceptCharset(List<Charset> acceptableCharsets) {
398 		StringBuilder builder = new StringBuilder();
399 		for (Iterator<Charset> iterator = acceptableCharsets.iterator(); iterator.hasNext();) {
400 			Charset charset = iterator.next();
401 			builder.append(charset.name().toLowerCase(Locale.ENGLISH));
402 			if (iterator.hasNext()) {
403 				builder.append(", ");
404 			}
405 		}
406 		set(ACCEPT_CHARSET, builder.toString());
407 	}
408 
409 	/**
410 	 * Return the list of acceptable {@linkplain Charset charsets},
411 	 * as specified by the {@code Accept-Charset} header.
412 	 */
413 	public List<Charset> getAcceptCharset() {
414 		List<Charset> result = new ArrayList<Charset>();
415 		String value = getFirst(ACCEPT_CHARSET);
416 		if (value != null) {
417 			String[] tokens = value.split(",\\s*");
418 			for (String token : tokens) {
419 				int paramIdx = token.indexOf(';');
420 				String charsetName;
421 				if (paramIdx == -1) {
422 					charsetName = token;
423 				}
424 				else {
425 					charsetName = token.substring(0, paramIdx);
426 				}
427 				if (!charsetName.equals("*")) {
428 					result.add(Charset.forName(charsetName));
429 				}
430 			}
431 		}
432 		return result;
433 	}
434 
435 	/**
436 	 * Set the set of allowed {@link HttpMethod HTTP methods},
437 	 * as specified by the {@code Allow} header.
438 	 */
439 	public void setAllow(Set<HttpMethod> allowedMethods) {
440 		set(ALLOW, StringUtils.collectionToCommaDelimitedString(allowedMethods));
441 	}
442 
443 	/**
444 	 * Return the set of allowed {@link HttpMethod HTTP methods},
445 	 * as specified by the {@code Allow} header.
446 	 * <p>Returns an empty set when the allowed methods are unspecified.
447 	 */
448 	public Set<HttpMethod> getAllow() {
449 		String value = getFirst(ALLOW);
450 		if (!StringUtils.isEmpty(value)) {
451 			List<HttpMethod> allowedMethod = new ArrayList<HttpMethod>(5);
452 			String[] tokens = value.split(",\\s*");
453 			for (String token : tokens) {
454 				allowedMethod.add(HttpMethod.valueOf(token));
455 			}
456 			return EnumSet.copyOf(allowedMethod);
457 		}
458 		else {
459 			return EnumSet.noneOf(HttpMethod.class);
460 		}
461 	}
462 
463 	/**
464 	 * Set the (new) value of the {@code Cache-Control} header.
465 	 */
466 	public void setCacheControl(String cacheControl) {
467 		set(CACHE_CONTROL, cacheControl);
468 	}
469 
470 	/**
471 	 * Returns the value of the {@code Cache-Control} header.
472 	 */
473 	public String getCacheControl() {
474 		return getFirst(CACHE_CONTROL);
475 	}
476 
477 	/**
478 	 * Set the (new) value of the {@code Connection} header.
479 	 */
480 	public void setConnection(String connection) {
481 		set(CONNECTION, connection);
482 	}
483 
484 	/**
485 	 * Set the (new) value of the {@code Connection} header.
486 	 */
487 	public void setConnection(List<String> connection) {
488 		set(CONNECTION, toCommaDelimitedString(connection));
489 	}
490 
491 	/**
492 	 * Returns the value of the {@code Connection} header.
493 	 */
494 	public List<String> getConnection() {
495 		return getFirstValueAsList(CONNECTION);
496 	}
497 
498 	/**
499 	 * Set the (new) value of the {@code Content-Disposition} header
500 	 * for {@code form-data}.
501 	 * @param name the control name
502 	 * @param filename the filename (may be {@code null})
503 	 */
504 	public void setContentDispositionFormData(String name, String filename) {
505 		Assert.notNull(name, "'name' must not be null");
506 		StringBuilder builder = new StringBuilder("form-data; name=\"");
507 		builder.append(name).append('\"');
508 		if (filename != null) {
509 			builder.append("; filename=\"");
510 			builder.append(filename).append('\"');
511 		}
512 		set(CONTENT_DISPOSITION, builder.toString());
513 	}
514 
515 	/**
516 	 * Set the length of the body in bytes, as specified by the
517 	 * {@code Content-Length} header.
518 	 */
519 	public void setContentLength(long contentLength) {
520 		set(CONTENT_LENGTH, Long.toString(contentLength));
521 	}
522 
523 	/**
524 	 * Return the length of the body in bytes, as specified by the
525 	 * {@code Content-Length} header.
526 	 * <p>Returns -1 when the content-length is unknown.
527 	 */
528 	public long getContentLength() {
529 		String value = getFirst(CONTENT_LENGTH);
530 		return (value != null ? Long.parseLong(value) : -1);
531 	}
532 
533 	/**
534 	 * Set the {@linkplain MediaType media type} of the body,
535 	 * as specified by the {@code Content-Type} header.
536 	 */
537 	public void setContentType(MediaType mediaType) {
538 		Assert.isTrue(!mediaType.isWildcardType(), "'Content-Type' cannot contain wildcard type '*'");
539 		Assert.isTrue(!mediaType.isWildcardSubtype(), "'Content-Type' cannot contain wildcard subtype '*'");
540 		set(CONTENT_TYPE, mediaType.toString());
541 	}
542 
543 	/**
544 	 * Return the {@linkplain MediaType media type} of the body, as specified
545 	 * by the {@code Content-Type} header.
546 	 * <p>Returns {@code null} when the content-type is unknown.
547 	 */
548 	public MediaType getContentType() {
549 		String value = getFirst(CONTENT_TYPE);
550 		return (StringUtils.hasLength(value) ? MediaType.parseMediaType(value) : null);
551 	}
552 
553 	/**
554 	 * Set the date and time at which the message was created, as specified
555 	 * by the {@code Date} header.
556 	 * <p>The date should be specified as the number of milliseconds since
557 	 * January 1, 1970 GMT.
558 	 */
559 	public void setDate(long date) {
560 		setDate(DATE, date);
561 	}
562 
563 	/**
564 	 * Return the date and time at which the message was created, as specified
565 	 * by the {@code Date} header.
566 	 * <p>The date is returned as the number of milliseconds since
567 	 * January 1, 1970 GMT. Returns -1 when the date is unknown.
568 	 * @throws IllegalArgumentException if the value can't be converted to a date
569 	 */
570 	public long getDate() {
571 		return getFirstDate(DATE);
572 	}
573 
574 	/**
575 	 * Set the (new) entity tag of the body, as specified by the {@code ETag} header.
576 	 */
577 	public void setETag(String eTag) {
578 		if (eTag != null) {
579 			Assert.isTrue(eTag.startsWith("\"") || eTag.startsWith("W/"),
580 					"Invalid eTag, does not start with W/ or \"");
581 			Assert.isTrue(eTag.endsWith("\""), "Invalid eTag, does not end with \"");
582 		}
583 		set(ETAG, eTag);
584 	}
585 
586 	/**
587 	 * Return the entity tag of the body, as specified by the {@code ETag} header.
588 	 */
589 	public String getETag() {
590 		return getFirst(ETAG);
591 	}
592 
593 	/**
594 	 * Set the date and time at which the message is no longer valid,
595 	 * as specified by the {@code Expires} header.
596 	 * <p>The date should be specified as the number of milliseconds since
597 	 * January 1, 1970 GMT.
598 	 */
599 	public void setExpires(long expires) {
600 		setDate(EXPIRES, expires);
601 	}
602 
603 	/**
604 	 * Return the date and time at which the message is no longer valid,
605 	 * as specified by the {@code Expires} header.
606 	 * <p>The date is returned as the number of milliseconds since
607 	 * January 1, 1970 GMT. Returns -1 when the date is unknown.
608 	 */
609 	public long getExpires() {
610 		try {
611 			return getFirstDate(EXPIRES);
612 		}
613 		catch (IllegalArgumentException ex) {
614 			return -1;
615 		}
616 	}
617 
618 	/**
619 	 * Set the (new) value of the {@code If-Modified-Since} header.
620 	 * <p>The date should be specified as the number of milliseconds since
621 	 * January 1, 1970 GMT.
622 	 */
623 	public void setIfModifiedSince(long ifModifiedSince) {
624 		setDate(IF_MODIFIED_SINCE, ifModifiedSince);
625 	}
626 
627 	/**
628 	 * Return the value of the {@code IfModifiedSince} header.
629 	 * <p>The date is returned as the number of milliseconds since
630 	 * January 1, 1970 GMT. Returns -1 when the date is unknown.
631 	 * @deprecated use {@link #getIfModifiedSince()}
632 	 */
633 	@Deprecated
634 	public long getIfNotModifiedSince() {
635 		return getIfModifiedSince();
636 	}
637 
638 	/**
639 	 * Return the value of the {@code If-Modified-Since} header.
640 	 * <p>The date is returned as the number of milliseconds since
641 	 * January 1, 1970 GMT. Returns -1 when the date is unknown.
642 	 */
643 	public long getIfModifiedSince() {
644 		return getFirstDate(IF_MODIFIED_SINCE);
645 	}
646 
647 	/**
648 	 * Set the (new) value of the {@code If-None-Match} header.
649 	 */
650 	public void setIfNoneMatch(String ifNoneMatch) {
651 		set(IF_NONE_MATCH, ifNoneMatch);
652 	}
653 
654 	/**
655 	 * Set the (new) values of the {@code If-None-Match} header.
656 	 */
657 	public void setIfNoneMatch(List<String> ifNoneMatchList) {
658 		set(IF_NONE_MATCH, toCommaDelimitedString(ifNoneMatchList));
659 	}
660 
661 	protected String toCommaDelimitedString(List<String> list) {
662 		StringBuilder builder = new StringBuilder();
663 		for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
664 			String ifNoneMatch = iterator.next();
665 			builder.append(ifNoneMatch);
666 			if (iterator.hasNext()) {
667 				builder.append(", ");
668 			}
669 		}
670 		return builder.toString();
671 	}
672 
673 	/**
674 	 * Return the value of the {@code If-None-Match} header.
675 	 */
676 	public List<String> getIfNoneMatch() {
677 		return getFirstValueAsList(IF_NONE_MATCH);
678 	}
679 
680 	protected List<String> getFirstValueAsList(String header) {
681 		List<String> result = new ArrayList<String>();
682 		String value = getFirst(header);
683 		if (value != null) {
684 			String[] tokens = value.split(",\\s*");
685 			for (String token : tokens) {
686 				result.add(token);
687 			}
688 		}
689 		return result;
690 	}
691 
692 	/**
693 	 * Set the time the resource was last changed, as specified by the
694 	 * {@code Last-Modified} header.
695 	 * <p>The date should be specified as the number of milliseconds since
696 	 * January 1, 1970 GMT.
697 	 */
698 	public void setLastModified(long lastModified) {
699 		setDate(LAST_MODIFIED, lastModified);
700 	}
701 
702 	/**
703 	 * Return the time the resource was last changed, as specified by the
704 	 * {@code Last-Modified} header.
705 	 * <p>The date is returned as the number of milliseconds since
706 	 * January 1, 1970 GMT. Returns -1 when the date is unknown.
707 	 */
708 	public long getLastModified() {
709 		return getFirstDate(LAST_MODIFIED);
710 	}
711 
712 	/**
713 	 * Set the (new) location of a resource,
714 	 * as specified by the {@code Location} header.
715 	 */
716 	public void setLocation(URI location) {
717 		set(LOCATION, location.toASCIIString());
718 	}
719 
720 	/**
721 	 * Return the (new) location of a resource
722 	 * as specified by the {@code Location} header.
723 	 * <p>Returns {@code null} when the location is unknown.
724 	 */
725 	public URI getLocation() {
726 		String value = getFirst(LOCATION);
727 		return (value != null ? URI.create(value) : null);
728 	}
729 
730 	/**
731 	 * Set the (new) value of the {@code Origin} header.
732 	 */
733 	public void setOrigin(String origin) {
734 		set(ORIGIN, origin);
735 	}
736 
737 	/**
738 	 * Return the value of the {@code Origin} header.
739 	 */
740 	public String getOrigin() {
741 		return getFirst(ORIGIN);
742 	}
743 
744 	/**
745 	 * Set the (new) value of the {@code Pragma} header.
746 	 */
747 	public void setPragma(String pragma) {
748 		set(PRAGMA, pragma);
749 	}
750 
751 	/**
752 	 * Return the value of the {@code Pragma} header.
753 	 */
754 	public String getPragma() {
755 		return getFirst(PRAGMA);
756 	}
757 
758 	/**
759 	 * Set the (new) value of the {@code Upgrade} header.
760 	 */
761 	public void setUpgrade(String upgrade) {
762 		set(UPGRADE, upgrade);
763 	}
764 
765 	/**
766 	 * Returns the value of the {@code Upgrade} header.
767 	 */
768 	public String getUpgrade() {
769 		return getFirst(UPGRADE);
770 	}
771 
772 	/**
773 	 * Parse the first header value for the given header name as a date,
774 	 * return -1 if there is no value, or raise {@link IllegalArgumentException}
775 	 * if the value cannot be parsed as a date.
776 	 */
777 	public long getFirstDate(String headerName) {
778 		String headerValue = getFirst(headerName);
779 		if (headerValue == null) {
780 			return -1;
781 		}
782 		for (String dateFormat : DATE_FORMATS) {
783 			SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US);
784 			simpleDateFormat.setTimeZone(GMT);
785 			try {
786 				return simpleDateFormat.parse(headerValue).getTime();
787 			}
788 			catch (ParseException e) {
789 				// ignore
790 			}
791 		}
792 		throw new IllegalArgumentException("Cannot parse date value \"" + headerValue +
793 				"\" for \"" + headerName + "\" header");
794 	}
795 
796 	/**
797 	 * Set the given date under the given header name after formatting it as a string
798 	 * using the pattern {@code "EEE, dd MMM yyyy HH:mm:ss zzz"}. The equivalent of
799 	 * {@link #set(String, String)} but for date headers.
800 	 */
801 	public void setDate(String headerName, long date) {
802 		SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMATS[0], Locale.US);
803 		dateFormat.setTimeZone(GMT);
804 		set(headerName, dateFormat.format(new Date(date)));
805 	}
806 
807 	/**
808 	 * Return the first header value for the given header name, if any.
809 	 * @param headerName the header name
810 	 * @return the first header value, or {@code null} if none
811 	 */
812 	@Override
813 	public String getFirst(String headerName) {
814 		List<String> headerValues = headers.get(headerName);
815 		return headerValues != null ? headerValues.get(0) : null;
816 	}
817 
818 	/**
819 	 * Add the given, single header value under the given name.
820 	 * @param headerName the header name
821 	 * @param headerValue the header value
822 	 * @throws UnsupportedOperationException if adding headers is not supported
823 	 * @see #put(String, List)
824 	 * @see #set(String, String)
825 	 */
826 	@Override
827 	public void add(String headerName, String headerValue) {
828 		List<String> headerValues = headers.get(headerName);
829 		if (headerValues == null) {
830 			headerValues = new LinkedList<String>();
831 			this.headers.put(headerName, headerValues);
832 		}
833 		headerValues.add(headerValue);
834 	}
835 
836 	/**
837 	 * Set the given, single header value under the given name.
838 	 * @param headerName the header name
839 	 * @param headerValue the header value
840 	 * @throws UnsupportedOperationException if adding headers is not supported
841 	 * @see #put(String, List)
842 	 * @see #add(String, String)
843 	 */
844 	@Override
845 	public void set(String headerName, String headerValue) {
846 		List<String> headerValues = new LinkedList<String>();
847 		headerValues.add(headerValue);
848 		headers.put(headerName, headerValues);
849 	}
850 
851 	@Override
852 	public void setAll(Map<String, String> values) {
853 		for (Entry<String, String> entry : values.entrySet()) {
854 			set(entry.getKey(), entry.getValue());
855 		}
856 	}
857 
858 	@Override
859 	public Map<String, String> toSingleValueMap() {
860 		LinkedHashMap<String, String> singleValueMap = new LinkedHashMap<String,String>(this.headers.size());
861 		for (Entry<String, List<String>> entry : headers.entrySet()) {
862 			singleValueMap.put(entry.getKey(), entry.getValue().get(0));
863 		}
864 		return singleValueMap;
865 	}
866 
867 
868 	// Map implementation
869 
870 	@Override
871 	public int size() {
872 		return this.headers.size();
873 	}
874 
875 	@Override
876 	public boolean isEmpty() {
877 		return this.headers.isEmpty();
878 	}
879 
880 	@Override
881 	public boolean containsKey(Object key) {
882 		return this.headers.containsKey(key);
883 	}
884 
885 	@Override
886 	public boolean containsValue(Object value) {
887 		return this.headers.containsValue(value);
888 	}
889 
890 	@Override
891 	public List<String> get(Object key) {
892 		return this.headers.get(key);
893 	}
894 
895 	@Override
896 	public List<String> put(String key, List<String> value) {
897 		return this.headers.put(key, value);
898 	}
899 
900 	@Override
901 	public List<String> remove(Object key) {
902 		return this.headers.remove(key);
903 	}
904 
905 	@Override
906 	public void putAll(Map<? extends String, ? extends List<String>> map) {
907 		this.headers.putAll(map);
908 	}
909 
910 	@Override
911 	public void clear() {
912 		this.headers.clear();
913 	}
914 
915 	@Override
916 	public Set<String> keySet() {
917 		return this.headers.keySet();
918 	}
919 
920 	@Override
921 	public Collection<List<String>> values() {
922 		return this.headers.values();
923 	}
924 
925 	@Override
926 	public Set<Entry<String, List<String>>> entrySet() {
927 		return this.headers.entrySet();
928 	}
929 
930 
931 	@Override
932 	public boolean equals(Object other) {
933 		if (this == other) {
934 			return true;
935 		}
936 		if (!(other instanceof HttpHeaders)) {
937 			return false;
938 		}
939 		HttpHeaders otherHeaders = (HttpHeaders) other;
940 		return this.headers.equals(otherHeaders.headers);
941 	}
942 
943 	@Override
944 	public int hashCode() {
945 		return this.headers.hashCode();
946 	}
947 
948 	@Override
949 	public String toString() {
950 		return this.headers.toString();
951 	}
952 
953 
954 	/**
955 	 * Return a {@code HttpHeaders} object that can only be read, not written to.
956 	 */
957 	public static HttpHeaders readOnlyHttpHeaders(HttpHeaders headers) {
958 		return new HttpHeaders(headers, true);
959 	}
960 
961 }