001/*
002 * Copyright 2002-2020 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.http;
018
019import java.io.Serializable;
020import java.net.URI;
021import java.nio.charset.Charset;
022import java.text.ParseException;
023import java.text.SimpleDateFormat;
024import java.util.ArrayList;
025import java.util.Collection;
026import java.util.Collections;
027import java.util.Date;
028import java.util.EnumSet;
029import java.util.Iterator;
030import java.util.LinkedHashMap;
031import java.util.LinkedList;
032import java.util.List;
033import java.util.Locale;
034import java.util.Map;
035import java.util.Set;
036import java.util.TimeZone;
037import java.util.regex.Matcher;
038import java.util.regex.Pattern;
039
040import org.springframework.util.Assert;
041import org.springframework.util.LinkedCaseInsensitiveMap;
042import org.springframework.util.MultiValueMap;
043import org.springframework.util.StringUtils;
044
045/**
046 * A data structure representing HTTP request or response headers, mapping String header names
047 * to a list of String values, also offering accessors for common application-level data types.
048 *
049 * <p>In addition to the regular methods defined by {@link Map}, this class offers many common
050 * convenience methods, for example:
051 * <ul>
052 * <li>{@link #getFirst(String)} returns the first value associated with a given header name</li>
053 * <li>{@link #add(String, String)} adds a header value to the list of values for a header name</li>
054 * <li>{@link #set(String, String)} sets the header value to a single string value</li>
055 * </ul>
056 *
057 * <p>Note that {@code HttpHeaders} generally treats header names in a case-insensitive manner.
058 *
059 * @author Arjen Poutsma
060 * @author Sebastien Deleuze
061 * @author Brian Clozel
062 * @author Juergen Hoeller
063 * @author Josh Long
064 * @since 3.0
065 */
066public class HttpHeaders implements MultiValueMap<String, String>, Serializable {
067
068        private static final long serialVersionUID = -8578554704772377436L;
069
070
071        /**
072         * The HTTP {@code Accept} header field name.
073         * @see <a href="https://tools.ietf.org/html/rfc7231#section-5.3.2">Section 5.3.2 of RFC 7231</a>
074         */
075        public static final String ACCEPT = "Accept";
076        /**
077         * The HTTP {@code Accept-Charset} header field name.
078         * @see <a href="https://tools.ietf.org/html/rfc7231#section-5.3.3">Section 5.3.3 of RFC 7231</a>
079         */
080        public static final String ACCEPT_CHARSET = "Accept-Charset";
081        /**
082         * The HTTP {@code Accept-Encoding} header field name.
083         * @see <a href="https://tools.ietf.org/html/rfc7231#section-5.3.4">Section 5.3.4 of RFC 7231</a>
084         */
085        public static final String ACCEPT_ENCODING = "Accept-Encoding";
086        /**
087         * The HTTP {@code Accept-Language} header field name.
088         * @see <a href="https://tools.ietf.org/html/rfc7231#section-5.3.5">Section 5.3.5 of RFC 7231</a>
089         */
090        public static final String ACCEPT_LANGUAGE = "Accept-Language";
091        /**
092         * The HTTP {@code Accept-Ranges} header field name.
093         * @see <a href="https://tools.ietf.org/html/rfc7233#section-2.3">Section 5.3.5 of RFC 7233</a>
094         */
095        public static final String ACCEPT_RANGES = "Accept-Ranges";
096        /**
097         * The CORS {@code Access-Control-Allow-Credentials} response header field name.
098         * @see <a href="https://www.w3.org/TR/cors/">CORS W3C recommendation</a>
099         */
100        public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
101        /**
102         * The CORS {@code Access-Control-Allow-Headers} response header field name.
103         * @see <a href="https://www.w3.org/TR/cors/">CORS W3C recommendation</a>
104         */
105        public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
106        /**
107         * The CORS {@code Access-Control-Allow-Methods} response header field name.
108         * @see <a href="https://www.w3.org/TR/cors/">CORS W3C recommendation</a>
109         */
110        public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
111        /**
112         * The CORS {@code Access-Control-Allow-Origin} response header field name.
113         * @see <a href="https://www.w3.org/TR/cors/">CORS W3C recommendation</a>
114         */
115        public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
116        /**
117         * The CORS {@code Access-Control-Expose-Headers} response header field name.
118         * @see <a href="https://www.w3.org/TR/cors/">CORS W3C recommendation</a>
119         */
120        public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers";
121        /**
122         * The CORS {@code Access-Control-Max-Age} response header field name.
123         * @see <a href="https://www.w3.org/TR/cors/">CORS W3C recommendation</a>
124         */
125        public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
126        /**
127         * The CORS {@code Access-Control-Request-Headers} request header field name.
128         * @see <a href="https://www.w3.org/TR/cors/">CORS W3C recommendation</a>
129         */
130        public static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
131        /**
132         * The CORS {@code Access-Control-Request-Method} request header field name.
133         * @see <a href="https://www.w3.org/TR/cors/">CORS W3C recommendation</a>
134         */
135        public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";
136        /**
137         * The HTTP {@code Age} header field name.
138         * @see <a href="https://tools.ietf.org/html/rfc7234#section-5.1">Section 5.1 of RFC 7234</a>
139         */
140        public static final String AGE = "Age";
141        /**
142         * The HTTP {@code Allow} header field name.
143         * @see <a href="https://tools.ietf.org/html/rfc7231#section-7.4.1">Section 7.4.1 of RFC 7231</a>
144         */
145        public static final String ALLOW = "Allow";
146        /**
147         * The HTTP {@code Authorization} header field name.
148         * @see <a href="https://tools.ietf.org/html/rfc7235#section-4.2">Section 4.2 of RFC 7235</a>
149         */
150        public static final String AUTHORIZATION = "Authorization";
151        /**
152         * The HTTP {@code Cache-Control} header field name.
153         * @see <a href="https://tools.ietf.org/html/rfc7234#section-5.2">Section 5.2 of RFC 7234</a>
154         */
155        public static final String CACHE_CONTROL = "Cache-Control";
156        /**
157         * The HTTP {@code Connection} header field name.
158         * @see <a href="https://tools.ietf.org/html/rfc7230#section-6.1">Section 6.1 of RFC 7230</a>
159         */
160        public static final String CONNECTION = "Connection";
161        /**
162         * The HTTP {@code Content-Encoding} header field name.
163         * @see <a href="https://tools.ietf.org/html/rfc7231#section-3.1.2.2">Section 3.1.2.2 of RFC 7231</a>
164         */
165        public static final String CONTENT_ENCODING = "Content-Encoding";
166        /**
167         * The HTTP {@code Content-Disposition} header field name.
168         * @see <a href="https://tools.ietf.org/html/rfc6266">RFC 6266</a>
169         */
170        public static final String CONTENT_DISPOSITION = "Content-Disposition";
171        /**
172         * The HTTP {@code Content-Language} header field name.
173         * @see <a href="https://tools.ietf.org/html/rfc7231#section-3.1.3.2">Section 3.1.3.2 of RFC 7231</a>
174         */
175        public static final String CONTENT_LANGUAGE = "Content-Language";
176        /**
177         * The HTTP {@code Content-Length} header field name.
178         * @see <a href="https://tools.ietf.org/html/rfc7230#section-3.3.2">Section 3.3.2 of RFC 7230</a>
179         */
180        public static final String CONTENT_LENGTH = "Content-Length";
181        /**
182         * The HTTP {@code Content-Location} header field name.
183         * @see <a href="https://tools.ietf.org/html/rfc7231#section-3.1.4.2">Section 3.1.4.2 of RFC 7231</a>
184         */
185        public static final String CONTENT_LOCATION = "Content-Location";
186        /**
187         * The HTTP {@code Content-Range} header field name.
188         * @see <a href="https://tools.ietf.org/html/rfc7233#section-4.2">Section 4.2 of RFC 7233</a>
189         */
190        public static final String CONTENT_RANGE = "Content-Range";
191        /**
192         * The HTTP {@code Content-Type} header field name.
193         * @see <a href="https://tools.ietf.org/html/rfc7231#section-3.1.1.5">Section 3.1.1.5 of RFC 7231</a>
194         */
195        public static final String CONTENT_TYPE = "Content-Type";
196        /**
197         * The HTTP {@code Cookie} header field name.
198         * @see <a href="https://tools.ietf.org/html/rfc2109#section-4.3.4">Section 4.3.4 of RFC 2109</a>
199         */
200        public static final String COOKIE = "Cookie";
201        /**
202         * The HTTP {@code Date} header field name.
203         * @see <a href="https://tools.ietf.org/html/rfc7231#section-7.1.1.2">Section 7.1.1.2 of RFC 7231</a>
204         */
205        public static final String DATE = "Date";
206        /**
207         * The HTTP {@code ETag} header field name.
208         * @see <a href="https://tools.ietf.org/html/rfc7232#section-2.3">Section 2.3 of RFC 7232</a>
209         */
210        public static final String ETAG = "ETag";
211        /**
212         * The HTTP {@code Expect} header field name.
213         * @see <a href="https://tools.ietf.org/html/rfc7231#section-5.1.1">Section 5.1.1 of RFC 7231</a>
214         */
215        public static final String EXPECT = "Expect";
216        /**
217         * The HTTP {@code Expires} header field name.
218         * @see <a href="https://tools.ietf.org/html/rfc7234#section-5.3">Section 5.3 of RFC 7234</a>
219         */
220        public static final String EXPIRES = "Expires";
221        /**
222         * The HTTP {@code From} header field name.
223         * @see <a href="https://tools.ietf.org/html/rfc7231#section-5.5.1">Section 5.5.1 of RFC 7231</a>
224         */
225        public static final String FROM = "From";
226        /**
227         * The HTTP {@code Host} header field name.
228         * @see <a href="https://tools.ietf.org/html/rfc7230#section-5.4">Section 5.4 of RFC 7230</a>
229         */
230        public static final String HOST = "Host";
231        /**
232         * The HTTP {@code If-Match} header field name.
233         * @see <a href="https://tools.ietf.org/html/rfc7232#section-3.1">Section 3.1 of RFC 7232</a>
234         */
235        public static final String IF_MATCH = "If-Match";
236        /**
237         * The HTTP {@code If-Modified-Since} header field name.
238         * @see <a href="https://tools.ietf.org/html/rfc7232#section-3.3">Section 3.3 of RFC 7232</a>
239         */
240        public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
241        /**
242         * The HTTP {@code If-None-Match} header field name.
243         * @see <a href="https://tools.ietf.org/html/rfc7232#section-3.2">Section 3.2 of RFC 7232</a>
244         */
245        public static final String IF_NONE_MATCH = "If-None-Match";
246        /**
247         * The HTTP {@code If-Range} header field name.
248         * @see <a href="https://tools.ietf.org/html/rfc7233#section-3.2">Section 3.2 of RFC 7233</a>
249         */
250        public static final String IF_RANGE = "If-Range";
251        /**
252         * The HTTP {@code If-Unmodified-Since} header field name.
253         * @see <a href="https://tools.ietf.org/html/rfc7232#section-3.4">Section 3.4 of RFC 7232</a>
254         */
255        public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
256        /**
257         * The HTTP {@code Last-Modified} header field name.
258         * @see <a href="https://tools.ietf.org/html/rfc7232#section-2.2">Section 2.2 of RFC 7232</a>
259         */
260        public static final String LAST_MODIFIED = "Last-Modified";
261        /**
262         * The HTTP {@code Link} header field name.
263         * @see <a href="https://tools.ietf.org/html/rfc5988">RFC 5988</a>
264         */
265        public static final String LINK = "Link";
266        /**
267         * The HTTP {@code Location} header field name.
268         * @see <a href="https://tools.ietf.org/html/rfc7231#section-7.1.2">Section 7.1.2 of RFC 7231</a>
269         */
270        public static final String LOCATION = "Location";
271        /**
272         * The HTTP {@code Max-Forwards} header field name.
273         * @see <a href="https://tools.ietf.org/html/rfc7231#section-5.1.2">Section 5.1.2 of RFC 7231</a>
274         */
275        public static final String MAX_FORWARDS = "Max-Forwards";
276        /**
277         * The HTTP {@code Origin} header field name.
278         * @see <a href="https://tools.ietf.org/html/rfc6454">RFC 6454</a>
279         */
280        public static final String ORIGIN = "Origin";
281        /**
282         * The HTTP {@code Pragma} header field name.
283         * @see <a href="https://tools.ietf.org/html/rfc7234#section-5.4">Section 5.4 of RFC 7234</a>
284         */
285        public static final String PRAGMA = "Pragma";
286        /**
287         * The HTTP {@code Proxy-Authenticate} header field name.
288         * @see <a href="https://tools.ietf.org/html/rfc7235#section-4.3">Section 4.3 of RFC 7235</a>
289         */
290        public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
291        /**
292         * The HTTP {@code Proxy-Authorization} header field name.
293         * @see <a href="https://tools.ietf.org/html/rfc7235#section-4.4">Section 4.4 of RFC 7235</a>
294         */
295        public static final String PROXY_AUTHORIZATION = "Proxy-Authorization";
296        /**
297         * The HTTP {@code Range} header field name.
298         * @see <a href="https://tools.ietf.org/html/rfc7233#section-3.1">Section 3.1 of RFC 7233</a>
299         */
300        public static final String RANGE = "Range";
301        /**
302         * The HTTP {@code Referer} header field name.
303         * @see <a href="https://tools.ietf.org/html/rfc7231#section-5.5.2">Section 5.5.2 of RFC 7231</a>
304         */
305        public static final String REFERER = "Referer";
306        /**
307         * The HTTP {@code Retry-After} header field name.
308         * @see <a href="https://tools.ietf.org/html/rfc7231#section-7.1.3">Section 7.1.3 of RFC 7231</a>
309         */
310        public static final String RETRY_AFTER = "Retry-After";
311        /**
312         * The HTTP {@code Server} header field name.
313         * @see <a href="https://tools.ietf.org/html/rfc7231#section-7.4.2">Section 7.4.2 of RFC 7231</a>
314         */
315        public static final String SERVER = "Server";
316        /**
317         * The HTTP {@code Set-Cookie} header field name.
318         * @see <a href="https://tools.ietf.org/html/rfc2109#section-4.2.2">Section 4.2.2 of RFC 2109</a>
319         */
320        public static final String SET_COOKIE = "Set-Cookie";
321        /**
322         * The HTTP {@code Set-Cookie2} header field name.
323         * @see <a href="https://tools.ietf.org/html/rfc2965">RFC 2965</a>
324         */
325        public static final String SET_COOKIE2 = "Set-Cookie2";
326        /**
327         * The HTTP {@code TE} header field name.
328         * @see <a href="https://tools.ietf.org/html/rfc7230#section-4.3">Section 4.3 of RFC 7230</a>
329         */
330        public static final String TE = "TE";
331        /**
332         * The HTTP {@code Trailer} header field name.
333         * @see <a href="https://tools.ietf.org/html/rfc7230#section-4.4">Section 4.4 of RFC 7230</a>
334         */
335        public static final String TRAILER = "Trailer";
336        /**
337         * The HTTP {@code Transfer-Encoding} header field name.
338         * @see <a href="https://tools.ietf.org/html/rfc7230#section-3.3.1">Section 3.3.1 of RFC 7230</a>
339         */
340        public static final String TRANSFER_ENCODING = "Transfer-Encoding";
341        /**
342         * The HTTP {@code Upgrade} header field name.
343         * @see <a href="https://tools.ietf.org/html/rfc7230#section-6.7">Section 6.7 of RFC 7230</a>
344         */
345        public static final String UPGRADE = "Upgrade";
346        /**
347         * The HTTP {@code User-Agent} header field name.
348         * @see <a href="https://tools.ietf.org/html/rfc7231#section-5.5.3">Section 5.5.3 of RFC 7231</a>
349         */
350        public static final String USER_AGENT = "User-Agent";
351        /**
352         * The HTTP {@code Vary} header field name.
353         * @see <a href="https://tools.ietf.org/html/rfc7231#section-7.1.4">Section 7.1.4 of RFC 7231</a>
354         */
355        public static final String VARY = "Vary";
356        /**
357         * The HTTP {@code Via} header field name.
358         * @see <a href="https://tools.ietf.org/html/rfc7230#section-5.7.1">Section 5.7.1 of RFC 7230</a>
359         */
360        public static final String VIA = "Via";
361        /**
362         * The HTTP {@code Warning} header field name.
363         * @see <a href="https://tools.ietf.org/html/rfc7234#section-5.5">Section 5.5 of RFC 7234</a>
364         */
365        public static final String WARNING = "Warning";
366        /**
367         * The HTTP {@code WWW-Authenticate} header field name.
368         * @see <a href="https://tools.ietf.org/html/rfc7235#section-4.1">Section 4.1 of RFC 7235</a>
369         */
370        public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
371
372        /**
373         * Pattern matching ETag multiple field values in headers such as "If-Match", "If-None-Match".
374         * @see <a href="https://tools.ietf.org/html/rfc7232#section-2.3">Section 2.3 of RFC 7232</a>
375         */
376        private static final Pattern ETAG_HEADER_VALUE_PATTERN = Pattern.compile("\\*|\\s*((W\\/)?(\"[^\"]*\"))\\s*,?");
377
378        private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
379
380        /**
381         * Date formats as specified in the HTTP RFC.
382         * @see <a href="https://tools.ietf.org/html/rfc7231#section-7.1.1.1">Section 7.1.1.1 of RFC 7231</a>
383         */
384        private static final String[] DATE_FORMATS = new String[] {
385                        "EEE, dd MMM yyyy HH:mm:ss zzz",
386                        "EEE, dd-MMM-yy HH:mm:ss zzz",
387                        "EEE MMM dd HH:mm:ss yyyy"
388        };
389
390
391        private final Map<String, List<String>> headers;
392
393
394        /**
395         * Construct a new, empty instance of the {@code HttpHeaders} object.
396         */
397        public HttpHeaders() {
398                this(new LinkedCaseInsensitiveMap<List<String>>(8, Locale.ENGLISH), false);
399        }
400
401        /**
402         * Private constructor that can create read-only {@code HttpHeader} instances.
403         */
404        private HttpHeaders(Map<String, List<String>> headers, boolean readOnly) {
405                if (readOnly) {
406                        Map<String, List<String>> map =
407                                        new LinkedCaseInsensitiveMap<List<String>>(headers.size(), Locale.ENGLISH);
408                        for (Entry<String, List<String>> entry : headers.entrySet()) {
409                                List<String> values = Collections.unmodifiableList(entry.getValue());
410                                map.put(entry.getKey(), values);
411                        }
412                        this.headers = Collections.unmodifiableMap(map);
413                }
414                else {
415                        this.headers = headers;
416                }
417        }
418
419
420        /**
421         * Set the list of acceptable {@linkplain MediaType media types},
422         * as specified by the {@code Accept} header.
423         */
424        public void setAccept(List<MediaType> acceptableMediaTypes) {
425                set(ACCEPT, MediaType.toString(acceptableMediaTypes));
426        }
427
428        /**
429         * Return the list of acceptable {@linkplain MediaType media types},
430         * as specified by the {@code Accept} header.
431         * <p>Returns an empty list when the acceptable media types are unspecified.
432         */
433        public List<MediaType> getAccept() {
434                return MediaType.parseMediaTypes(get(ACCEPT));
435        }
436
437        /**
438         * Set the (new) value of the {@code Access-Control-Allow-Credentials} response header.
439         */
440        public void setAccessControlAllowCredentials(boolean allowCredentials) {
441                set(ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.toString(allowCredentials));
442        }
443
444        /**
445         * Return the value of the {@code Access-Control-Allow-Credentials} response header.
446         */
447        public boolean getAccessControlAllowCredentials() {
448                return Boolean.parseBoolean(getFirst(ACCESS_CONTROL_ALLOW_CREDENTIALS));
449        }
450
451        /**
452         * Set the (new) value of the {@code Access-Control-Allow-Headers} response header.
453         */
454        public void setAccessControlAllowHeaders(List<String> allowedHeaders) {
455                set(ACCESS_CONTROL_ALLOW_HEADERS, toCommaDelimitedString(allowedHeaders));
456        }
457
458        /**
459         * Return the value of the {@code Access-Control-Allow-Headers} response header.
460         */
461        public List<String> getAccessControlAllowHeaders() {
462                return getValuesAsList(ACCESS_CONTROL_ALLOW_HEADERS);
463        }
464
465        /**
466         * Set the (new) value of the {@code Access-Control-Allow-Methods} response header.
467         */
468        public void setAccessControlAllowMethods(List<HttpMethod> allowedMethods) {
469                set(ACCESS_CONTROL_ALLOW_METHODS, StringUtils.collectionToCommaDelimitedString(allowedMethods));
470        }
471
472        /**
473         * Return the value of the {@code Access-Control-Allow-Methods} response header.
474         */
475        public List<HttpMethod> getAccessControlAllowMethods() {
476                List<HttpMethod> result = new ArrayList<HttpMethod>();
477                String value = getFirst(ACCESS_CONTROL_ALLOW_METHODS);
478                if (value != null) {
479                        String[] tokens = StringUtils.tokenizeToStringArray(value, ",");
480                        for (String token : tokens) {
481                                HttpMethod resolved = HttpMethod.resolve(token);
482                                if (resolved != null) {
483                                        result.add(resolved);
484                                }
485                        }
486                }
487                return result;
488        }
489
490        /**
491         * Set the (new) value of the {@code Access-Control-Allow-Origin} response header.
492         */
493        public void setAccessControlAllowOrigin(String allowedOrigin) {
494                set(ACCESS_CONTROL_ALLOW_ORIGIN, allowedOrigin);
495        }
496
497        /**
498         * Return the value of the {@code Access-Control-Allow-Origin} response header.
499         */
500        public String getAccessControlAllowOrigin() {
501                return getFieldValues(ACCESS_CONTROL_ALLOW_ORIGIN);
502        }
503
504        /**
505         * Set the (new) value of the {@code Access-Control-Expose-Headers} response header.
506         */
507        public void setAccessControlExposeHeaders(List<String> exposedHeaders) {
508                set(ACCESS_CONTROL_EXPOSE_HEADERS, toCommaDelimitedString(exposedHeaders));
509        }
510
511        /**
512         * Return the value of the {@code Access-Control-Expose-Headers} response header.
513         */
514        public List<String> getAccessControlExposeHeaders() {
515                return getValuesAsList(ACCESS_CONTROL_EXPOSE_HEADERS);
516        }
517
518        /**
519         * Set the (new) value of the {@code Access-Control-Max-Age} response header.
520         */
521        public void setAccessControlMaxAge(long maxAge) {
522                set(ACCESS_CONTROL_MAX_AGE, Long.toString(maxAge));
523        }
524
525        /**
526         * Return the value of the {@code Access-Control-Max-Age} response header.
527         * <p>Returns -1 when the max age is unknown.
528         */
529        public long getAccessControlMaxAge() {
530                String value = getFirst(ACCESS_CONTROL_MAX_AGE);
531                return (value != null ? Long.parseLong(value) : -1);
532        }
533
534        /**
535         * Set the (new) value of the {@code Access-Control-Request-Headers} request header.
536         */
537        public void setAccessControlRequestHeaders(List<String> requestHeaders) {
538                set(ACCESS_CONTROL_REQUEST_HEADERS, toCommaDelimitedString(requestHeaders));
539        }
540
541        /**
542         * Return the value of the {@code Access-Control-Request-Headers} request header.
543         */
544        public List<String> getAccessControlRequestHeaders() {
545                return getValuesAsList(ACCESS_CONTROL_REQUEST_HEADERS);
546        }
547
548        /**
549         * Set the (new) value of the {@code Access-Control-Request-Method} request header.
550         */
551        public void setAccessControlRequestMethod(HttpMethod requestMethod) {
552                set(ACCESS_CONTROL_REQUEST_METHOD, requestMethod.name());
553        }
554
555        /**
556         * Return the value of the {@code Access-Control-Request-Method} request header.
557         */
558        public HttpMethod getAccessControlRequestMethod() {
559                return HttpMethod.resolve(getFirst(ACCESS_CONTROL_REQUEST_METHOD));
560        }
561
562        /**
563         * Set the list of acceptable {@linkplain Charset charsets},
564         * as specified by the {@code Accept-Charset} header.
565         */
566        public void setAcceptCharset(List<Charset> acceptableCharsets) {
567                StringBuilder builder = new StringBuilder();
568                for (Iterator<Charset> iterator = acceptableCharsets.iterator(); iterator.hasNext();) {
569                        Charset charset = iterator.next();
570                        builder.append(charset.name().toLowerCase(Locale.ENGLISH));
571                        if (iterator.hasNext()) {
572                                builder.append(", ");
573                        }
574                }
575                set(ACCEPT_CHARSET, builder.toString());
576        }
577
578        /**
579         * Return the list of acceptable {@linkplain Charset charsets},
580         * as specified by the {@code Accept-Charset} header.
581         */
582        public List<Charset> getAcceptCharset() {
583                String value = getFirst(ACCEPT_CHARSET);
584                if (value != null) {
585                        String[] tokens = StringUtils.tokenizeToStringArray(value, ",");
586                        List<Charset> result = new ArrayList<Charset>(tokens.length);
587                        for (String token : tokens) {
588                                int paramIdx = token.indexOf(';');
589                                String charsetName;
590                                if (paramIdx == -1) {
591                                        charsetName = token;
592                                }
593                                else {
594                                        charsetName = token.substring(0, paramIdx);
595                                }
596                                if (!charsetName.equals("*")) {
597                                        result.add(Charset.forName(charsetName));
598                                }
599                        }
600                        return result;
601                }
602                else {
603                        return Collections.emptyList();
604                }
605        }
606
607        /**
608         * Set the set of allowed {@link HttpMethod HTTP methods},
609         * as specified by the {@code Allow} header.
610         */
611        public void setAllow(Set<HttpMethod> allowedMethods) {
612                set(ALLOW, StringUtils.collectionToCommaDelimitedString(allowedMethods));
613        }
614
615        /**
616         * Return the set of allowed {@link HttpMethod HTTP methods},
617         * as specified by the {@code Allow} header.
618         * <p>Returns an empty set when the allowed methods are unspecified.
619         */
620        public Set<HttpMethod> getAllow() {
621                String value = getFirst(ALLOW);
622                if (!StringUtils.isEmpty(value)) {
623                        String[] tokens = StringUtils.tokenizeToStringArray(value, ",");
624                        List<HttpMethod> result = new ArrayList<HttpMethod>(tokens.length);
625                        for (String token : tokens) {
626                                HttpMethod resolved = HttpMethod.resolve(token);
627                                if (resolved != null) {
628                                        result.add(resolved);
629                                }
630                        }
631                        return EnumSet.copyOf(result);
632                }
633                else {
634                        return EnumSet.noneOf(HttpMethod.class);
635                }
636        }
637
638        /**
639         * Set the (new) value of the {@code Cache-Control} header.
640         */
641        public void setCacheControl(String cacheControl) {
642                set(CACHE_CONTROL, cacheControl);
643        }
644
645        /**
646         * Return the value of the {@code Cache-Control} header.
647         */
648        public String getCacheControl() {
649                return getFieldValues(CACHE_CONTROL);
650        }
651
652        /**
653         * Set the (new) value of the {@code Connection} header.
654         */
655        public void setConnection(String connection) {
656                set(CONNECTION, connection);
657        }
658
659        /**
660         * Set the (new) value of the {@code Connection} header.
661         */
662        public void setConnection(List<String> connection) {
663                set(CONNECTION, toCommaDelimitedString(connection));
664        }
665
666        /**
667         * Return the value of the {@code Connection} header.
668         */
669        public List<String> getConnection() {
670                return getValuesAsList(CONNECTION);
671        }
672
673        /**
674         * Set the {@code Content-Disposition} header when creating a
675         * {@code "multipart/form-data"} request. The given filename is formatted
676         * as a quoted-string, as defined in RFC 2616, section 2.2, and any quote
677         * characters within the filename value will be escaped with a backslash,
678         * e.g. {@code "foo\"bar.txt"} becomes {@code "foo\\\"bar.txt"}.
679         * <p>Applications typically would not set this header directly but
680         * rather prepare a {@code MultiValueMap<String, Object>}, containing an
681         * Object or a {@link org.springframework.core.io.Resource} for each part,
682         * and then pass that to the {@code RestTemplate} or {@code WebClient}.
683         * @param name the control name
684         * @param filename the filename (may be {@code null})
685         */
686        public void setContentDispositionFormData(String name, String filename) {
687                Assert.notNull(name, "'name' must not be null");
688                StringBuilder builder = new StringBuilder("form-data; name=\"");
689                builder.append(name).append('\"');
690                if (filename != null) {
691                        builder.append("; filename=\"");
692                        builder.append(escapeQuotationsInFilename(filename)).append('\"');
693                }
694                set(CONTENT_DISPOSITION, builder.toString());
695        }
696
697        /**
698         * Set the (new) value of the {@code Content-Disposition} header
699         * for {@code form-data}, optionally encoding the filename using the RFC 5987.
700         * <p>Only the US-ASCII, UTF-8 and ISO-8859-1 charsets are supported.
701         * @param name the control name
702         * @param filename the filename (may be {@code null})
703         * @param charset the charset used for the filename (may be {@code null})
704         * @deprecated deprecated in 4.3.11 and removed from 5.0; as per
705         * <a link="https://tools.ietf.org/html/rfc7578#section-4.2">RFC 7578, Section 4.2</a>,
706         * an RFC 5987 style encoding should not be used for multipart/form-data requests.
707         * Furthermore there should be no reason for applications to set this header
708         * explicitly; for more details also read
709         * {@link #setContentDispositionFormData(String, String)}
710         */
711        @Deprecated
712        public void setContentDispositionFormData(String name, String filename, Charset charset) {
713                if (filename == null || charset == null || charset.name().equals("US-ASCII")) {
714                        setContentDispositionFormData(name, filename);
715                        return;
716                }
717                Assert.notNull(name, "'name' must not be null");
718                String encodedFilename = encodeHeaderFieldParam(filename, charset);
719                set(CONTENT_DISPOSITION, "form-data; name=\"" + name + '\"' + "; filename*=" + encodedFilename);
720        }
721
722        /**
723         * Set the length of the body in bytes, as specified by the
724         * {@code Content-Length} header.
725         */
726        public void setContentLength(long contentLength) {
727                set(CONTENT_LENGTH, Long.toString(contentLength));
728        }
729
730        /**
731         * Return the length of the body in bytes, as specified by the
732         * {@code Content-Length} header.
733         * <p>Returns -1 when the content-length is unknown.
734         */
735        public long getContentLength() {
736                String value = getFirst(CONTENT_LENGTH);
737                return (value != null ? Long.parseLong(value) : -1);
738        }
739
740        /**
741         * Set the {@linkplain MediaType media type} of the body,
742         * as specified by the {@code Content-Type} header.
743         */
744        public void setContentType(MediaType mediaType) {
745                Assert.isTrue(!mediaType.isWildcardType(), "Content-Type cannot contain wildcard type '*'");
746                Assert.isTrue(!mediaType.isWildcardSubtype(), "Content-Type cannot contain wildcard subtype '*'");
747                set(CONTENT_TYPE, mediaType.toString());
748        }
749
750        /**
751         * Return the {@linkplain MediaType media type} of the body, as specified
752         * by the {@code Content-Type} header.
753         * <p>Returns {@code null} when the content-type is unknown.
754         */
755        public MediaType getContentType() {
756                String value = getFirst(CONTENT_TYPE);
757                return (StringUtils.hasLength(value) ? MediaType.parseMediaType(value) : null);
758        }
759
760        /**
761         * Set the date and time at which the message was created, as specified
762         * by the {@code Date} header.
763         * <p>The date should be specified as the number of milliseconds since
764         * January 1, 1970 GMT.
765         */
766        public void setDate(long date) {
767                setDate(DATE, date);
768        }
769
770        /**
771         * Return the date and time at which the message was created, as specified
772         * by the {@code Date} header.
773         * <p>The date is returned as the number of milliseconds since
774         * January 1, 1970 GMT. Returns -1 when the date is unknown.
775         * @throws IllegalArgumentException if the value cannot be converted to a date
776         */
777        public long getDate() {
778                return getFirstDate(DATE);
779        }
780
781        /**
782         * Set the (new) entity tag of the body, as specified by the {@code ETag} header.
783         */
784        public void setETag(String etag) {
785                if (etag != null) {
786                        Assert.isTrue(etag.startsWith("\"") || etag.startsWith("W/"),
787                                        "Invalid ETag: does not start with W/ or \"");
788                        Assert.isTrue(etag.endsWith("\""), "Invalid ETag: does not end with \"");
789                }
790                set(ETAG, etag);
791        }
792
793        /**
794         * Return the entity tag of the body, as specified by the {@code ETag} header.
795         */
796        public String getETag() {
797                return getFirst(ETAG);
798        }
799
800        /**
801         * Set the date and time at which the message is no longer valid,
802         * as specified by the {@code Expires} header.
803         * <p>The date should be specified as the number of milliseconds since
804         * January 1, 1970 GMT.
805         */
806        public void setExpires(long expires) {
807                setDate(EXPIRES, expires);
808        }
809
810        /**
811         * Return the date and time at which the message is no longer valid,
812         * as specified by the {@code Expires} header.
813         * <p>The date is returned as the number of milliseconds since
814         * January 1, 1970 GMT. Returns -1 when the date is unknown.
815         */
816        public long getExpires() {
817                return getFirstDate(EXPIRES, false);
818        }
819
820        /**
821         * Set the (new) value of the {@code If-Match} header.
822         * @since 4.3
823         */
824        public void setIfMatch(String ifMatch) {
825                set(IF_MATCH, ifMatch);
826        }
827
828        /**
829         * Set the (new) value of the {@code If-Match} header.
830         * @since 4.3
831         */
832        public void setIfMatch(List<String> ifMatchList) {
833                set(IF_MATCH, toCommaDelimitedString(ifMatchList));
834        }
835
836        /**
837         * Return the value of the {@code If-Match} header.
838         * @since 4.3
839         */
840        public List<String> getIfMatch() {
841                return getETagValuesAsList(IF_MATCH);
842        }
843
844        /**
845         * Set the (new) value of the {@code If-Modified-Since} header.
846         * <p>The date should be specified as the number of milliseconds since
847         * January 1, 1970 GMT.
848         */
849        public void setIfModifiedSince(long ifModifiedSince) {
850                setDate(IF_MODIFIED_SINCE, ifModifiedSince);
851        }
852
853        /**
854         * Return the value of the {@code If-Modified-Since} header.
855         * <p>The date is returned as the number of milliseconds since
856         * January 1, 1970 GMT. Returns -1 when the date is unknown.
857         */
858        public long getIfModifiedSince() {
859                return getFirstDate(IF_MODIFIED_SINCE, false);
860        }
861
862        /**
863         * Set the (new) value of the {@code If-None-Match} header.
864         */
865        public void setIfNoneMatch(String ifNoneMatch) {
866                set(IF_NONE_MATCH, ifNoneMatch);
867        }
868
869        /**
870         * Set the (new) values of the {@code If-None-Match} header.
871         */
872        public void setIfNoneMatch(List<String> ifNoneMatchList) {
873                set(IF_NONE_MATCH, toCommaDelimitedString(ifNoneMatchList));
874        }
875
876        /**
877         * Return the value of the {@code If-None-Match} header.
878         */
879        public List<String> getIfNoneMatch() {
880                return getETagValuesAsList(IF_NONE_MATCH);
881        }
882
883        /**
884         * Set the (new) value of the {@code If-Unmodified-Since} header.
885         * <p>The date should be specified as the number of milliseconds since
886         * January 1, 1970 GMT.
887         * @since 4.3
888         */
889        public void setIfUnmodifiedSince(long ifUnmodifiedSince) {
890                setDate(IF_UNMODIFIED_SINCE, ifUnmodifiedSince);
891        }
892
893        /**
894         * Return the value of the {@code If-Unmodified-Since} header.
895         * <p>The date is returned as the number of milliseconds since
896         * January 1, 1970 GMT. Returns -1 when the date is unknown.
897         * @since 4.3
898         */
899        public long getIfUnmodifiedSince() {
900                return getFirstDate(IF_UNMODIFIED_SINCE, false);
901        }
902
903        /**
904         * Set the time the resource was last changed, as specified by the
905         * {@code Last-Modified} header.
906         * <p>The date should be specified as the number of milliseconds since
907         * January 1, 1970 GMT.
908         */
909        public void setLastModified(long lastModified) {
910                setDate(LAST_MODIFIED, lastModified);
911        }
912
913        /**
914         * Return the time the resource was last changed, as specified by the
915         * {@code Last-Modified} header.
916         * <p>The date is returned as the number of milliseconds since
917         * January 1, 1970 GMT. Returns -1 when the date is unknown.
918         */
919        public long getLastModified() {
920                return getFirstDate(LAST_MODIFIED, false);
921        }
922
923        /**
924         * Set the (new) location of a resource,
925         * as specified by the {@code Location} header.
926         */
927        public void setLocation(URI location) {
928                set(LOCATION, location.toASCIIString());
929        }
930
931        /**
932         * Return the (new) location of a resource
933         * as specified by the {@code Location} header.
934         * <p>Returns {@code null} when the location is unknown.
935         */
936        public URI getLocation() {
937                String value = getFirst(LOCATION);
938                return (value != null ? URI.create(value) : null);
939        }
940
941        /**
942         * Set the (new) value of the {@code Origin} header.
943         */
944        public void setOrigin(String origin) {
945                set(ORIGIN, origin);
946        }
947
948        /**
949         * Return the value of the {@code Origin} header.
950         */
951        public String getOrigin() {
952                return getFirst(ORIGIN);
953        }
954
955        /**
956         * Set the (new) value of the {@code Pragma} header.
957         */
958        public void setPragma(String pragma) {
959                set(PRAGMA, pragma);
960        }
961
962        /**
963         * Return the value of the {@code Pragma} header.
964         */
965        public String getPragma() {
966                return getFirst(PRAGMA);
967        }
968
969        /**
970         * Sets the (new) value of the {@code Range} header.
971         */
972        public void setRange(List<HttpRange> ranges) {
973                String value = HttpRange.toString(ranges);
974                set(RANGE, value);
975        }
976
977        /**
978         * Return the value of the {@code Range} header.
979         * <p>Returns an empty list when the range is unknown.
980         */
981        public List<HttpRange> getRange() {
982                String value = getFirst(RANGE);
983                return HttpRange.parseRanges(value);
984        }
985
986        /**
987         * Set the (new) value of the {@code Upgrade} header.
988         */
989        public void setUpgrade(String upgrade) {
990                set(UPGRADE, upgrade);
991        }
992
993        /**
994         * Return the value of the {@code Upgrade} header.
995         */
996        public String getUpgrade() {
997                return getFirst(UPGRADE);
998        }
999
1000        /**
1001         * Set the request header names (e.g. "Accept-Language") for which the
1002         * response is subject to content negotiation and variances based on the
1003         * value of those request headers.
1004         * @param requestHeaders the request header names
1005         * @since 4.3
1006         */
1007        public void setVary(List<String> requestHeaders) {
1008                set(VARY, toCommaDelimitedString(requestHeaders));
1009        }
1010
1011        /**
1012         * Return the request header names subject to content negotiation.
1013         * @since 4.3
1014         */
1015        public List<String> getVary() {
1016                return getValuesAsList(VARY);
1017        }
1018
1019        /**
1020         * Set the given date under the given header name after formatting it as a string
1021         * using the pattern {@code "EEE, dd MMM yyyy HH:mm:ss zzz"}. The equivalent of
1022         * {@link #set(String, String)} but for date headers.
1023         * @since 3.2.4
1024         */
1025        public void setDate(String headerName, long date) {
1026                SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMATS[0], Locale.US);
1027                dateFormat.setTimeZone(GMT);
1028                set(headerName, dateFormat.format(new Date(date)));
1029        }
1030
1031        /**
1032         * Parse the first header value for the given header name as a date,
1033         * return -1 if there is no value, or raise {@link IllegalArgumentException}
1034         * if the value cannot be parsed as a date.
1035         * @param headerName the header name
1036         * @return the parsed date header, or -1 if none
1037         * @since 3.2.4
1038         */
1039        public long getFirstDate(String headerName) {
1040                return getFirstDate(headerName, true);
1041        }
1042
1043        /**
1044         * Parse the first header value for the given header name as a date,
1045         * return -1 if there is no value or also in case of an invalid value
1046         * (if {@code rejectInvalid=false}), or raise {@link IllegalArgumentException}
1047         * if the value cannot be parsed as a date.
1048         * @param headerName the header name
1049         * @param rejectInvalid whether to reject invalid values with an
1050         * {@link IllegalArgumentException} ({@code true}) or rather return -1
1051         * in that case ({@code false})
1052         * @return the parsed date header, or -1 if none (or invalid)
1053         */
1054        private long getFirstDate(String headerName, boolean rejectInvalid) {
1055                String headerValue = getFirst(headerName);
1056                if (headerValue == null) {
1057                        // No header value sent at all
1058                        return -1;
1059                }
1060                if (headerValue.length() >= 3) {
1061                        // Short "0" or "-1" like values are never valid HTTP date headers...
1062                        // Let's only bother with SimpleDateFormat parsing for long enough values.
1063                        for (String dateFormat : DATE_FORMATS) {
1064                                SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US);
1065                                simpleDateFormat.setTimeZone(GMT);
1066                                try {
1067                                        return simpleDateFormat.parse(headerValue).getTime();
1068                                }
1069                                catch (ParseException ex) {
1070                                        // ignore
1071                                }
1072                        }
1073                }
1074                if (rejectInvalid) {
1075                        throw new IllegalArgumentException("Cannot parse date value \"" + headerValue +
1076                                        "\" for \"" + headerName + "\" header");
1077                }
1078                return -1;
1079        }
1080
1081        /**
1082         * Return all values of a given header name,
1083         * even if this header is set multiple times.
1084         * @param headerName the header name
1085         * @return all associated values
1086         * @since 4.3
1087         */
1088        public List<String> getValuesAsList(String headerName) {
1089                List<String> values = get(headerName);
1090                if (values != null) {
1091                        List<String> result = new ArrayList<String>();
1092                        for (String value : values) {
1093                                if (value != null) {
1094                                        String[] tokens = StringUtils.tokenizeToStringArray(value, ",");
1095                                        for (String token : tokens) {
1096                                                result.add(token);
1097                                        }
1098                                }
1099                        }
1100                        return result;
1101                }
1102                return Collections.emptyList();
1103        }
1104
1105        /**
1106         * Retrieve a combined result from the field values of the ETag header.
1107         * @param headerName the header name
1108         * @return the combined result
1109         * @since 4.3
1110         */
1111        protected List<String> getETagValuesAsList(String headerName) {
1112                List<String> values = get(headerName);
1113                if (values != null) {
1114                        List<String> result = new ArrayList<String>();
1115                        for (String value : values) {
1116                                if (value != null) {
1117                                        Matcher matcher = ETAG_HEADER_VALUE_PATTERN.matcher(value);
1118                                        while (matcher.find()) {
1119                                                if ("*".equals(matcher.group())) {
1120                                                        result.add(matcher.group());
1121                                                }
1122                                                else {
1123                                                        result.add(matcher.group(1));
1124                                                }
1125                                        }
1126                                        if (result.isEmpty()) {
1127                                                throw new IllegalArgumentException(
1128                                                                "Could not parse header '" + headerName + "' with value '" + value + "'");
1129                                        }
1130                                }
1131                        }
1132                        return result;
1133                }
1134                return Collections.emptyList();
1135        }
1136
1137        /**
1138         * Retrieve a combined result from the field values of multi-valued headers.
1139         * @param headerName the header name
1140         * @return the combined result
1141         * @since 4.3
1142         */
1143        protected String getFieldValues(String headerName) {
1144                List<String> headerValues = get(headerName);
1145                return (headerValues != null ? toCommaDelimitedString(headerValues) : null);
1146        }
1147
1148        /**
1149         * Turn the given list of header values into a comma-delimited result.
1150         * @param headerValues the list of header values
1151         * @return a combined result with comma delimitation
1152         */
1153        protected String toCommaDelimitedString(List<String> headerValues) {
1154                StringBuilder builder = new StringBuilder();
1155                for (Iterator<String> it = headerValues.iterator(); it.hasNext(); ) {
1156                        String val = it.next();
1157                        builder.append(val);
1158                        if (it.hasNext()) {
1159                                builder.append(", ");
1160                        }
1161                }
1162                return builder.toString();
1163        }
1164
1165
1166        // MultiValueMap implementation
1167
1168        /**
1169         * Return the first header value for the given header name, if any.
1170         * @param headerName the header name
1171         * @return the first header value, or {@code null} if none
1172         */
1173        @Override
1174        public String getFirst(String headerName) {
1175                List<String> headerValues = this.headers.get(headerName);
1176                return (headerValues != null ? headerValues.get(0) : null);
1177        }
1178
1179        /**
1180         * Add the given, single header value under the given name.
1181         * @param headerName the header name
1182         * @param headerValue the header value
1183         * @throws UnsupportedOperationException if adding headers is not supported
1184         * @see #put(String, List)
1185         * @see #set(String, String)
1186         */
1187        @Override
1188        public void add(String headerName, String headerValue) {
1189                List<String> headerValues = this.headers.get(headerName);
1190                if (headerValues == null) {
1191                        headerValues = new LinkedList<String>();
1192                        this.headers.put(headerName, headerValues);
1193                }
1194                headerValues.add(headerValue);
1195        }
1196
1197        /**
1198         * Set the given, single header value under the given name.
1199         * @param headerName the header name
1200         * @param headerValue the header value
1201         * @throws UnsupportedOperationException if adding headers is not supported
1202         * @see #put(String, List)
1203         * @see #add(String, String)
1204         */
1205        @Override
1206        public void set(String headerName, String headerValue) {
1207                List<String> headerValues = new LinkedList<String>();
1208                headerValues.add(headerValue);
1209                this.headers.put(headerName, headerValues);
1210        }
1211
1212        @Override
1213        public void setAll(Map<String, String> values) {
1214                for (Entry<String, String> entry : values.entrySet()) {
1215                        set(entry.getKey(), entry.getValue());
1216                }
1217        }
1218
1219        @Override
1220        public Map<String, String> toSingleValueMap() {
1221                LinkedHashMap<String, String> singleValueMap = new LinkedHashMap<String,String>(this.headers.size());
1222                for (Entry<String, List<String>> entry : this.headers.entrySet()) {
1223                        singleValueMap.put(entry.getKey(), entry.getValue().get(0));
1224                }
1225                return singleValueMap;
1226        }
1227
1228
1229        // Map implementation
1230
1231        @Override
1232        public int size() {
1233                return this.headers.size();
1234        }
1235
1236        @Override
1237        public boolean isEmpty() {
1238                return this.headers.isEmpty();
1239        }
1240
1241        @Override
1242        public boolean containsKey(Object key) {
1243                return this.headers.containsKey(key);
1244        }
1245
1246        @Override
1247        public boolean containsValue(Object value) {
1248                return this.headers.containsValue(value);
1249        }
1250
1251        @Override
1252        public List<String> get(Object key) {
1253                return this.headers.get(key);
1254        }
1255
1256        @Override
1257        public List<String> put(String key, List<String> value) {
1258                return this.headers.put(key, value);
1259        }
1260
1261        @Override
1262        public List<String> remove(Object key) {
1263                return this.headers.remove(key);
1264        }
1265
1266        @Override
1267        public void putAll(Map<? extends String, ? extends List<String>> map) {
1268                this.headers.putAll(map);
1269        }
1270
1271        @Override
1272        public void clear() {
1273                this.headers.clear();
1274        }
1275
1276        @Override
1277        public Set<String> keySet() {
1278                return this.headers.keySet();
1279        }
1280
1281        @Override
1282        public Collection<List<String>> values() {
1283                return this.headers.values();
1284        }
1285
1286        @Override
1287        public Set<Entry<String, List<String>>> entrySet() {
1288                return this.headers.entrySet();
1289        }
1290
1291
1292        @Override
1293        public boolean equals(Object other) {
1294                if (this == other) {
1295                        return true;
1296                }
1297                if (!(other instanceof HttpHeaders)) {
1298                        return false;
1299                }
1300                HttpHeaders otherHeaders = (HttpHeaders) other;
1301                return this.headers.equals(otherHeaders.headers);
1302        }
1303
1304        @Override
1305        public int hashCode() {
1306                return this.headers.hashCode();
1307        }
1308
1309        @Override
1310        public String toString() {
1311                return this.headers.toString();
1312        }
1313
1314
1315        /**
1316         * Return an {@code HttpHeaders} object that can only be read, not written to.
1317         */
1318        public static HttpHeaders readOnlyHttpHeaders(HttpHeaders headers) {
1319                Assert.notNull(headers, "HttpHeaders must not be null");
1320                return new HttpHeaders(headers, true);
1321        }
1322
1323        private static String escapeQuotationsInFilename(String filename) {
1324                if (filename.indexOf('"') == -1 && filename.indexOf('\\') == -1) {
1325                        return filename;
1326                }
1327                boolean escaped = false;
1328                StringBuilder sb = new StringBuilder();
1329                for (char c : filename.toCharArray()) {
1330                        sb.append((c == '"' && !escaped) ? "\\\"" : c);
1331                        escaped = (!escaped && c == '\\');
1332                }
1333                // Remove backslash at the end..
1334                if (escaped) {
1335                        sb.deleteCharAt(sb.length() - 1);
1336                }
1337                return sb.toString();
1338        }
1339
1340        /**
1341         * Encode the given header field param as describe in RFC 5987.
1342         * @param input the header field param
1343         * @param charset the charset of the header field param string
1344         * @return the encoded header field param
1345         * @see <a href="https://tools.ietf.org/html/rfc5987">RFC 5987</a>
1346         */
1347        private static String encodeHeaderFieldParam(String input, Charset charset) {
1348                Assert.notNull(input, "Input String should not be null");
1349                Assert.notNull(charset, "Charset should not be null");
1350                Assert.isTrue(!charset.name().equals("US-ASCII"), "ASCII does not require encoding");
1351                Assert.isTrue(charset.name().equals("UTF-8") || charset.name().equals("ISO-8859-1"),
1352                                "Charset should be UTF-8 or ISO-8859-1");
1353                byte[] source = input.getBytes(charset);
1354                int len = source.length;
1355                StringBuilder sb = new StringBuilder(len << 1);
1356                sb.append(charset.name());
1357                sb.append("''");
1358                for (byte b : source) {
1359                        if (isRFC5987AttrChar(b)) {
1360                                sb.append((char) b);
1361                        }
1362                        else {
1363                                sb.append('%');
1364                                char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16));
1365                                char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16));
1366                                sb.append(hex1);
1367                                sb.append(hex2);
1368                        }
1369                }
1370                return sb.toString();
1371        }
1372
1373        private static boolean isRFC5987AttrChar(byte c) {
1374                return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
1375                                c == '!' || c == '#' || c == '$' || c == '&' || c == '+' || c == '-' ||
1376                                c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
1377        }
1378
1379}