HttpHeaders.java 31.1 KB
Newer Older
1
/*
2
 * Copyright 2002-2014 the original author or authors.
3 4 5 6 7
 *
 * 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
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9 10 11 12 13 14 15 16
 *
 * 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.
 */

17
package org.springframework.http;
18

19
import java.io.Serializable;
20
import java.net.URI;
21
import java.nio.charset.Charset;
A
Arjen Poutsma 已提交
22 23
import java.text.ParseException;
import java.text.SimpleDateFormat;
24 25 26
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
A
Arjen Poutsma 已提交
27
import java.util.Date;
28
import java.util.EnumSet;
29
import java.util.Iterator;
30
import java.util.LinkedHashMap;
31 32
import java.util.LinkedList;
import java.util.List;
33
import java.util.Locale;
34 35
import java.util.Map;
import java.util.Set;
A
Arjen Poutsma 已提交
36
import java.util.TimeZone;
37 38

import org.springframework.util.Assert;
39
import org.springframework.util.LinkedCaseInsensitiveMap;
A
Arjen Poutsma 已提交
40
import org.springframework.util.MultiValueMap;
41 42 43 44 45 46
import org.springframework.util.StringUtils;

/**
 * Represents HTTP request and response headers, mapping string header names to list of string values.
 *
 * <p>In addition to the normal methods defined by {@link Map}, this class offers the following convenience methods:
47 48 49 50 51
 * <ul>
 * <li>{@link #getFirst(String)} returns the first value associated with a given header name</li>
 * <li>{@link #add(String, String)} adds a header value to the list of values for a header name</li>
 * <li>{@link #set(String, String)} sets the header value to a single string value</li>
 * </ul>
52 53 54 55
 *
 * <p>Inspired by {@link com.sun.net.httpserver.Headers}.
 *
 * @author Arjen Poutsma
56
 * @author Sebastien Deleuze
57 58
 * @since 3.0
 */
59 60 61
public class HttpHeaders implements MultiValueMap<String, String>, Serializable {

	private static final long serialVersionUID = -8578554704772377436L;
62

63 64 65 66
	/**
	 * The HTTP {@code Accept} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.3.2">Section 5.3.2 of RFC 7231</a>
	 */
67
	public static final String ACCEPT = "Accept";
68 69 70 71
	/**
	 * The HTTP {@code Accept-Charset} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.3.3">Section 5.3.3 of RFC 7231</a>
	 */
72
	public static final String ACCEPT_CHARSET = "Accept-Charset";
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
	/**
	 * The HTTP {@code Accept-Encoding} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.3.4">Section 5.3.4 of RFC 7231</a>
	 */
	public static final String ACCEPT_ENCODING = "Accept-Encoding";
	/**
	 * The HTTP {@code Accept-Language} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.3.5">Section 5.3.5 of RFC 7231</a>
	 */
	public static final String ACCEPT_LANGUAGE = "Accept-Language";
	/**
	 * The HTTP {@code Accept-Ranges} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7233#section-2.3">Section 5.3.5 of RFC 7233</a>
	 */
	public static final String ACCEPT_RANGES = "Accept-Ranges";
	/**
	 * The HTTP {@code Age} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7234#section-5.1">Section 5.1 of RFC 7234</a>
	 */
	public static final String AGE = "Age";
	/**
	 * The HTTP {@code Allow} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-7.4.1">Section 7.4.1 of RFC 7231</a>
	 */
97
	public static final String ALLOW = "Allow";
98 99 100 101 102 103 104 105 106
	/**
	 * The HTTP {@code Authorization} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7235#section-4.2">Section 4.2 of RFC 7235</a>
	 */
	public static final String AUTHORIZATION = "Authorization";
	/**
	 * The HTTP {@code Cache-Control} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7234#section-5.2">Section 5.2 of RFC 7234</a>
	 */
107
	public static final String CACHE_CONTROL = "Cache-Control";
108 109 110 111
	/**
	 * The HTTP {@code Connection} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7230#section-6.1">Section 6.1 of RFC 7230</a>
	 */
112
	public static final String CONNECTION = "Connection";
113 114 115 116 117 118 119 120 121
	/**
	 * The HTTP {@code Content-Encoding} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-3.1.2.2">Section 3.1.2.2 of RFC 7231</a>
	 */
	public static final String CONTENT_ENCODING = "Content-Encoding";
	/**
	 * The HTTP {@code Content-Disposition} header field name
	 * @see <a href="http://tools.ietf.org/html/rfc6266">RFC 6266</a>
	 */
122
	public static final String CONTENT_DISPOSITION = "Content-Disposition";
123 124 125 126 127 128 129 130 131
	/**
	 * The HTTP {@code Content-Language} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-3.1.3.2">Section 3.1.3.2 of RFC 7231</a>
	 */
	public static final String CONTENT_LANGUAGE = "Content-Language";
	/**
	 * The HTTP {@code Content-Length} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7230#section-3.3.2">Section 3.3.2 of RFC 7230</a>
	 */
132
	public static final String CONTENT_LENGTH = "Content-Length";
133 134 135 136 137 138 139 140 141 142 143 144 145 146
	/**
	 * The HTTP {@code Content-Location} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-3.1.4.2">Section 3.1.4.2 of RFC 7231</a>
	 */
	public static final String CONTENT_LOCATION = "Content-Location";
	/**
	 * The HTTP {@code Content-Range} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7233#section-4.2">Section 4.2 of RFC 7233</a>
	 */
	public static final String CONTENT_RANGE = "Content-Range";
	/**
	 * The HTTP {@code Content-Type} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-3.1.1.5">Section 3.1.1.5 of RFC 7231</a>
	 */
147
	public static final String CONTENT_TYPE = "Content-Type";
148 149 150 151 152 153 154 155 156
	/**
	 * The HTTP {@code Cookie} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc2109#section-4.3.4">Section 4.3.4 of RFC 2109</a>
	 */
	public static final String COOKIE = "Cookie";
	/**
	 * The HTTP {@code Date} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-7.1.1.2">Section 7.1.1.2 of RFC 7231</a>
	 */
157
	public static final String DATE = "Date";
158 159 160 161
	/**
	 * The HTTP {@code ETag} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7232#section-2.3">Section 2.3 of RFC 7232</a>
	 */
162
	public static final String ETAG = "ETag";
163 164 165 166 167 168 169 170 171
	/**
	 * The HTTP {@code Expect} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.1.1">Section 5.1.1 of RFC 7231</a>
	 */
	public static final String EXPECT = "Expect";
	/**
	 * The HTTP {@code Expires} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7234#section-5.3">Section 5.3 of RFC 7234</a>
	 */
172
	public static final String EXPIRES = "Expires";
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
	/**
	 * The HTTP {@code From} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.5.1">Section 5.5.1 of RFC 7231</a>
	 */
	public static final String FROM = "From";
	/**
	 * The HTTP {@code Host} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7230#section-5.4">Section 5.4 of RFC 7230</a>
	 */
	public static final String HOST = "Host";
	/**
	 * The HTTP {@code If-Match} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7232#section-3.1">Section 3.1 of RFC 7232</a>
	 */
	public static final String IF_MATCH = "If-Match";
	/**
	 * The HTTP {@code If-Modified-Since} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7232#section-3.3">Section 3.3 of RFC 7232</a>
	 */
192
	public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
193 194 195 196
	/**
	 * The HTTP {@code If-None-Match} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7232#section-3.2">Section 3.2 of RFC 7232</a>
	 */
197
	public static final String IF_NONE_MATCH = "If-None-Match";
198 199 200 201 202 203 204 205 206 207 208 209 210 211
	/**
	 * The HTTP {@code If-Range} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7233#section-3.2">Section 3.2 of RFC 7233</a>
	 */
	public static final String IF_RANGE = "If-Range";
	/**
	 * The HTTP {@code If-Unmodified-Since} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7232#section-3.4">Section 3.4 of RFC 7232</a>
	 */
	public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
	/**
	 * The HTTP {@code Last-Modified} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7232#section-2.2">Section 2.2 of RFC 7232</a>
	 */
212
	public static final String LAST_MODIFIED = "Last-Modified";
213 214 215 216 217 218 219 220 221
	/**
	 * The HTTP {@code Link} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc5988">RFC 5988</a>
	 */
	public static final String LINK = "Link";
	/**
	 * The HTTP {@code Location} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-7.1.2">Section 7.1.2 of RFC 7231</a>
	 */
222
	public static final String LOCATION = "Location";
223 224 225 226 227 228 229 230 231
	/**
	 * The HTTP {@code Max-Forwards} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.1.2">Section 5.1.2 of RFC 7231</a>
	 */
	public static final String MAX_FORWARDS = "Max-Forwards";
	/**
	 * The HTTP {@code Origin} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc6454">RFC 6454</a>
	 */
232
	public static final String ORIGIN = "Origin";
233 234 235 236
	/**
	 * The HTTP {@code Pragma} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7234#section-5.4">Section 5.4 of RFC 7234</a>
	 */
237
	public static final String PRAGMA = "Pragma";
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
	/**
	 * The HTTP {@code Proxy-Authenticate} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7235#section-4.3">Section 4.3 of RFC 7235</a>
	 */
	public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
	/**
	 * The HTTP {@code Proxy-Authorization} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7235#section-4.4">Section 4.4 of RFC 7235</a>
	 */
	public static final String PROXY_AUTHORIZATION = "Proxy-Authorization";
	/**
	 * The HTTP {@code Range} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7233#section-3.1">Section 3.1 of RFC 7233</a>
	 */
	public static final String RANGE = "Range";
	/**
	 * The HTTP {@code Referer} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.5.2">Section 5.5.2 of RFC 7231</a>
	 */
	public static final String REFERER = "Referer";
	/**
	 * The HTTP {@code Retry-After} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-7.1.3">Section 7.1.3 of RFC 7231</a>
	 */
	public static final String RETRY_AFTER = "Retry-After";
	/**
	 * The HTTP {@code Server} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-7.4.2">Section 7.4.2 of RFC 7231</a>
	 */
	public static final String SERVER = "Server";
	/**
	 * The HTTP {@code Set-Cookie} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc2109#section-4.2.2">Section 4.2.2 of RFC 2109</a>
	 */
	public static final String SET_COOKIE = "Set-Cookie";
	/**
	 * The HTTP {@code Set-Cookie2} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc2965">RFC 2965</a>
	 */
	public static final String SET_COOKIE2 = "Set-Cookie2";
	/**
	 * The HTTP {@code TE} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7230#section-4.3">Section 4.3 of RFC 7230</a>
	 */
	public static final String TE = "TE";
	/**
	 * The HTTP {@code Trailer} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7230#section-4.4">Section 4.4 of RFC 7230</a>
	 */
	public static final String TRAILER = "Trailer";
	/**
	 * The HTTP {@code Transfer-Encoding} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7230#section-3.3.1">Section 3.3.1 of RFC 7230</a>
	 */
	public static final String TRANSFER_ENCODING = "Transfer-Encoding";
	/**
	 * The HTTP {@code Upgrade} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7230#section-6.7">Section 6.7 of RFC 7230</a>
	 */
297
	public static final String UPGRADE = "Upgrade";
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
	/**
	 * The HTTP {@code User-Agent} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-5.5.3">Section 5.5.3 of RFC 7231</a>
	 */
	public static final String USER_AGENT = "User-Agent";
	/**
	 * The HTTP {@code Vary} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7231#section-7.1.4">Section 7.1.4 of RFC 7231</a>
	 */
	public static final String VARY = "Vary";
	/**
	 * The HTTP {@code Via} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7230#section-5.7.1">Section 5.7.1 of RFC 7230</a>
	 */
	public static final String VIA = "Via";
	/**
	 * The HTTP {@code Warning} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7234#section-5.5">Section 5.5 of RFC 7234</a>
	 */
	public static final String WARNING = "Warning";
	/**
	 * The HTTP {@code WWW-Authenticate} header field name.
	 * @see <a href="http://tools.ietf.org/html/rfc7235#section-4.1">Section 4.1 of RFC 7235</a>
	 */
	public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
A
Arjen Poutsma 已提交
323 324 325

	private static final String[] DATE_FORMATS = new String[] {
		"EEE, dd MMM yyyy HH:mm:ss zzz",
P
Phillip Webb 已提交
326 327
		"EEE, dd-MMM-yy HH:mm:ss zzz",
		"EEE MMM dd HH:mm:ss yyyy"
A
Arjen Poutsma 已提交
328 329 330
	};

	private static TimeZone GMT = TimeZone.getTimeZone("GMT");
331

332 333
	private final Map<String, List<String>> headers;

334

335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
	/**
	 * Private constructor that can create read-only {@code HttpHeader} instances.
	 */
	private HttpHeaders(Map<String, List<String>> headers, boolean readOnly) {
		Assert.notNull(headers, "'headers' must not be null");
		if (readOnly) {
			Map<String, List<String>> map =
					new LinkedCaseInsensitiveMap<List<String>>(headers.size(), Locale.ENGLISH);
			for (Entry<String, List<String>> entry : headers.entrySet()) {
				List<String> values = Collections.unmodifiableList(entry.getValue());
				map.put(entry.getKey(), values);
			}
			this.headers = Collections.unmodifiableMap(map);
		}
		else {
			this.headers = headers;
		}
	}
353

354
	/**
A
Javadoc  
Arjen Poutsma 已提交
355
	 * Constructs a new, empty instance of the {@code HttpHeaders} object.
356 357 358 359
	 */
	public HttpHeaders() {
		this(new LinkedCaseInsensitiveMap<List<String>>(8, Locale.ENGLISH), false);
	}
360

361 362 363 364 365 366
	/**
	 * Returns {@code HttpHeaders} object that can only be read, not written to.
	 */
	public static HttpHeaders readOnlyHttpHeaders(HttpHeaders headers) {
		return new HttpHeaders(headers, true);
	}
367 368

	/**
A
Arjen Poutsma 已提交
369
	 * Set the list of acceptable {@linkplain MediaType media types}, as specified by the {@code Accept} header.
370 371 372 373 374
	 * @param acceptableMediaTypes the acceptable media types
	 */
	public void setAccept(List<MediaType> acceptableMediaTypes) {
		set(ACCEPT, MediaType.toString(acceptableMediaTypes));
	}
375 376

	/**
A
Arjen Poutsma 已提交
377
	 * Return the list of acceptable {@linkplain MediaType media types}, as specified by the {@code Accept} header.
378
	 * <p>Returns an empty list when the acceptable media types are unspecified.
379 380 381 382
	 * @return the acceptable media types
	 */
	public List<MediaType> getAccept() {
		String value = getFirst(ACCEPT);
J
Juergen Hoeller 已提交
383
		List<MediaType> result = (value != null ? MediaType.parseMediaTypes(value) : Collections.<MediaType>emptyList());
384 385

		// Some containers parse 'Accept' into multiple values
J
Juergen Hoeller 已提交
386 387 388 389 390 391
		if (result.size() == 1) {
			List<String> acceptHeader = get(ACCEPT);
			if (acceptHeader.size() > 1) {
				value = StringUtils.collectionToCommaDelimitedString(acceptHeader);
				result = MediaType.parseMediaTypes(value);
			}
392 393 394
		}

		return result;
395 396 397
	}

	/**
A
Arjen Poutsma 已提交
398
	 * Set the list of acceptable {@linkplain Charset charsets}, as specified by the {@code Accept-Charset} header.
399
	 * @param acceptableCharsets the acceptable charsets
400
	 */
401 402 403 404 405 406 407 408 409 410
	public void setAcceptCharset(List<Charset> acceptableCharsets) {
		StringBuilder builder = new StringBuilder();
		for (Iterator<Charset> iterator = acceptableCharsets.iterator(); iterator.hasNext();) {
			Charset charset = iterator.next();
			builder.append(charset.name().toLowerCase(Locale.ENGLISH));
			if (iterator.hasNext()) {
				builder.append(", ");
			}
		}
		set(ACCEPT_CHARSET, builder.toString());
411 412
	}

413
	/**
A
Arjen Poutsma 已提交
414
	 * Return the list of acceptable {@linkplain Charset charsets}, as specified by the {@code Accept-Charset}
415 416 417 418 419 420 421 422 423 424
	 * header.
	 * @return the acceptable charsets
	 */
	public List<Charset> getAcceptCharset() {
		List<Charset> result = new ArrayList<Charset>();
		String value = getFirst(ACCEPT_CHARSET);
		if (value != null) {
			String[] tokens = value.split(",\\s*");
			for (String token : tokens) {
				int paramIdx = token.indexOf(';');
425
				String charsetName;
426
				if (paramIdx == -1) {
427
					charsetName = token;
428 429
				}
				else {
430 431 432 433
					charsetName = token.substring(0, paramIdx);
				}
				if (!charsetName.equals("*")) {
					result.add(Charset.forName(charsetName));
434 435 436 437 438 439 440
				}
			}
		}
		return result;
	}

	/**
A
Arjen Poutsma 已提交
441
	 * Set the set of allowed {@link HttpMethod HTTP methods}, as specified by the {@code Allow} header.
442
	 * @param allowedMethods the allowed methods
443
	 */
444 445
	public void setAllow(Set<HttpMethod> allowedMethods) {
		set(ALLOW, StringUtils.collectionToCommaDelimitedString(allowedMethods));
446 447
	}

448
	/**
A
Arjen Poutsma 已提交
449
	 * Return the set of allowed {@link HttpMethod HTTP methods}, as specified by the {@code Allow} header.
450
	 * <p>Returns an empty set when the allowed methods are unspecified.
451 452
	 * @return the allowed methods
	 */
453
	public Set<HttpMethod> getAllow() {
454
		String value = getFirst(ALLOW);
455
		if (!StringUtils.isEmpty(value)) {
456 457 458 459 460 461 462 463 464 465 466 467
			List<HttpMethod> allowedMethod = new ArrayList<HttpMethod>(5);
			String[] tokens = value.split(",\\s*");
			for (String token : tokens) {
				allowedMethod.add(HttpMethod.valueOf(token));
			}
			return EnumSet.copyOf(allowedMethod);
		}
		else {
			return EnumSet.noneOf(HttpMethod.class);
		}
	}

A
Arjen Poutsma 已提交
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
	/**
	 * Sets the (new) value of the {@code Cache-Control} header.
	 * @param cacheControl the value of the header
	 */
	public void setCacheControl(String cacheControl) {
		set(CACHE_CONTROL, cacheControl);
	}

	/**
	 * Returns the value of the {@code Cache-Control} header.
	 * @return the value of the header
	 */
	public String getCacheControl() {
		return getFirst(CACHE_CONTROL);
	}

484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
	/**
	 * Sets the (new) value of the {@code Connection} header.
	 * @param connection the value of the header
	 */
	public void setConnection(String connection) {
		set(CONNECTION, connection);
	}

	/**
	 * Sets the (new) value of the {@code Connection} header.
	 * @param connection the value of the header
	 */
	public void setConnection(List<String> connection) {
		set(CONNECTION, toCommaDelimitedString(connection));
	}

	/**
	 * Returns the value of the {@code Connection} header.
	 * @return the value of the header
	 */
	public List<String> getConnection() {
		return getFirstValueAsList(CONNECTION);
	}

508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
	/**
	 * Sets the (new) value of the {@code Content-Disposition} header for {@code form-data}.
	 * @param name the control name
	 * @param filename the filename, may be {@code null}
	 */
	public void setContentDispositionFormData(String name, String filename) {
		Assert.notNull(name, "'name' must not be null");
		StringBuilder builder = new StringBuilder("form-data; name=\"");
		builder.append(name).append('\"');
		if (filename != null) {
			builder.append("; filename=\"");
			builder.append(filename).append('\"');
		}
		set(CONTENT_DISPOSITION, builder.toString());
	}

524
	/**
A
Arjen Poutsma 已提交
525
	 * Set the length of the body in bytes, as specified by the {@code Content-Length} header.
526
	 * @param contentLength the content length
527
	 */
528 529
	public void setContentLength(long contentLength) {
		set(CONTENT_LENGTH, Long.toString(contentLength));
530 531 532
	}

	/**
A
Arjen Poutsma 已提交
533
	 * Return the length of the body in bytes, as specified by the {@code Content-Length} header.
534
	 * <p>Returns -1 when the content-length is unknown.
535 536 537 538
	 * @return the content length
	 */
	public long getContentLength() {
		String value = getFirst(CONTENT_LENGTH);
539
		return (value != null ? Long.parseLong(value) : -1);
540 541 542
	}

	/**
A
Arjen Poutsma 已提交
543
	 * Set the {@linkplain MediaType media type} of the body, as specified by the {@code Content-Type} header.
544
	 * @param mediaType the media type
545
	 */
546 547 548 549
	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());
550 551 552
	}

	/**
A
Arjen Poutsma 已提交
553 554
	 * 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.
555 556 557 558
	 * @return the content type
	 */
	public MediaType getContentType() {
		String value = getFirst(CONTENT_TYPE);
559
		return (StringUtils.hasLength(value) ? MediaType.parseMediaType(value) : null);
560 561 562
	}

	/**
A
Arjen Poutsma 已提交
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
	 * 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) {
586 587 588 589
		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 \"");
		}
J
Juergen Hoeller 已提交
590
		set(ETAG, eTag);
A
Arjen Poutsma 已提交
591 592 593 594 595 596 597
	}

	/**
	 * Returns the entity tag of the body, as specified by the {@code ETag} header.
	 * @return the entity tag
	 */
	public String getETag() {
598
		return getFirst(ETAG);
A
Arjen Poutsma 已提交
599 600 601 602 603 604 605 606 607 608 609 610
	}

	/**
	 * 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);
	}

	/**
611 612 613 614 615
	 * 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.
	 *
A
Arjen Poutsma 已提交
616 617 618
	 * @return the expires value
	 */
	public long getExpires() {
619 620 621 622 623 624
		try {
			return getFirstDate(EXPIRES);
		}
		catch (IllegalArgumentException ex) {
			return -1;
		}
A
Arjen Poutsma 已提交
625 626
	}

A
Arjen Poutsma 已提交
627 628 629 630 631 632 633 634 635 636 637 638 639
	/**
	 * Sets the (new) value of the {@code If-Modified-Since} header.
	 * <p>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.
	 * <p>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
640
	 * @deprecated use {@link #getIfModifiedSince()}
A
Arjen Poutsma 已提交
641
	 */
642
	@Deprecated
A
Arjen Poutsma 已提交
643
	public long getIfNotModifiedSince() {
644 645 646 647 648 649 650 651 652
		return getIfModifiedSince();
	}

	/**
	 * Returns the value of the {@code If-Modified-Since} 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 header value
	 */
	public long getIfModifiedSince() {
A
Arjen Poutsma 已提交
653 654 655
		return getFirstDate(IF_MODIFIED_SINCE);
	}

A
Arjen Poutsma 已提交
656 657 658 659 660
	/**
	 * Sets the (new) value of the {@code If-None-Match} header.
	 * @param ifNoneMatch the new value of the header
	 */
	public void setIfNoneMatch(String ifNoneMatch) {
661
		set(IF_NONE_MATCH, ifNoneMatch);
A
Arjen Poutsma 已提交
662 663 664 665 666 667 668
	}

	/**
	 * 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) {
669 670 671
		set(IF_NONE_MATCH, toCommaDelimitedString(ifNoneMatchList));
	}

672
	protected String toCommaDelimitedString(List<String> list) {
A
Arjen Poutsma 已提交
673
		StringBuilder builder = new StringBuilder();
674
		for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
A
Arjen Poutsma 已提交
675
			String ifNoneMatch = iterator.next();
676
			builder.append(ifNoneMatch);
A
Arjen Poutsma 已提交
677 678 679 680
			if (iterator.hasNext()) {
				builder.append(", ");
			}
		}
681
		return builder.toString();
A
Arjen Poutsma 已提交
682 683 684 685 686 687 688
	}

	/**
	 * Returns the value of the {@code If-None-Match} header.
	 * @return the header value
	 */
	public List<String> getIfNoneMatch() {
689 690 691
		return getFirstValueAsList(IF_NONE_MATCH);
	}

692
	protected List<String> getFirstValueAsList(String header) {
A
Arjen Poutsma 已提交
693 694
		List<String> result = new ArrayList<String>();

695
		String value = getFirst(header);
A
Arjen Poutsma 已提交
696 697 698
		if (value != null) {
			String[] tokens = value.split(",\\s*");
			for (String token : tokens) {
699
				result.add(token);
A
Arjen Poutsma 已提交
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
			}
		}
		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.
725
	 * @param location the location
726
	 */
727 728
	public void setLocation(URI location) {
		set(LOCATION, location.toASCIIString());
729 730 731
	}

	/**
A
Arjen Poutsma 已提交
732 733
	 * Return the (new) location of a resource, as specified by the {@code Location} header.
	 * <p>Returns {@code null} when the location is unknown.
734 735 736 737
	 * @return the location
	 */
	public URI getLocation() {
		String value = getFirst(LOCATION);
738
		return (value != null ? URI.create(value) : null);
739 740
	}

741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
	/**
	 * 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);
	}

A
Arjen Poutsma 已提交
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
	/**
	 * 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);
	}

773 774 775 776 777
	/**
	 * Sets the (new) value of the {@code Upgrade} header.
	 * @param upgrade the value of the header
	 */
	public void setUpgrade(String upgrade) {
R
Rossen Stoyanchev 已提交
778
		set(UPGRADE, upgrade);
779 780 781 782 783 784 785
	}

	/**
	 * Returns the value of the {@code Upgrade} header.
	 * @return the value of the header
	 */
	public String getUpgrade() {
R
Rossen Stoyanchev 已提交
786
		return getFirst(UPGRADE);
787 788
	}

789
	// Date methods
A
Arjen Poutsma 已提交
790

791 792 793 794 795 796
	/**
	 * 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) {
A
Arjen Poutsma 已提交
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814
		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");
	}

815 816 817 818 819 820
	/**
	 * 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) {
821
		SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMATS[0], Locale.US);
A
Arjen Poutsma 已提交
822 823 824
		dateFormat.setTimeZone(GMT);
		set(headerName, dateFormat.format(new Date(date)));
	}
825

826
	// Single string methods
827 828

	/**
829
	 * Return the first header value for the given header name, if any.
830
	 * @param headerName the header name
A
Arjen Poutsma 已提交
831
	 * @return the first header value; or {@code null}
832
	 */
833
	@Override
834 835 836 837 838 839
	public String getFirst(String headerName) {
		List<String> headerValues = headers.get(headerName);
		return headerValues != null ? headerValues.get(0) : null;
	}

	/**
840
	 * Add the given, single header value under the given name.
841 842 843 844 845 846
	 * @param headerName  the header name
	 * @param headerValue the header value
	 * @throws UnsupportedOperationException if adding headers is not supported
	 * @see #put(String, List)
	 * @see #set(String, String)
	 */
847
	@Override
848 849 850 851
	public void add(String headerName, String headerValue) {
		List<String> headerValues = headers.get(headerName);
		if (headerValues == null) {
			headerValues = new LinkedList<String>();
852
			this.headers.put(headerName, headerValues);
853 854 855 856 857
		}
		headerValues.add(headerValue);
	}

	/**
858
	 * Set the given, single header value under the given name.
859 860 861 862 863 864
	 * @param headerName  the header name
	 * @param headerValue the header value
	 * @throws UnsupportedOperationException if adding headers is not supported
	 * @see #put(String, List)
	 * @see #add(String, String)
	 */
865
	@Override
866 867 868 869 870 871
	public void set(String headerName, String headerValue) {
		List<String> headerValues = new LinkedList<String>();
		headerValues.add(headerValue);
		headers.put(headerName, headerValues);
	}

872
	@Override
873 874 875 876 877 878
	public void setAll(Map<String, String> values) {
		for (Entry<String, String> entry : values.entrySet()) {
			set(entry.getKey(), entry.getValue());
		}
	}

879
	@Override
880 881 882 883 884 885 886
	public Map<String, String> toSingleValueMap() {
		LinkedHashMap<String, String> singleValueMap = new LinkedHashMap<String,String>(this.headers.size());
		for (Entry<String, List<String>> entry : headers.entrySet()) {
			singleValueMap.put(entry.getKey(), entry.getValue().get(0));
		}
		return singleValueMap;
	}
887 888

	// Map implementation
889

890
	@Override
891
	public int size() {
892
		return this.headers.size();
893 894
	}

895
	@Override
896
	public boolean isEmpty() {
897
		return this.headers.isEmpty();
898 899
	}

900
	@Override
901
	public boolean containsKey(Object key) {
902
		return this.headers.containsKey(key);
903 904
	}

905
	@Override
906
	public boolean containsValue(Object value) {
907
		return this.headers.containsValue(value);
908 909
	}

910
	@Override
911
	public List<String> get(Object key) {
912
		return this.headers.get(key);
913 914
	}

915
	@Override
916
	public List<String> put(String key, List<String> value) {
917
		return this.headers.put(key, value);
918 919
	}

920
	@Override
921
	public List<String> remove(Object key) {
922
		return this.headers.remove(key);
923 924
	}

925
	@Override
926
	public void putAll(Map<? extends String, ? extends List<String>> m) {
927
		this.headers.putAll(m);
928 929
	}

930
	@Override
931
	public void clear() {
932
		this.headers.clear();
933 934
	}

935
	@Override
936
	public Set<String> keySet() {
937
		return this.headers.keySet();
938 939
	}

940
	@Override
941
	public Collection<List<String>> values() {
942
		return this.headers.values();
943 944
	}

945
	@Override
946
	public Set<Entry<String, List<String>>> entrySet() {
947
		return this.headers.entrySet();
948 949
	}

950

951
	@Override
952 953
	public boolean equals(Object other) {
		if (this == other) {
954 955
			return true;
		}
956 957
		if (!(other instanceof HttpHeaders)) {
			return false;
958
		}
959 960 961 962 963 964 965
		HttpHeaders otherHeaders = (HttpHeaders) other;
		return this.headers.equals(otherHeaders.headers);
	}

	@Override
	public int hashCode() {
		return this.headers.hashCode();
966 967 968 969
	}

	@Override
	public String toString() {
970
		return this.headers.toString();
971
	}
972

973
}